code-gauge 4.7.0 → 4.8.0

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.
Files changed (78) hide show
  1. package/README.md +1 -1
  2. package/THIRD-PARTY-NOTICES.txt +595 -29
  3. package/dist/cli.cjs +1 -1
  4. package/dist/cli.cjs.map +1 -1
  5. package/dist/cli.js +1 -1
  6. package/dist/cli.js.map +1 -1
  7. package/dist/cliConfig.cjs +1 -1
  8. package/dist/cliConfig.cjs.map +1 -1
  9. package/dist/cliConfig.js +1 -1
  10. package/dist/cliConfig.js.map +1 -1
  11. package/dist/crossFileDuplication.cjs +1 -1
  12. package/dist/crossFileDuplication.cjs.map +1 -1
  13. package/dist/crossFileDuplication.js +1 -1
  14. package/dist/crossFileDuplication.js.map +1 -1
  15. package/dist/crossFileNearMiss.cjs +1 -1
  16. package/dist/crossFileNearMiss.cjs.map +1 -1
  17. package/dist/crossFileNearMiss.js +1 -1
  18. package/dist/crossFileNearMiss.js.map +1 -1
  19. package/dist/diffCommand.cjs +3 -3
  20. package/dist/diffCommand.cjs.map +1 -1
  21. package/dist/diffCommand.js +3 -3
  22. package/dist/diffCommand.js.map +1 -1
  23. package/dist/duplicateSelection.cjs +1 -1
  24. package/dist/duplicateSelection.cjs.map +1 -1
  25. package/dist/duplicateSelection.js +1 -1
  26. package/dist/duplicateSelection.js.map +1 -1
  27. package/dist/duplication.cjs +1 -1
  28. package/dist/duplication.cjs.map +1 -1
  29. package/dist/duplication.js +1 -1
  30. package/dist/duplication.js.map +1 -1
  31. package/dist/git.cjs +1 -1
  32. package/dist/git.cjs.map +1 -1
  33. package/dist/git.js +1 -1
  34. package/dist/git.js.map +1 -1
  35. package/dist/languages.cjs +1 -1
  36. package/dist/languages.cjs.map +1 -1
  37. package/dist/languages.js +1 -1
  38. package/dist/languages.js.map +1 -1
  39. package/dist/metrics.cjs +1 -1
  40. package/dist/metrics.cjs.map +1 -1
  41. package/dist/metrics.d.ts +8 -3
  42. package/dist/metrics.js +1 -1
  43. package/dist/metrics.js.map +1 -1
  44. package/dist/nativeMetrics.cjs +3 -3
  45. package/dist/nativeMetrics.cjs.map +1 -1
  46. package/dist/nativeMetrics.d.ts +19 -3
  47. package/dist/nativeMetrics.js +3 -3
  48. package/dist/nativeMetrics.js.map +1 -1
  49. package/dist/regressionGate.cjs +1 -1
  50. package/dist/regressionGate.cjs.map +1 -1
  51. package/dist/regressionGate.js +1 -1
  52. package/dist/regressionGate.js.map +1 -1
  53. package/dist/scan.cjs +1 -1
  54. package/dist/scan.cjs.map +1 -1
  55. package/dist/scan.d.ts +2 -2
  56. package/dist/scan.js +1 -1
  57. package/dist/scan.js.map +1 -1
  58. package/dist/wasmBinding.cjs +1 -1
  59. package/dist/wasmBinding.cjs.map +1 -1
  60. package/dist/wasmBinding.js +1 -1
  61. package/dist/wasmBinding.js.map +1 -1
  62. package/native/Cargo.lock +48 -15
  63. package/native/Cargo.toml +11 -10
  64. package/native/code-gauge.wasm +0 -0
  65. package/native/src/complexity.rs +152 -255
  66. package/native/src/dep_degree.rs +79 -65
  67. package/native/src/duplication.rs +114 -97
  68. package/native/src/functions.rs +181 -206
  69. package/native/src/languages.rs +48 -7
  70. package/native/src/lib.rs +10 -5
  71. package/native/src/measure.rs +125 -150
  72. package/native/src/napi.rs +61 -2
  73. package/native/src/ncss.rs +108 -104
  74. package/native/src/near_miss.rs +7 -7
  75. package/native/src/tree_index.rs +119 -0
  76. package/native/src/util.rs +15 -55
  77. package/native/src/worker_pool.rs +53 -0
  78. package/package.json +10 -10
