diff --git a/internal/cbm/resolve/adapter_swift.c b/internal/cbm/resolve/adapter_swift.c index 03dafa370..8fa01cbb5 100644 --- a/internal/cbm/resolve/adapter_swift.c +++ b/internal/cbm/resolve/adapter_swift.c @@ -41,6 +41,7 @@ #include "source_adapter_internal.h" #include #include +#include "stats.h" #include "syntax_index.h" #define SWIFT_TYPE_RECORD "&type" @@ -1221,8 +1222,8 @@ static swift_lookup_t swift_find_member(swift_ctx_t *ctx, const char *type_key, } /* Only extensions here: what the extension spells is declared elsewhere, or outside. */ bool extension_only = true; - for (const cbm_source_decl_t *record = records; record; record = record->next) - extension_only &= (record->flags & CBM_SOURCE_DECL_F_ADAPTED) != 0; + for (const cbm_source_decl_t *record = records; record && extension_only; record = record->next) + extension_only = (record->flags & CBM_SOURCE_DECL_F_ADAPTED) != 0; bool declared = false; if (extension_only && !bases_only) { const char *hash = strchr(type_key, '#'); @@ -1255,8 +1256,12 @@ static swift_lookup_t swift_find_member(swift_ctx_t *ctx, const char *type_key, /* An extension of a type outside the project has no declaration here. */ const char *base_owner = type_key; for (const cbm_source_decl_t *record = records; record; record = record->next) { + cbm_resolve_stats_add(CBM_RESOLVE_STAT_LOOKUP_PROBES, 1); declared |= !(record->flags & CBM_SOURCE_DECL_F_ADAPTED); open |= (record->flags & CBM_SOURCE_DECL_F_OPEN) != 0; + /* Past the chain's bound every further base answers open, and the answer is open now. */ + if (open && chain->overflow) + break; for (const char *const *base = record->bases; base && *base; base++) { const char *parent = swift_lookup_type(ctx, base_owner, *base); if (!parent) { diff --git a/tests/test_complexity.c b/tests/test_complexity.c index d9a86def6..d3dc2bfdc 100644 --- a/tests/test_complexity.c +++ b/tests/test_complexity.c @@ -3263,6 +3263,16 @@ TEST(complexity_source_same_named_definitions_are_near_linear) { PASS(); } +/* `extension A: P` many times over: a name found in none of A's bases asks each of them. */ +CX_LINE3(cx_line_swift_extension_base, + "protocol P%d {}\nextension A: P%d {\n func m_%d() { base() }\n}\n") +TEST(complexity_swift_type_bases_are_near_linear) { + ASSERT_TRUE(cx_source_shape_is_near_linear(CBM_LANG_SWIFT, "bases.swift", + "class A { func base() {} }\n", + cx_line_swift_extension_base, "")); + PASS(); +} + /* A barrel library re-exports the project, and every library that imports it read its whole * export list again. */ static uint64_t cx_dart_barrel_work(int libraries) { @@ -3457,6 +3467,7 @@ SUITE(complexity) { RUN_TEST(complexity_syntax_index_prev_siblings_answer_as_tree_sitter_does); RUN_TEST(complexity_prev_sibling_walk_is_linear); RUN_TEST(complexity_source_same_named_definitions_are_near_linear); + RUN_TEST(complexity_swift_type_bases_are_near_linear); RUN_TEST(complexity_dart_barrel_exports_are_near_linear); RUN_TEST(complexity_go_route_bindings_are_near_linear); RUN_TEST(complexity_flat_preceding_label_is_linear);