Resolve the write target of a node in an assignment_node_types set. For a plain assignment the target is the "left" field (or first child). For an increment/decrement unary expression (`x++`, `++x`, C# postfix_/prefix_unary_expression) there is no "left" field and the operand may sit on either side of the operator token, so scan named children for the first identifier / member-style operand. Re
| 199 | // first identifier / member-style operand. Returns a null node when no simple |
| 200 | // target is found. |
| 201 | static TSNode resolve_write_lhs_node(TSNode node) { |
| 202 | TSNode left = ts_node_child_by_field_name(node, TS_FIELD("left")); |
| 203 | if (!ts_node_is_null(left)) { |
| 204 | return left; |
| 205 | } |
| 206 | const char *nk = ts_node_type(node); |
| 207 | if (strcmp(nk, "postfix_unary_expression") == 0 || strcmp(nk, "prefix_unary_expression") == 0 || |
| 208 | strcmp(nk, "update_expression") == 0) { |
| 209 | // Only ++/-- mutate their operand. Other unary postfix/prefix forms |
| 210 | // (C# null-forgiving `x!`, address-of `&x`, deref `*x`, logical `!x`) |
| 211 | // READ the operand — never treat them as writes. |
| 212 | bool is_incdec = false; |
| 213 | uint32_t total = ts_node_child_count(node); |
| 214 | for (uint32_t i = 0; i < total; i++) { |
| 215 | TSNode c = ts_node_child(node, i); |
| 216 | if (ts_node_is_named(c)) { |
| 217 | continue; // operator is an anonymous token |
| 218 | } |
| 219 | const char *op = ts_node_type(c); |
| 220 | if (strcmp(op, "++") == 0 || strcmp(op, "--") == 0) { |
| 221 | is_incdec = true; |
| 222 | break; |
| 223 | } |
| 224 | } |
| 225 | if (!is_incdec) { |
| 226 | return (TSNode){0}; |
| 227 | } |
| 228 | uint32_t cnc = ts_node_named_child_count(node); |
| 229 | for (uint32_t i = 0; i < cnc; i++) { |
| 230 | TSNode c = ts_node_named_child(node, i); |
| 231 | const char *ck = ts_node_type(c); |
| 232 | if (strcmp(ck, "identifier") == 0 || strcmp(ck, "simple_identifier") == 0 || |
| 233 | strcmp(ck, "member_access_expression") == 0 || |
| 234 | strcmp(ck, "field_expression") == 0 || strcmp(ck, "field_access") == 0 || |
| 235 | strcmp(ck, "selector_expression") == 0 || strcmp(ck, "subscript_expression") == 0 || |
| 236 | strcmp(ck, "index_expression") == 0) { |
| 237 | return c; |
| 238 | } |
| 239 | } |
| 240 | return (TSNode){0}; |
| 241 | } |
| 242 | if (ts_node_child_count(node) > 0) { |
| 243 | return ts_node_child(node, 0); |
| 244 | } |
| 245 | return (TSNode){0}; |
| 246 | } |
| 247 | |
| 248 | // Try to emit a write for an assignment node. |
| 249 | static void try_emit_assignment_write(CBMExtractCtx *ctx, TSNode node, const char *func_qn) { |
no outgoing calls
no test coverage detected