Skip to content

Commit c540426

Browse files
committed
fix(typescript): preserve awaited generic call edges
1 parent a43b220 commit c540426

4 files changed

Lines changed: 46 additions & 0 deletions

File tree

‎internal/cbm/extract_calls.c‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,9 +259,18 @@ static char *extract_constructor_callee(CBMArena *a, TSNode node, const char *so
259259
}
260260

261261
// Try common field-based callee resolution (function, name, method fields).
262+
static TSNode unwrap_await_callee(TSNode node) {
263+
if (ts_node_is_null(node) || strcmp(ts_node_type(node), "await_expression") != 0 ||
264+
ts_node_named_child_count(node) == 0) {
265+
return node;
266+
}
267+
return ts_node_named_child(node, 0);
268+
}
269+
262270
static char *extract_callee_from_fields(CBMArena *a, TSNode node, const char *source) {
263271
// Try "function" field
264272
TSNode func_node = ts_node_child_by_field_name(node, TS_FIELD("function"));
273+
func_node = unwrap_await_callee(func_node);
265274
if (!ts_node_is_null(func_node)) {
266275
const char *fk = ts_node_type(func_node);
267276
if (strcmp(fk, "selector_expression") == 0) {
@@ -3435,6 +3444,7 @@ static CBMPrimaryCalleeSelection select_primary_callee(CBMExtractCtx *ctx, TSNod
34353444
}
34363445

34373446
selection.expr = language_specific_callee_expr(ctx->language, node);
3447+
selection.expr = unwrap_await_callee(selection.expr);
34383448
if (is_dynamic_callee_expr(ctx, selection.expr)) {
34393449
selection.expr = (TSNode){0};
34403450
return selection;

‎internal/cbm/lsp/ts_lsp.c‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2800,8 +2800,17 @@ static void resolve_value_references_at(TSLSPContext *ctx, TSNode call_node) {
28002800
}
28012801
}
28022802

2803+
static TSNode unwrap_await_callee(TSNode node) {
2804+
if (ts_node_is_null(node) || strcmp(ts_node_type(node), "await_expression") != 0 ||
2805+
ts_node_named_child_count(node) == 0) {
2806+
return node;
2807+
}
2808+
return ts_node_named_child(node, 0);
2809+
}
2810+
28032811
static void resolve_call_at(TSLSPContext *ctx, TSNode call_node) {
28042812
TSNode fn = ts_node_child_by_field_name(call_node, "function", TS_LSP_FIELD_LEN("function"));
2813+
fn = unwrap_await_callee(fn);
28052814
if (ts_node_is_null(fn))
28062815
return;
28072816

‎tests/test_extraction.c‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4056,6 +4056,18 @@ TEST(extract_ts_url_builder_issue1009) {
40564056
PASS();
40574057
}
40584058

4059+
TEST(extract_ts_await_generic_call_issue2210) {
4060+
CBMFileResult *r = extract("function parseJsonBody<T>() { return {} as T; }\n"
4061+
"async function plain() { return await parseJsonBody(); }\n"
4062+
"async function generic() { return await parseJsonBody<string>(); }\n",
4063+
CBM_LANG_TYPESCRIPT, "t", "await.ts");
4064+
ASSERT_NOT_NULL(r);
4065+
ASSERT_FALSE(r->has_error);
4066+
ASSERT_EQ(count_calls_named(r, "parseJsonBody"), 2);
4067+
cbm_free_result(r);
4068+
PASS();
4069+
}
4070+
40594071
/* Issue #1009 (composed builders): a builder whose template inlines an earlier
40604072
* builder's call plus a query string: `return \`${basePath(id)}?${params}\``.
40614073
* The known-substitution is inlined and the query string is truncated, so the
@@ -6903,6 +6915,7 @@ SUITE(extraction) {
69036915
RUN_TEST(extract_go_binary_concat_url_issue1249);
69046916
RUN_TEST(extract_go_binary_concat_url_no_literal_suffix_issue1249);
69056917
RUN_TEST(extract_ts_url_builder_issue1009);
6918+
RUN_TEST(extract_ts_await_generic_call_issue2210);
69066919
RUN_TEST(extract_ts_url_builder_composed_issue1009);
69076920
RUN_TEST(extract_c_url_builder_gated_issue1009);
69086921
RUN_TEST(extract_ts_url_builder_mixed_returns_issue1009);

‎tests/test_ts_lsp.c‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,18 @@ TEST(tslsp_nocrash_megasource) {
729729

730730
/* ── Category 11: generics (deeper) ─────────────────────────────────────────── */
731731

732+
TEST(tslsp_await_generic_call_issue2210) {
733+
CBMFileResult *r =
734+
extract_ts("function parseJsonBody<T>(): T { return null as T; }\n"
735+
"async function plain() { return await parseJsonBody(); }\n"
736+
"async function generic() { return await parseJsonBody<string>(); }\n");
737+
ASSERT_NOT_NULL(r);
738+
ASSERT_TRUE(find_resolved(r, "plain", "parseJsonBody") >= 0);
739+
ASSERT_TRUE(find_resolved(r, "generic", "parseJsonBody") >= 0);
740+
cbm_free_result(r);
741+
PASS();
742+
}
743+
732744
TEST(tslsp_generic_identity_inference) {
733745
CBMFileResult *r = extract_ts("function identity<T>(x: T): T { return x; }\n"
734746
"class Box { use(): void {} }\n"
@@ -4316,6 +4328,8 @@ SUITE(ts_lsp) {
43164328
RUN_TEST(tslsp_nocrash_using_decl);
43174329
RUN_TEST(tslsp_nocrash_megasource);
43184330

4331+
RUN_TEST(tslsp_await_generic_call_issue2210);
4332+
43194333
/* Category 11: generics deeper */
43204334
RUN_TEST(tslsp_generic_identity_inference);
43214335
RUN_TEST(tslsp_generic_array_map_chain);

0 commit comments

Comments
 (0)