code-gauge 10.1.0 → 10.2.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.
- package/README.md +33 -14
- package/THIRD-PARTY-NOTICES.txt +8 -8
- package/dist/checkCommand.cjs +1 -1
- package/dist/checkCommand.cjs.map +1 -1
- package/dist/checkCommand.js +1 -1
- package/dist/checkCommand.js.map +1 -1
- package/dist/crossFileDuplication.cjs +1 -1
- package/dist/crossFileDuplication.cjs.map +1 -1
- package/dist/crossFileDuplication.d.ts +3 -2
- package/dist/crossFileDuplication.js +1 -1
- package/dist/crossFileDuplication.js.map +1 -1
- package/dist/crossFileNearMiss.cjs +1 -1
- package/dist/crossFileNearMiss.cjs.map +1 -1
- package/dist/crossFileNearMiss.d.ts +6 -4
- package/dist/crossFileNearMiss.js +1 -1
- package/dist/crossFileNearMiss.js.map +1 -1
- package/dist/duplication.cjs +1 -1
- package/dist/duplication.cjs.map +1 -1
- package/dist/duplication.d.ts +22 -7
- package/dist/duplication.js +1 -1
- package/dist/duplication.js.map +1 -1
- package/dist/thresholdCheck.cjs +1 -1
- package/dist/thresholdCheck.cjs.map +1 -1
- package/dist/thresholdCheck.d.ts +2 -0
- package/dist/thresholdCheck.js +1 -1
- package/dist/thresholdCheck.js.map +1 -1
- package/dist/thresholds.cjs.map +1 -1
- package/dist/thresholds.d.ts +2 -3
- package/dist/thresholds.js.map +1 -1
- package/dist/types.d.ts +19 -8
- package/native/Cargo.lock +16 -16
- package/native/Cargo.toml +8 -8
- package/native/code-gauge.wasm +0 -0
- package/native/src/complexity.rs +10 -1
- package/native/src/container.rs +158 -7
- package/native/src/dep_degree.rs +13 -14
- package/native/src/duplication.rs +188 -75
- package/native/src/functions.rs +112 -24
- package/native/src/languages.rs +5 -0
- package/native/src/measure.rs +9 -4
- package/native/src/near_miss.rs +303 -52
- package/package.json +8 -8
package/native/src/functions.rs
CHANGED
|
@@ -14,7 +14,12 @@ const BODY_REQUIRED_FUNCTION_TYPES: &[&str] = &[
|
|
|
14
14
|
"function_definition",
|
|
15
15
|
"function_declaration",
|
|
16
16
|
"method_declaration",
|
|
17
|
+
"conditional_method_declaration",
|
|
17
18
|
"constructor_declaration",
|
|
19
|
+
"destructor_declaration",
|
|
20
|
+
"operator_declaration",
|
|
21
|
+
"conversion_operator_declaration",
|
|
22
|
+
"local_function_statement",
|
|
18
23
|
"compact_constructor_declaration",
|
|
19
24
|
"function_signature_item",
|
|
20
25
|
"accessor_declaration",
|
|
@@ -140,6 +145,7 @@ fn is_void_parameter(node: Node<'_>, code: &Source<'_>) -> bool {
|
|
|
140
145
|
}
|
|
141
146
|
|
|
142
147
|
fn find_parameters_node(node: Node<'_>) -> Option<Node<'_>> {
|
|
148
|
+
let node = find_conditional_signature(node).unwrap_or(node);
|
|
143
149
|
if let Some(direct) = node.child_by_field_name("parameters") {
|
|
144
150
|
return Some(direct);
|
|
145
151
|
}
|
|
@@ -250,7 +256,7 @@ fn wrapped_transparent_value(wrapper: Node<'_>) -> Option<Node<'_>> {
|
|
|
250
256
|
/// Climbs from a value through the transparent wrappers around it (`(() => 1)`, `(() => 2) as Fn`,
|
|
251
257
|
/// `<Fn>(() => 3)`, Rust `(|x| x) as fn(i32) -> i32`) to the outermost one, whose binding site
|
|
252
258
|
/// names the value.
|
|
253
|
-
fn unwrap_transparent_value_wrappers(node: Node<'_>) -> Node<'_> {
|
|
259
|
+
pub fn unwrap_transparent_value_wrappers(node: Node<'_>) -> Node<'_> {
|
|
254
260
|
let mut bound = node;
|
|
255
261
|
while let Some(wrapper) = bound.parent_node().filter(|wrapper| {
|
|
256
262
|
wrapped_transparent_value(*wrapper).is_some_and(|value| value.id() == bound.id())
|
|
@@ -260,7 +266,94 @@ fn unwrap_transparent_value_wrappers(node: Node<'_>) -> Node<'_> {
|
|
|
260
266
|
bound
|
|
261
267
|
}
|
|
262
268
|
|
|
269
|
+
/// The declaration whose variable or field is the C++ lambda `value` held by `parent`, with the
|
|
270
|
+
/// declarator naming it.
|
|
271
|
+
fn find_cpp_lambda_declaration<'t>(
|
|
272
|
+
parent: Node<'t>,
|
|
273
|
+
value: Node<'_>,
|
|
274
|
+
code: &Source<'_>,
|
|
275
|
+
) -> Option<(Node<'t>, Option<Node<'t>>)> {
|
|
276
|
+
let declaration = match parent.kind_name() {
|
|
277
|
+
"init_declarator" => Some(parent),
|
|
278
|
+
// A member initialized in its class (`Handler pick = [] {};`) holds its value directly, and
|
|
279
|
+
// a declaration of several members each value after the member it initializes.
|
|
280
|
+
"field_declaration" => {
|
|
281
|
+
let mut declarator = None;
|
|
282
|
+
for index in 0..parent.child_count() {
|
|
283
|
+
let child = parent.child(index)?;
|
|
284
|
+
if child.id() == value.id() {
|
|
285
|
+
return Some((parent, declarator));
|
|
286
|
+
}
|
|
287
|
+
if parent.field_name_for_child(index) == Some("declarator") && !child.is_extra() {
|
|
288
|
+
declarator = Some(child);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
None
|
|
292
|
+
}
|
|
293
|
+
"argument_list" | "initializer_list" if binding_children(parent).len() == 1 => parent
|
|
294
|
+
.parent_node()
|
|
295
|
+
.filter(|holder| holder.kind_name() == "init_declarator")
|
|
296
|
+
.filter(|holder| declares_deduced_type(*holder))
|
|
297
|
+
// `auto f = {[] {}}` deduces a list holding the closure, not the closure itself;
|
|
298
|
+
// only the direct form `auto f{[] {}}` makes the variable the closure.
|
|
299
|
+
.filter(|holder| {
|
|
300
|
+
parent.kind_name() == "argument_list"
|
|
301
|
+
|| !all_children(*holder)
|
|
302
|
+
.iter()
|
|
303
|
+
.any(|child| !child.is_named() && node_text(*child, code) == "=")
|
|
304
|
+
}),
|
|
305
|
+
_ => None,
|
|
306
|
+
}?;
|
|
307
|
+
Some((declaration, declaration.child_by_field_name("declarator")))
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/// The node binding a function written as a value and the value it holds, through the forms
|
|
311
|
+
/// find_function_name names the function by: transparent wrappers, the label or annotation of a
|
|
312
|
+
/// Kotlin lambda, the `lambda`/`proc` call around a Ruby block, and the C++ initializer of a
|
|
313
|
+
/// variable or of a member declared in its class.
|
|
314
|
+
pub fn find_value_binding<'t>(node: Node<'t>, code: &Source<'_>) -> Option<(Node<'t>, Node<'t>)> {
|
|
315
|
+
let mut value = unwrap_transparent_value_wrappers(node);
|
|
316
|
+
let mut holder = value.parent_node()?;
|
|
317
|
+
if node.kind_name() == "lambda_expression" {
|
|
318
|
+
if let Some((declaration, _)) = find_cpp_lambda_declaration(holder, value, code) {
|
|
319
|
+
return Some((declaration, value));
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if matches!(node.kind_name(), "block" | "do_block") && is_ruby_lambda_call(holder, code) {
|
|
323
|
+
value = unwrap_transparent_value_wrappers(holder);
|
|
324
|
+
holder = value.parent_node()?;
|
|
325
|
+
}
|
|
326
|
+
while matches!(
|
|
327
|
+
holder.kind_name(),
|
|
328
|
+
"labeled_expression" | "annotated_expression"
|
|
329
|
+
) {
|
|
330
|
+
value = holder;
|
|
331
|
+
holder = value.parent_node()?;
|
|
332
|
+
}
|
|
333
|
+
Some((holder, value))
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/// A C# method whose header is chosen by `#if` keeps its body outside the directive and its
|
|
337
|
+
/// alternative headers inside; the first one names the method and declares its parameters.
|
|
338
|
+
fn find_conditional_signature(node: Node<'_>) -> Option<Node<'_>> {
|
|
339
|
+
if node.kind_name() != "conditional_method_declaration" {
|
|
340
|
+
return None;
|
|
341
|
+
}
|
|
342
|
+
let mut pending = named_children(node);
|
|
343
|
+
pending.reverse();
|
|
344
|
+
while let Some(current) = pending.pop() {
|
|
345
|
+
if current.kind_name() == "method_signature" {
|
|
346
|
+
return Some(current);
|
|
347
|
+
}
|
|
348
|
+
if current.kind_name().starts_with("preproc_") {
|
|
349
|
+
pending.extend(named_children(current).into_iter().rev());
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
None
|
|
353
|
+
}
|
|
354
|
+
|
|
263
355
|
pub fn find_function_name(node: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
356
|
+
let node = find_conditional_signature(node).unwrap_or(node);
|
|
264
357
|
// JS truthiness: empty strings from MISSING nodes act like "no name" at every `if (name)`.
|
|
265
358
|
if let Some(wrapped_name) =
|
|
266
359
|
find_wrapped_component_name(node, code).filter(|name| !name.is_empty())
|
|
@@ -303,24 +396,8 @@ pub fn find_function_name(node: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
|
303
396
|
// closure itself. With a written type (`std::thread worker([] {})`) the lambda is a constructor
|
|
304
397
|
// argument, and the constructor stores whatever it likes, so it names nothing.
|
|
305
398
|
if node.kind_name() == "lambda_expression" {
|
|
306
|
-
let
|
|
307
|
-
|
|
308
|
-
"argument_list" | "initializer_list" if binding_children(parent).len() == 1 => parent
|
|
309
|
-
.parent_node()
|
|
310
|
-
.filter(|holder| holder.kind_name() == "init_declarator")
|
|
311
|
-
.filter(|holder| declares_deduced_type(*holder))
|
|
312
|
-
// `auto f = {[] {}}` deduces a list holding the closure, not the closure itself;
|
|
313
|
-
// only the direct form `auto f{[] {}}` makes the variable the closure.
|
|
314
|
-
.filter(|holder| {
|
|
315
|
-
parent.kind_name() == "argument_list"
|
|
316
|
-
|| !all_children(*holder)
|
|
317
|
-
.iter()
|
|
318
|
-
.any(|child| !child.is_named() && node_text(*child, code) == "=")
|
|
319
|
-
}),
|
|
320
|
-
_ => None,
|
|
321
|
-
};
|
|
322
|
-
if let Some(declaration) = declaration {
|
|
323
|
-
return unwrap_declarator_name(declaration.child_by_field_name("declarator"), code);
|
|
399
|
+
if let Some((_, declarator)) = find_cpp_lambda_declaration(parent, bound, code) {
|
|
400
|
+
return unwrap_declarator_name(declarator, code).filter(|name| !name.is_empty());
|
|
324
401
|
}
|
|
325
402
|
}
|
|
326
403
|
|
|
@@ -425,7 +502,7 @@ pub fn find_function_name(node: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
|
425
502
|
|
|
426
503
|
/// The key of a `pair` when it is a plain, Ruby symbol, or string-literal property name; a
|
|
427
504
|
/// computed key (`[k]: ...`), an interpolated string or symbol, or an empty string names nothing.
|
|
428
|
-
fn find_pair_key_name(pair: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
505
|
+
pub fn find_pair_key_name(pair: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
429
506
|
let key = pair.child_by_field_name("key")?;
|
|
430
507
|
match key.kind_name() {
|
|
431
508
|
// A numeric key (`{ 1: () => {} }`) is as stable a property name as an identifier, signed
|
|
@@ -813,7 +890,7 @@ fn find_declared_type_spec<'t>(from: Node<'t>, name: &str, code: &Source<'t>) ->
|
|
|
813
890
|
/// delimiters and prefixes (`"""k"""`, `r"k"`, `%q(k)`, `:"k"`) never leak into it. JavaScript
|
|
814
891
|
/// splits the content into `string_fragment` and `escape_sequence` siblings; Ruby and Python emit
|
|
815
892
|
/// `string_content` (Python nests escapes inside it). Interpolation makes the key unstable.
|
|
816
|
-
fn find_string_literal_content(literal: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
893
|
+
pub fn find_string_literal_content(literal: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
817
894
|
let children = named_children(literal);
|
|
818
895
|
if children
|
|
819
896
|
.iter()
|
|
@@ -986,7 +1063,7 @@ fn is_value_of_parent(node: Node<'_>, parent: Node<'_>) -> bool {
|
|
|
986
1063
|
/// A value group of a Python or Ruby parallel assignment: the value list itself, or a tuple, list,
|
|
987
1064
|
/// or array literal that destructuring takes apart (`a, (b, c) = x, (f, y)`, `a, b = [f, g]`). A set
|
|
988
1065
|
/// or a mapping is unordered, so it groups nothing positionally.
|
|
989
|
-
fn is_value_group(node: Node<'_>) -> bool {
|
|
1066
|
+
pub fn is_value_group(node: Node<'_>) -> bool {
|
|
990
1067
|
matches!(
|
|
991
1068
|
node.kind_name(),
|
|
992
1069
|
"expression_list" | "right_assignment_list" | "tuple" | "list" | "array"
|
|
@@ -1009,6 +1086,12 @@ fn is_target_group(node: Node<'_>) -> bool {
|
|
|
1009
1086
|
/// destructuring. The position this value takes in each group it sits in is collected on the way up
|
|
1010
1087
|
/// to the assignment, then replayed on the target side.
|
|
1011
1088
|
fn find_parallel_assignment_name(value: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
1089
|
+
let (_, target) = find_parallel_assignment_target(value)?;
|
|
1090
|
+
find_assignment_target_text(target, code)
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
/// The assignment a value of a parallel assignment sits in and the target it is bound to.
|
|
1094
|
+
pub fn find_parallel_assignment_target<'t>(value: Node<'t>) -> Option<(Node<'t>, Node<'t>)> {
|
|
1012
1095
|
let mut positions = Vec::new();
|
|
1013
1096
|
let mut current = value;
|
|
1014
1097
|
let assignment = loop {
|
|
@@ -1041,7 +1124,7 @@ fn find_parallel_assignment_name(value: Node<'_>, code: &Source<'_>) -> Option<S
|
|
|
1041
1124
|
}
|
|
1042
1125
|
target = aligned_target(*values, *child, target)?;
|
|
1043
1126
|
}
|
|
1044
|
-
|
|
1127
|
+
Some((assignment, target))
|
|
1045
1128
|
}
|
|
1046
1129
|
|
|
1047
1130
|
/// The target a value takes within one group. Comments are named children of both sides but bind
|
|
@@ -1149,6 +1232,11 @@ fn find_assignment_target_text(target: Node<'_>, code: &Source<'_>) -> Option<St
|
|
|
1149
1232
|
/// value: a value that is not an array goes to the first target that is not the star, descending
|
|
1150
1233
|
/// into a nested group (`a, b = -> { 1 }` and `*a, b = -> { 1 }` both bind the lambda to a name).
|
|
1151
1234
|
fn find_ruby_assignment_name(assignment: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
1235
|
+
find_assignment_target_text(find_single_assignment_target(assignment)?, code)
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
/// The target a Ruby or Python assignment of one value binds it to.
|
|
1239
|
+
pub fn find_single_assignment_target(assignment: Node<'_>) -> Option<Node<'_>> {
|
|
1152
1240
|
let left = assignment.child_by_field_name("left")?;
|
|
1153
1241
|
let mut target = left;
|
|
1154
1242
|
if matches!(
|
|
@@ -1161,7 +1249,7 @@ fn find_ruby_assignment_name(assignment: Node<'_>, code: &Source<'_>) -> Option<
|
|
|
1161
1249
|
.find(|child| !is_splat(child))?;
|
|
1162
1250
|
}
|
|
1163
1251
|
}
|
|
1164
|
-
|
|
1252
|
+
Some(target)
|
|
1165
1253
|
}
|
|
1166
1254
|
|
|
1167
1255
|
fn is_ruby_lambda_call(node: Node<'_>, code: &Source<'_>) -> bool {
|
package/native/src/languages.rs
CHANGED
|
@@ -305,6 +305,7 @@ const JS_NCSS_NODES: &[&str] = &[
|
|
|
305
305
|
"export_statement",
|
|
306
306
|
"lexical_declaration",
|
|
307
307
|
"variable_declaration",
|
|
308
|
+
"using_declaration",
|
|
308
309
|
"function_declaration",
|
|
309
310
|
"function_signature",
|
|
310
311
|
"generator_function_declaration",
|
|
@@ -425,6 +426,8 @@ const RUST_NCSS_NODES: &[&str] = &[
|
|
|
425
426
|
"type_item",
|
|
426
427
|
"associated_type",
|
|
427
428
|
"macro_definition",
|
|
429
|
+
"decl_macro",
|
|
430
|
+
"trait_alias",
|
|
428
431
|
"field_declaration",
|
|
429
432
|
"let_declaration",
|
|
430
433
|
"expression_statement",
|
|
@@ -563,6 +566,7 @@ const CPP_NCSS_NODES: &[&str] = &[
|
|
|
563
566
|
// expression-bodied property or indexer (`int X => ...`) is its own getter (see functions.rs).
|
|
564
567
|
const CSHARP_FUNCTION_NODES: &[&str] = &[
|
|
565
568
|
"method_declaration",
|
|
569
|
+
"conditional_method_declaration",
|
|
566
570
|
"constructor_declaration",
|
|
567
571
|
"destructor_declaration",
|
|
568
572
|
"operator_declaration",
|
|
@@ -603,6 +607,7 @@ const CSHARP_NCSS_NODES: &[&str] = &[
|
|
|
603
607
|
"property_declaration",
|
|
604
608
|
"indexer_declaration",
|
|
605
609
|
"method_declaration",
|
|
610
|
+
"conditional_method_declaration",
|
|
606
611
|
"constructor_declaration",
|
|
607
612
|
"destructor_declaration",
|
|
608
613
|
"operator_declaration",
|
package/native/src/measure.rs
CHANGED
|
@@ -57,14 +57,19 @@ pub fn measure(
|
|
|
57
57
|
.by_function
|
|
58
58
|
.get(&node.id())
|
|
59
59
|
.expect("every collected function node opens a frame in the body-metrics pass");
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
let name = find_function_name(*node, code);
|
|
61
|
+
// Without a name of its own, a function has nothing an owner would qualify.
|
|
62
|
+
let container_name = name.as_ref().and_then(|_| {
|
|
63
|
+
crate::container::find_container_name(
|
|
63
64
|
*node,
|
|
64
65
|
&sets.function_nodes,
|
|
65
66
|
code,
|
|
66
67
|
&inline_namespaces,
|
|
67
|
-
)
|
|
68
|
+
)
|
|
69
|
+
});
|
|
70
|
+
FunctionMetrics {
|
|
71
|
+
name,
|
|
72
|
+
container_name,
|
|
68
73
|
node_type: node.kind_name().to_string(),
|
|
69
74
|
start_line: node.start_position().row + 1,
|
|
70
75
|
// The tree is parsed from UTF-16, so columns are UTF-16 code units x 2 — halving
|