| 1065 | } |
| 1066 | |
| 1067 | static bool is_binding_occurrence(CBMExtractCtx *ctx, TSNode node, const CBMLangSpec *spec, |
| 1068 | WalkState *state) { |
| 1069 | const CBMOccurrenceSpec *occurrence = &occurrence_specs[ctx->language]; |
| 1070 | if (is_exact_language_binding(ctx, node, state)) { |
| 1071 | return true; |
| 1072 | } |
| 1073 | if (is_policy_binding(ctx, node, occurrence)) { |
| 1074 | return true; |
| 1075 | } |
| 1076 | |
| 1077 | TSNode current = node; |
| 1078 | TSTreeCursor *cursor = reset_occurrence_cursor(state, node); |
| 1079 | while (!ts_node_is_null(current)) { |
| 1080 | TSNode parent; |
| 1081 | const char *field; |
| 1082 | if (!occurrence_parent(cursor, current, &parent, &field)) { |
| 1083 | break; |
| 1084 | } |
| 1085 | if (is_value_field(field)) { |
| 1086 | return false; |
| 1087 | } |
| 1088 | /* A declaration container binds its name/pattern, not the symbols used |
| 1089 | * by its type annotation. Both TypeScript (`cfg: Config`) and Go |
| 1090 | * (`cfg Config`) expose that annotation through the exact `type` field; |
| 1091 | * stop before the whole-parameter binding rule can swallow `Config`. |
| 1092 | * The emitted occurrence remains an ordinary USAGE, never a callable |
| 1093 | * reference merely because the target happens to be a type. */ |
| 1094 | if (field && strcmp(field, "type") == 0) { |
| 1095 | return false; |
| 1096 | } |
| 1097 | |
| 1098 | const char *kind = ts_node_type(parent); |
| 1099 | if (kind_in_exact_set(kind, common_whole_binding_nodes) || |
| 1100 | kind_in_exact_set(kind, occurrence->whole_binding_nodes)) { |
| 1101 | return true; |
| 1102 | } |
| 1103 | |
| 1104 | bool variable_container = |
| 1105 | spec->variable_node_types && cbm_kind_in_set(parent, spec->variable_node_types); |
| 1106 | if (ctx->language == CBM_LANG_ELIXIR && strcmp(kind, "binary_operator") == 0) { |
| 1107 | variable_container = elixir_binary_operator_binds(ctx, parent); |
| 1108 | } |
| 1109 | bool declared_container = |
| 1110 | kind_in_exact_set(kind, field_binding_nodes) || |
| 1111 | (spec->function_node_types && cbm_kind_in_set(parent, spec->function_node_types)) || |
| 1112 | (spec->class_node_types && cbm_kind_in_set(parent, spec->class_node_types)) || |
| 1113 | (spec->field_node_types && cbm_kind_in_set(parent, spec->field_node_types)) || |
| 1114 | variable_container; |
| 1115 | if (declared_container) { |
| 1116 | for (const char *const *binding_field = binding_fields; *binding_field; |
| 1117 | binding_field++) { |
| 1118 | if (field_contains_node(parent, *binding_field, node)) { |
| 1119 | return true; |
| 1120 | } |
| 1121 | } |
| 1122 | } |
| 1123 | current = parent; |
| 1124 | } |
no test coverage detected