code-gauge 4.2.0 → 4.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -72,6 +72,20 @@ const CSHARP_QUERY_BINDING_PARENT_TYPES: &[&str] = &[
72
72
  "query_expression",
73
73
  ];
74
74
 
75
+ /// C/C++ declarators wrapping the declared name (`int* p = q;`, `int& r = *q;`, `int a[n] = {};`,
76
+ /// `int (*fp)(int) = g;`, member pointer `int C::* p = q;`): the definition field is checked on the
77
+ /// outermost wrapper.
78
+ const DECLARATOR_WRAPPER_TYPES: &[&str] = &[
79
+ "pointer_declarator",
80
+ "reference_declarator",
81
+ "array_declarator",
82
+ "parenthesized_declarator",
83
+ "function_declarator",
84
+ "attributed_declarator",
85
+ "pointer_type_declarator",
86
+ "qualified_identifier",
87
+ ];
88
+
75
89
  /// Multi-target lists (`a, b = ...`, `a, b := ...`) whose holder's `left` field marks definitions.
76
90
  const DEFINITION_LIST_NODE_TYPES: &[&str] = &["expression_list", "pattern_list", "tuple_pattern"];
77
91
  const DEFINITION_LIST_HOLDER_TYPES: &[&str] = &[
@@ -119,9 +133,7 @@ pub fn measure_dep_degree(
119
133
  let mut pairs = 0u64;
120
134
  for index in 0..leaves.len() {
121
135
  let leaf = &leaves[index];
122
- if !VARIABLE_NODE_TYPES.contains(&leaf.node.kind())
123
- && !crate::util::is_kotlin_callable_receiver(leaf.node)
124
- {
136
+ if !is_variable_leaf(leaf.node) {
125
137
  continue;
126
138
  }
127
139
  let name = node_text(leaf.node, code);
@@ -147,6 +159,33 @@ pub fn measure_dep_degree(
147
159
  pairs
148
160
  }
149
161
 
162
+ /// A C++ member-pointer variable (`int C::* p`, also `int C::* arr[1]`) is declared as a
163
+ /// `type_identifier` under the `pointer_type_declarator` spelling `C::*`, possibly through further
164
+ /// declarator wrappers; every other `type_identifier` names a type, not a variable.
165
+ fn is_variable_leaf(node: Node<'_>) -> bool {
166
+ VARIABLE_NODE_TYPES.contains(&node.kind())
167
+ || crate::util::is_kotlin_callable_receiver(node)
168
+ || (node.kind() == "type_identifier" && is_member_pointer_name(node))
169
+ }
170
+
171
+ fn is_member_pointer_name(node: Node<'_>) -> bool {
172
+ let mut current = node;
173
+ while let Some(parent) = current.parent() {
174
+ if parent.kind() == "pointer_type_declarator" {
175
+ return true;
176
+ }
177
+ // The climb follows the declared-name position only, exactly like unwrap_declarator_wrappers.
178
+ if !DECLARATOR_WRAPPER_TYPES.contains(&parent.kind())
179
+ || parent.kind() == "qualified_identifier"
180
+ || field_name_in_parent(current, parent).is_some_and(|field| field != "declarator")
181
+ {
182
+ return false;
183
+ }
184
+ current = parent;
185
+ }
186
+ false
187
+ }
188
+
150
189
  /// Names a C# accessor body can read without declaring them in its own subtree: the owning
151
190
  /// indexer's parameters (including a `params` array, which the grammar names directly on the
152
191
  /// parameter list) and, in a setter, initializer, or event accessor, the implicit `value`.
@@ -270,11 +309,10 @@ fn is_structural_definition(leaf: &DepDegreeLeaf<'_>) -> bool {
270
309
  {
271
310
  return true;
272
311
  }
273
- if DEFINITION_FIELD_BY_PARENT_TYPE
274
- .iter()
275
- .any(|(parent_type, definition_field)| {
276
- *parent_type == parent.kind() && leaf.field_name == Some(definition_field)
277
- })
312
+ let (declared, declared_field) = unwrap_declarator_wrappers(leaf);
313
+ if declared
314
+ .parent()
315
+ .is_some_and(|holder| is_definition_field(holder, declared_field))
278
316
  {
279
317
  return true;
280
318
  }
@@ -288,6 +326,42 @@ fn is_structural_definition(leaf: &DepDegreeLeaf<'_>) -> bool {
288
326
  && field_name_in_parent(parent, holder) == Some("left")
289
327
  }
290
328
 
329
+ fn is_definition_field(holder: Node<'_>, field_name: Option<&str>) -> bool {
330
+ DEFINITION_FIELD_BY_PARENT_TYPE
331
+ .iter()
332
+ .any(|(parent_type, definition_field)| {
333
+ *parent_type == holder.kind() && field_name == Some(definition_field)
334
+ })
335
+ }
336
+
337
+ /// Climbs from the identifier through the C/C++ declarator wrappers it is the declared name of
338
+ /// (`reference_declarator` and `parenthesized_declarator` expose no field, so their name is the
339
+ /// fieldless child; a member pointer's `pointer_type_declarator` is the `name` of a
340
+ /// `qualified_identifier`; an `array_declarator` size or a nested parameter has another field and
341
+ /// stops the climb) to the outermost wrapper and its field in the declaration.
342
+ fn unwrap_declarator_wrappers<'t>(leaf: &DepDegreeLeaf<'t>) -> (Node<'t>, Option<&'static str>) {
343
+ let mut current = leaf.node;
344
+ let mut field_name = leaf.field_name;
345
+ while let Some(parent) = current
346
+ .parent()
347
+ .filter(|parent| DECLARATOR_WRAPPER_TYPES.contains(&parent.kind()))
348
+ {
349
+ let declared_field = if parent.kind() == "qualified_identifier" {
350
+ "name"
351
+ } else {
352
+ "declarator"
353
+ };
354
+ if field_name.is_some_and(|name| name != declared_field) {
355
+ break;
356
+ }
357
+ field_name = parent
358
+ .parent()
359
+ .and_then(|grandparent| field_name_in_parent(parent, grandparent));
360
+ current = parent;
361
+ }
362
+ (current, field_name)
363
+ }
364
+
291
365
  /// Mirrors isParameterDefinition in metrics.ts: an ancestor reached through declarator wrappers
292
366
  /// (C/C++ function-pointer or array parameters) is a parameter-ish node, or the identifier
293
367
  /// directly occupies a parameter field; type annotations and default values bind nothing.
@@ -306,14 +380,31 @@ fn is_parameter_definition(leaf: &DepDegreeLeaf<'_>) -> bool {
306
380
  }
307
381
  let mut current = leaf.node;
308
382
  let mut depth = 0usize;
383
+ // Only the member pointer's own declared name may climb past its qualified name; a size or
384
+ // other expression inside the same declarator (`int C::* a[n]`) is a read.
385
+ let declares_member_pointer = is_member_pointer_name(leaf.node);
386
+ let mut in_member_pointer = false;
309
387
  loop {
310
388
  let Some(parent) = current.parent() else {
311
389
  return false;
312
390
  };
313
391
  let parent_is_parameterish = parent.kind().contains("parameter");
314
- // Beyond the grandparent, only declarator wrappers keep climbing; checking this before
315
- // the field lookup also keeps reads inside high-arity nodes O(1).
316
- if depth >= 1 && !parent_is_parameterish && !parent.kind().contains("declarator") {
392
+ // Beyond the grandparent, only declarator wrappers keep climbing, plus the
393
+ // `qualified_identifier` nodes that spell a member-pointer parameter's class (`int C::* q`,
394
+ // `int N::C::* q`) — entered from the pointer declarator and continued through the `name`
395
+ // side, so a qualified constant in a default value or array size (`int x = N::M::C`) stays
396
+ // a read. Checking this before the field lookup also keeps reads inside high-arity nodes
397
+ // O(1).
398
+ let continues_member_pointer = declares_member_pointer
399
+ && parent.kind() == "qualified_identifier"
400
+ && (current.kind() == "pointer_type_declarator" || in_member_pointer)
401
+ && field_name_in_parent(current, parent) == Some("name");
402
+ in_member_pointer = continues_member_pointer;
403
+ if depth >= 1
404
+ && !parent_is_parameterish
405
+ && !parent.kind().contains("declarator")
406
+ && !continues_member_pointer
407
+ {
317
408
  return false;
318
409
  }
319
410
  let field = if depth == 0 {