@@ -1,14 +1,13 @@
1
- use std::collections::{HashMap, HashSet};
1
+ use rustc_hash::{FxHashMap, FxHashSet};
2
2
  use tree_sitter::Node;
3
3
 
4
4
  use crate::complexity::is_function_boundary;
5
- use crate::util::{is_identifier_leaf, node_text, Source};
5
+ use crate::tree_index::NodeExt;
6
+ use crate::util::{node_text, Source};
6
7
 
7
8
  /// Leaf node types treated as variable references by the def-use approximation.
8
9
  const VARIABLE_NODE_TYPES: &[&str] = &[
9
10
  "identifier",
10
- "simple_identifier",
11
- "interpolated_identifier",
12
11
  "implicit_parameter",
13
12
  "instance_variable",
14
13
  "class_variable",
@@ -50,18 +49,10 @@ const DEFINITION_FIELD_BY_PARENT_TYPE: &[(&str, &str)] = &[
50
49
  ("from_clause", "name"),
51
50
  ];
52
51
 
53
- /// Kotlin has no grammar fields: an identifier directly under one of these declares a binding
52
+ /// Kotlin marks no binding fields: an identifier directly under one of these declares a binding
54
53
  /// (`val x`, `for (x in xs)`, lambda parameters, and the `catch (e: T)` exception name).
55
54
  const KOTLIN_DEFINITION_PARENT_TYPES: &[&str] = &["variable_declaration", "catch_block"];
56
55
 
57
- /// Kotlin parameter nodes whose identifier child is the declared name; the parameter LIST nodes
58
- /// (`function_value_parameters`) also hold default-value expressions, which are reads.
59
- const KOTLIN_PARAMETER_TYPES: &[&str] = &[
60
- "parameter",
61
- "parameter_with_optional_type",
62
- "class_parameter",
63
- ];
64
-
65
56
  /// C# LINQ clauses that bind a range variable as their first identifier child (no grammar field):
66
57
  /// `join y in ...`, `into ys`, `let z = ...`, and a query continuation `into g`.
67
58
  const CSHARP_QUERY_BINDING_PARENT_TYPES: &[&str] = &[
@@ -112,7 +103,7 @@ struct DepDegreeLeaf<'t> {
112
103
  pub fn measure_dep_degree(
113
104
  function_node: Node<'_>,
114
105
  code: &Source<'_>,
115
- function_nodes: &HashSet<&'static str>,
106
+ function_nodes: &FxHashSet<&'static str>,
116
107
  ) -> u64 {
117
108
  let mut leaves = Vec::new();
118
109
  let mut next_scope_id = 0usize;
@@ -125,14 +116,14 @@ pub fn measure_dep_degree(
125
116
  &mut leaves,
126
117
  true,
127
118
  );
128
- let mut definition_scopes_by_name: HashMap<&str, Vec<String>> = HashMap::new();
119
+ let mut definition_scopes_by_name: FxHashMap<&str, Vec<String>> = FxHashMap::default();
129
120
  for name in implicit_accessor_definitions(function_node, code) {
130
121
  add_definition(&mut definition_scopes_by_name, name, "");
131
122
  }
132
123
  let mut pairs = 0u64;
133
124
  for index in 0..leaves.len() {
134
125
  let leaf = &leaves[index];
135
- if !is_variable_leaf(leaf.node) {
126
+ if !is_variable_leaf(leaf) {
136
127
  continue;
137
128
  }
138
129
  let name = node_text(leaf.node, code);
@@ -160,22 +151,30 @@ pub fn measure_dep_degree(
160
151
 
161
152
  /// A C++ member-pointer variable (`int C::* p`, also `int C::* arr[1]`) is declared as a
162
153
  /// `type_identifier` under the `pointer_type_declarator` spelling `C::*`, possibly through further
163
- /// declarator wrappers; every other `type_identifier` names a type, not a variable.
164
- fn is_variable_leaf(node: Node<'_>) -> bool {
165
- VARIABLE_NODE_TYPES.contains(&node.kind())
166
- || crate::util::is_kotlin_callable_receiver(node)
167
- || (node.kind() == "type_identifier" && is_member_pointer_name(node))
154
+ /// declarator wrappers; every other `type_identifier` names a type, not a variable. Kotlin spells
155
+ /// type names as identifiers: in a `user_type`, or as a type parameter's fieldless name (C# puts its
156
+ /// type parameter in a `name` field).
157
+ fn is_variable_leaf(leaf: &DepDegreeLeaf<'_>) -> bool {
158
+ let node = leaf.node;
159
+ if node.kind_name() == "type_identifier" {
160
+ return is_member_pointer_name(node);
161
+ }
162
+ VARIABLE_NODE_TYPES.contains(&node.kind_name())
163
+ && !node.parent_node().is_some_and(|parent| {
164
+ parent.kind_name() == "user_type"
165
+ || (parent.kind_name() == "type_parameter" && leaf.field_name.is_none())
166
+ })
168
167
  }
169
168
 
170
169
  fn is_member_pointer_name(node: Node<'_>) -> bool {
171
170
  let mut current = node;
172
- while let Some(parent) = current.parent() {
173
- if parent.kind() == "pointer_type_declarator" {
171
+ while let Some(parent) = current.parent_node() {
172
+ if parent.kind_name() == "pointer_type_declarator" {
174
173
  return true;
175
174
  }
176
175
  // The climb follows the declared-name position only, exactly like unwrap_declarator_wrappers.
177
- if !DECLARATOR_WRAPPER_TYPES.contains(&parent.kind())
178
- || parent.kind() == "qualified_identifier"
176
+ if !DECLARATOR_WRAPPER_TYPES.contains(&parent.kind_name())
177
+ || parent.kind_name() == "qualified_identifier"
179
178
  || field_name_in_parent(current, parent).is_some_and(|field| field != "declarator")
180
179
  {
181
180
  return false;
@@ -189,7 +188,7 @@ fn is_member_pointer_name(node: Node<'_>) -> bool {
189
188
  /// indexer's parameters (including a `params` array, which the grammar names directly on the
190
189
  /// parameter list) and, in a setter, initializer, or event accessor, the implicit `value`.
191
190
  fn implicit_accessor_definitions<'a>(function_node: Node<'_>, code: &Source<'a>) -> Vec<&'a str> {
192
- if function_node.kind() != "accessor_declaration" {
191
+ if function_node.kind_name() != "accessor_declaration" {
193
192
  return Vec::new();
194
193
  }
195
194
  let mut names = Vec::new();
@@ -201,9 +200,11 @@ fn implicit_accessor_definitions<'a>(function_node: Node<'_>, code: &Source<'a>)
201
200
  {
202
201
  names.push("value");
203
202
  }
204
- let owner = function_node.parent().and_then(|list| list.parent());
203
+ let owner = function_node
204
+ .parent_node()
205
+ .and_then(|list| list.parent_node());
205
206
  if let Some(parameters) = owner
206
- .filter(|owner| owner.kind() == "indexer_declaration")
207
+ .filter(|owner| owner.kind_name() == "indexer_declaration")
207
208
  .and_then(|owner| owner.child_by_field_name("parameters"))
208
209
  {
209
210
  let declared = crate::util::named_children(parameters)
@@ -223,17 +224,17 @@ fn collect_dep_degree_leaves<'t>(
223
224
  field_name: Option<&'static str>,
224
225
  scope: &str,
225
226
  next_scope_id: &mut usize,
226
- function_nodes: &HashSet<&'static str>,
227
+ function_nodes: &FxHashSet<&'static str>,
227
228
  leaves: &mut Vec<DepDegreeLeaf<'t>>,
228
229
  is_measured_root: bool,
229
230
  ) {
230
231
  if matches!(
231
- node.kind(),
232
- "comment" | "line_comment" | "block_comment" | "multiline_comment"
232
+ node.kind_name(),
233
+ "comment" | "line_comment" | "block_comment"
233
234
  ) {
234
235
  return;
235
236
  }
236
- if is_identifier_leaf(node) {
237
+ if node.child_count() == 0 {
237
238
  leaves.push(DepDegreeLeaf {
238
239
  node,
239
240
  field_name,
@@ -268,7 +269,7 @@ fn collect_dep_degree_leaves<'t>(
268
269
  }
269
270
 
270
271
  fn add_definition<'a>(
271
- definition_scopes_by_name: &mut HashMap<&'a str, Vec<String>>,
272
+ definition_scopes_by_name: &mut FxHashMap<&'a str, Vec<String>>,
272
273
  name: &'a str,
273
274
  scope: &str,
274
275
  ) {
@@ -291,37 +292,39 @@ fn is_definition_visible(definition_scopes: Option<&Vec<String>>, scope: &str) -
291
292
  }
292
293
 
293
294
  fn is_structural_definition(leaf: &DepDegreeLeaf<'_>) -> bool {
294
- let Some(parent) = leaf.node.parent() else {
295
+ let Some(parent) = leaf.node.parent_node() else {
295
296
  return false;
296
297
  };
297
- if leaf.node.kind() == "simple_identifier"
298
- && KOTLIN_DEFINITION_PARENT_TYPES.contains(&parent.kind())
298
+ // C#'s `variable_declaration` holds its type in the `type` field; Kotlin's bound name has none.
299
+ if leaf.node.kind_name() == "identifier"
300
+ && leaf.field_name.is_none()
301
+ && KOTLIN_DEFINITION_PARENT_TYPES.contains(&parent.kind_name())
299
302
  {
300
303
  return true;
301
304
  }
302
- if leaf.node.kind() == "identifier"
303
- && CSHARP_QUERY_BINDING_PARENT_TYPES.contains(&parent.kind())
305
+ if leaf.node.kind_name() == "identifier"
306
+ && CSHARP_QUERY_BINDING_PARENT_TYPES.contains(&parent.kind_name())
304
307
  && crate::util::named_children(parent)
305
308
  .into_iter()
306
- .find(|child| child.kind() == "identifier")
309
+ .find(|child| child.kind_name() == "identifier")
307
310
  .is_some_and(|first| first.id() == leaf.node.id())
308
311
  {
309
312
  return true;
310
313
  }
311
314
  let (declared, declared_field) = unwrap_declarator_wrappers(leaf);
312
315
  if declared
313
- .parent()
316
+ .parent_node()
314
317
  .is_some_and(|holder| is_definition_field(holder, declared_field))
315
318
  {
316
319
  return true;
317
320
  }
318
- if !DEFINITION_LIST_NODE_TYPES.contains(&parent.kind()) {
321
+ if !DEFINITION_LIST_NODE_TYPES.contains(&parent.kind_name()) {
319
322
  return false;
320
323
  }
321
- let Some(holder) = parent.parent() else {
324
+ let Some(holder) = parent.parent_node() else {
322
325
  return false;
323
326
  };
324
- DEFINITION_LIST_HOLDER_TYPES.contains(&holder.kind())
327
+ DEFINITION_LIST_HOLDER_TYPES.contains(&holder.kind_name())
325
328
  && field_name_in_parent(parent, holder) == Some("left")
326
329
  }
327
330
 
@@ -329,7 +332,7 @@ fn is_definition_field(holder: Node<'_>, field_name: Option<&str>) -> bool {
329
332
  DEFINITION_FIELD_BY_PARENT_TYPE
330
333
  .iter()
331
334
  .any(|(parent_type, definition_field)| {
332
- *parent_type == holder.kind() && field_name == Some(definition_field)
335
+ *parent_type == holder.kind_name() && field_name == Some(definition_field)
333
336
  })
334
337
  }
335
338
 
@@ -342,10 +345,10 @@ fn unwrap_declarator_wrappers<'t>(leaf: &DepDegreeLeaf<'t>) -> (Node<'t>, Option
342
345
  let mut current = leaf.node;
343
346
  let mut field_name = leaf.field_name;
344
347
  while let Some(parent) = current
345
- .parent()
346
- .filter(|parent| DECLARATOR_WRAPPER_TYPES.contains(&parent.kind()))
348
+ .parent_node()
349
+ .filter(|parent| DECLARATOR_WRAPPER_TYPES.contains(&parent.kind_name()))
347
350
  {
348
- let declared_field = if parent.kind() == "qualified_identifier" {
351
+ let declared_field = if parent.kind_name() == "qualified_identifier" {
349
352
  "name"
350
353
  } else {
351
354
  "declarator"
@@ -354,7 +357,7 @@ fn unwrap_declarator_wrappers<'t>(leaf: &DepDegreeLeaf<'t>) -> (Node<'t>, Option
354
357
  break;
355
358
  }
356
359
  field_name = parent
357
- .parent()
360
+ .parent_node()
358
361
  .and_then(|grandparent| field_name_in_parent(parent, grandparent));
359
362
  current = parent;
360
363
  }
@@ -365,18 +368,6 @@ fn unwrap_declarator_wrappers<'t>(leaf: &DepDegreeLeaf<'t>) -> (Node<'t>, Option
365
368
  /// (C/C++ function-pointer or array parameters) is a parameter-ish node, or the identifier
366
369
  /// directly occupies a parameter field; type annotations and default values bind nothing.
367
370
  fn is_parameter_definition(leaf: &DepDegreeLeaf<'_>) -> bool {
368
- // Kotlin has no `type`/`value` fields to veto default values: a function parameter's default
369
- // sits in the parameter list (`fun f(b: Int = a)`), a class parameter's inside the parameter
370
- // node (`class A(val y: Int = a)`), so only a parameter node's first identifier child binds.
371
- if leaf.node.kind() == "simple_identifier" {
372
- return leaf.node.parent().is_some_and(|parent| {
373
- KOTLIN_PARAMETER_TYPES.contains(&parent.kind())
374
- && crate::util::named_children(parent)
375
- .into_iter()
376
- .find(|child| child.kind() == "simple_identifier")
377
- .is_some_and(|first| first.id() == leaf.node.id())
378
- });
379
- }
380
371
  let mut current = leaf.node;
381
372
  let mut depth = 0usize;
382
373
  // Only the member pointer's own declared name may climb past its qualified name; a size or
@@ -384,10 +375,23 @@ fn is_parameter_definition(leaf: &DepDegreeLeaf<'_>) -> bool {
384
375
  let declares_member_pointer = is_member_pointer_name(leaf.node);
385
376
  let mut in_member_pointer = false;
386
377
  loop {
387
- let Some(parent) = current.parent() else {
378
+ let Some(parent) = current.parent_node() else {
388
379
  return false;
389
380
  };
390
- let parent_is_parameterish = parent.kind().contains("parameter");
381
+ // Kotlin has no `type`/`value` fields to veto default values: a function parameter's default
382
+ // sits in the parameter list (`fun f(b: Int = a)`), a class parameter's inside the parameter
383
+ // node (`class A(val y: Int = a)`), so only such a node's first identifier child binds.
384
+ if parent.kind_name() == "function_value_parameters" {
385
+ return false;
386
+ }
387
+ if is_kotlin_parameter(parent) {
388
+ return depth == 0
389
+ && crate::util::named_children(parent)
390
+ .into_iter()
391
+ .find(|child| child.kind_name() == "identifier")
392
+ .is_some_and(|first| first.id() == leaf.node.id());
393
+ }
394
+ let parent_is_parameterish = parent.kind_name().contains("parameter");
391
395
  // Beyond the grandparent, only declarator wrappers keep climbing, plus the
392
396
  // `qualified_identifier` nodes that spell a member-pointer parameter's class (`int C::* q`,
393
397
  // `int N::C::* q`) — entered from the pointer declarator and continued through the `name`
@@ -395,13 +399,13 @@ fn is_parameter_definition(leaf: &DepDegreeLeaf<'_>) -> bool {
395
399
  // a read. Checking this before the field lookup also keeps reads inside high-arity nodes
396
400
  // O(1).
397
401
  let continues_member_pointer = declares_member_pointer
398
- && parent.kind() == "qualified_identifier"
399
- && (current.kind() == "pointer_type_declarator" || in_member_pointer)
402
+ && parent.kind_name() == "qualified_identifier"
403
+ && (current.kind_name() == "pointer_type_declarator" || in_member_pointer)
400
404
  && field_name_in_parent(current, parent) == Some("name");
401
405
  in_member_pointer = continues_member_pointer;
402
406
  if depth >= 1
403
407
  && !parent_is_parameterish
404
- && !parent.kind().contains("declarator")
408
+ && !parent.kind_name().contains("declarator")
405
409
  && !continues_member_pointer
406
410
  {
407
411
  return false;
@@ -424,6 +428,16 @@ fn is_parameter_definition(leaf: &DepDegreeLeaf<'_>) -> bool {
424
428
  }
425
429
  }
426
430
 
431
+ /// A Kotlin class parameter, or a setter's parameter list (`set(value)`; Ruby's `setter` is a method
432
+ /// name, never under a property).
433
+ fn is_kotlin_parameter(node: Node<'_>) -> bool {
434
+ node.kind_name() == "class_parameter"
435
+ || (node.kind_name() == "setter"
436
+ && node
437
+ .parent_node()
438
+ .is_some_and(|property| property.kind_name() == "property_declaration"))
439
+ }
440
+
427
441
  /// Only called with small-arity parents (declarator wrappers, definition-list holders).
428
442
  fn field_name_in_parent(node: Node<'_>, parent: Node<'_>) -> Option<&'static str> {
429
443
  for index in 0..parent.child_count() {