Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion internal/cbm/lsp/ts_lsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -3760,6 +3760,31 @@ static void process_function_body(TSLSPContext *ctx, TSNode func_node, const cha
ctx->current_scope = saved_scope;
}

static void process_top_level_variable_arrows(TSLSPContext *ctx, TSNode declaration) {
if (!ctx || ts_node_is_null(declaration) || !ctx->module_qn)
return;

uint32_t count = ts_node_named_child_count(declaration);
for (uint32_t i = 0; i < count; i++) {
TSNode declarator = ts_node_named_child(declaration, i);
if (!node_kind_is(declarator, "variable_declarator"))
continue;

TSNode name = ts_node_child_by_field_name(declarator, "name", TS_LSP_FIELD_LEN("name"));
TSNode value =
ts_node_child_by_field_name(declarator, "value", TS_LSP_FIELD_LEN("value"));
if (!node_kind_is(name, "identifier") || !node_kind_is(value, "arrow_function"))
continue;

char *function_name = node_text(ctx, name);
if (!function_name)
continue;
const char *function_qn =
cbm_arena_sprintf(ctx->arena, "%s.%s", ctx->module_qn, function_name);
process_function_body(ctx, value, function_qn, NULL);
}
}

void ts_lsp_process_file(TSLSPContext *ctx, TSNode root) {
if (!ctx || ts_node_is_null(root))
return;
Expand All @@ -3777,6 +3802,14 @@ void ts_lsp_process_file(TSLSPContext *ctx, TSNode root) {
const char *kind = ts_node_type(child);
if (strcmp(kind, "lexical_declaration") == 0 || strcmp(kind, "variable_declaration") == 0) {
ts_process_statement(ctx, child);
} else if (strcmp(kind, "export_statement") == 0) {
uint32_t count = ts_node_named_child_count(child);
for (uint32_t j = 0; j < count; j++) {
TSNode inner = ts_node_named_child(child, j);
if (node_kind_is(inner, "lexical_declaration") ||
node_kind_is(inner, "variable_declaration"))
ts_process_statement(ctx, inner);
}
}
}

Expand All @@ -3785,7 +3818,9 @@ void ts_lsp_process_file(TSLSPContext *ctx, TSNode root) {
TSNode child = kids[i];
const char *kind = ts_node_type(child);

if (strcmp(kind, "function_declaration") == 0) {
if (strcmp(kind, "lexical_declaration") == 0 || strcmp(kind, "variable_declaration") == 0) {
process_top_level_variable_arrows(ctx, child);
} else if (strcmp(kind, "function_declaration") == 0) {
TSNode name = ts_node_child_by_field_name(child, "name", TS_LSP_FIELD_LEN("name"));
if (ts_node_is_null(name) || !ctx->module_qn)
continue;
Expand Down Expand Up @@ -3824,6 +3859,9 @@ void ts_lsp_process_file(TSLSPContext *ctx, TSNode root) {
continue;
const char *fqn = cbm_arena_sprintf(ctx->arena, "%s.%s", ctx->module_qn, fn);
process_function_body(ctx, inner, fqn, NULL);
} else if (strcmp(ik, "lexical_declaration") == 0 ||
strcmp(ik, "variable_declaration") == 0) {
process_top_level_variable_arrows(ctx, inner);
} else if (strcmp(ik, "class_declaration") == 0 ||
strcmp(ik, "internal_module") == 0) {
process_node(ctx, inner);
Expand Down
84 changes: 84 additions & 0 deletions tests/test_ts_lsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1157,6 +1157,89 @@ TEST(tslsp_jsx_component_resolutions_join_exact_tag_sites) {
PASS();
}

TEST(tslsp_jsx_top_level_async_arrow_callers_keep_exact_identity) {
const char *source = "function Card(): JSX.Element { return <span/>; }\n"
"const Direct = async () => <Card/>;\n"
"export const Exported = async () => <Card/>;\n"
"function Declared(): JSX.Element { return <Card/>; }\n";
const char *const declarations[] = {"const Direct", "export const Exported",
"function Declared"};
const char *const callers[] = {"test.main.Direct", "test.main.Exported", "test.main.Declared"};
CBMFileResult *r = extract_tsx(source);
const bool result_created = r != NULL;
bool exact_carrier[3] = {false};
bool exact_resolution[3] = {false};
bool resolved_invocation[3] = {false};
bool resolved_card_callee[3] = {false};
bool resolved_jsx_strategy[3] = {false};
int carrier_count[3] = {0};
int failed_cases = result_created ? 0 : 1;

if (!result_created)
printf(" JSX caller diagnostic: extraction failed\n");
if (r) {
for (int i = 0; i < 3; i++) {
const char *declaration = strstr(source, declarations[i]);
const char *site = declaration ? strstr(declaration, "<Card/>") : NULL;
if (!site) {
printf(" JSX caller diagnostic: source site missing for %s\n", callers[i]);
failed_cases++;
continue;
}
const uint32_t start = (uint32_t)(site - source);
const uint32_t end = start + (uint32_t)strlen("<Card/>");
const CBMCall *carrier = NULL;
for (int j = 0; j < r->calls.count; j++) {
const CBMCall *call = &r->calls.items[j];
if (call->callee_name && strcmp(call->callee_name, "Card") == 0 &&
call->site_start_byte == start && call->site_end_byte == end) {
carrier = call;
carrier_count[i]++;
}
}

const CBMResolvedCall *resolved = carrier ? cbm_pipeline_find_lsp_resolution(
&r->resolved_calls, carrier, false)
: NULL;
exact_carrier[i] = carrier && carrier->enclosing_func_qn &&
strcmp(carrier->enclosing_func_qn, callers[i]) == 0 &&
carrier->site_start_byte == start && carrier->site_end_byte == end &&
carrier->requires_lsp_resolution;
exact_resolution[i] = resolved && resolved->caller_qn &&
strcmp(resolved->caller_qn, callers[i]) == 0 &&
resolved->site_start_byte == start && resolved->site_end_byte == end;
resolved_invocation[i] = resolved && resolved->kind == CBM_RESOLVED_INVOCATION;
if (resolved && resolved->callee_qn) {
const size_t callee_len = strlen(resolved->callee_qn);
resolved_card_callee[i] = callee_len >= strlen(".Card") &&
strcmp(resolved->callee_qn + callee_len - strlen(".Card"),
".Card") == 0;
}
resolved_jsx_strategy[i] =
resolved && resolved->strategy && strcmp(resolved->strategy, "lsp_ts_jsx") == 0;
if (carrier_count[i] != 1 || !exact_carrier[i] || !exact_resolution[i] ||
!resolved_invocation[i] || !resolved_card_callee[i] || !resolved_jsx_strategy[i]) {
printf(" JSX caller diagnostic: expected=%s span=%u:%u carriers=%d caller=%s "
"requires_lsp=%d resolution=%s resolution_span=%u:%u kind=%d callee=%s "
"strategy=%s\n",
callers[i], start, end, carrier_count[i],
carrier && carrier->enclosing_func_qn ? carrier->enclosing_func_qn : "(null)",
carrier ? carrier->requires_lsp_resolution : 0,
resolved && resolved->caller_qn ? resolved->caller_qn : "(null)",
resolved ? resolved->site_start_byte : 0, resolved ? resolved->site_end_byte : 0,
resolved ? resolved->kind : -1,
resolved && resolved->callee_qn ? resolved->callee_qn : "(null)",
resolved && resolved->strategy ? resolved->strategy : "(null)");
failed_cases++;
}
}
}

cbm_free_result(r);
ASSERT_EQ(failed_cases, 0);
PASS();
}

/* A callable value named like a module component owns the JSX tag. The LSP
* pass must not confidently redirect it to the module function, and the
* carrier must require semantic resolution so graph construction cannot do
Expand Down Expand Up @@ -4867,6 +4950,7 @@ SUITE(ts_lsp) {
RUN_TEST(tslsp_jsx_component_with_children);
RUN_TEST(tslsp_jsx_import_requires_registered_component);
RUN_TEST(tslsp_jsx_component_resolutions_join_exact_tag_sites);
RUN_TEST(tslsp_jsx_top_level_async_arrow_callers_keep_exact_identity);
RUN_TEST(tslsp_jsx_local_tag_shadow_blocks_module_component);
RUN_TEST(tslsp_jsx_intrinsic_skipped);
RUN_TEST(tslsp_jsx_nested_component);
Expand Down
Loading