astgen: fix rlExpr bugs for inline while/for labels, assign_destructure, and @-quoted identifiers

Fix three issues in the RL annotation pre-pass (rlExpr):

1. Label detection for `inline while`/`inline for` now accounts for
   the `keyword_inline` token before checking for `identifier colon`,
   matching upstream fullWhileComponents/fullForComponents logic.

2. `assign_destructure` now recurses into variable nodes and the value
   expression with RL_RI_NONE, matching upstream behavior instead of
   returning false without visiting sub-expressions.

3. `rlTokenIdentEqual` now handles @"..."-quoted identifiers by comparing
   the quoted content rather than stopping at the `@` character, which
   previously caused all @-quoted identifiers to compare as equal.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-13 22:16:41 +00:00
parent 1c1407adb8
commit f54d3f94a3

View File

@@ -9547,11 +9547,33 @@ static bool nodesNeedRlContains(const AstGenCtx* ag, uint32_t node) {
}
// Compare two identifier tokens by their source text.
// Handles both regular identifiers and @"..."-quoted identifiers.
static bool rlTokenIdentEqual(
const Ast* tree, uint32_t tok_a, uint32_t tok_b) {
const char* src = tree->source;
uint32_t a_start = tree->tokens.starts[tok_a];
uint32_t b_start = tree->tokens.starts[tok_b];
bool a_quoted = (src[a_start] == '@');
bool b_quoted = (src[b_start] == '@');
if (a_quoted != b_quoted)
return false;
if (a_quoted) {
// Both are @"..."-quoted: skip '@"' prefix, compare up to '"'.
uint32_t ai = a_start + 2;
uint32_t bi = b_start + 2;
for (;;) {
char ca = src[ai];
char cb = src[bi];
if (ca == '"' && cb == '"')
return true;
if (ca == '"' || cb == '"')
return false;
if (ca != cb)
return false;
ai++;
bi++;
}
}
for (uint32_t i = 0;; i++) {
char ca = src[a_start + i];
char cb = src[b_start + i];
@@ -9998,10 +10020,13 @@ static bool rlExpr(
else_node = tree->extra_data.arr[nd.rhs + 2];
}
uint32_t main_tok = tree->nodes.main_tokens[node];
uint32_t tok_i = main_tok;
if (tok_i >= 1 && tree->tokens.tags[tok_i - 1] == TOKEN_KEYWORD_INLINE)
tok_i = tok_i - 1;
bool is_labeled
= (main_tok >= 2 && tree->tokens.tags[main_tok - 1] == TOKEN_COLON
&& tree->tokens.tags[main_tok - 2] == TOKEN_IDENTIFIER);
uint32_t label_token = is_labeled ? main_tok - 2 : UINT32_MAX;
= (tok_i >= 2 && tree->tokens.tags[tok_i - 1] == TOKEN_COLON
&& tree->tokens.tags[tok_i - 2] == TOKEN_IDENTIFIER);
uint32_t label_token = is_labeled ? tok_i - 2 : UINT32_MAX;
// Detect payload/error.
uint32_t last_cond_tok = lastToken(tree, cond_node);
@@ -10069,10 +10094,14 @@ static bool rlExpr(
}
uint32_t main_tok = tree->nodes.main_tokens[node];
bool is_labeled
= (main_tok >= 2 && tree->tokens.tags[main_tok - 1] == TOKEN_COLON
&& tree->tokens.tags[main_tok - 2] == TOKEN_IDENTIFIER);
uint32_t label_token = is_labeled ? main_tok - 2 : UINT32_MAX;
uint32_t for_tok_i = main_tok;
if (for_tok_i >= 1
&& tree->tokens.tags[for_tok_i - 1] == TOKEN_KEYWORD_INLINE)
for_tok_i = for_tok_i - 1;
bool is_labeled = (for_tok_i >= 2
&& tree->tokens.tags[for_tok_i - 1] == TOKEN_COLON
&& tree->tokens.tags[for_tok_i - 2] == TOKEN_IDENTIFIER);
uint32_t label_token = is_labeled ? for_tok_i - 2 : UINT32_MAX;
for (uint32_t i = 0; i < num_inputs; i++) {
uint32_t input = inputs[i];
@@ -10646,8 +10675,15 @@ static bool rlExpr(
case AST_NODE_AWAIT:
(void)rlExpr(ag, nd.lhs, block, RL_RI_NONE);
return false;
case AST_NODE_ASSIGN_DESTRUCTURE:
return false; // TODO if needed
case AST_NODE_ASSIGN_DESTRUCTURE: {
uint32_t extra_start = nd.lhs;
uint32_t variable_count = tree->extra_data.arr[extra_start];
for (uint32_t i = 0; i < variable_count; i++)
(void)rlExpr(ag, tree->extra_data.arr[extra_start + 1 + i], block,
RL_RI_NONE);
(void)rlExpr(ag, nd.rhs, block, RL_RI_NONE);
return false;
}
case AST_NODE_ASYNC_CALL_ONE:
case AST_NODE_ASYNC_CALL_ONE_COMMA:
case AST_NODE_ASYNC_CALL: