code-gauge 10.4.0 → 10.4.1
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/native/code-gauge.wasm +0 -0
- package/native/src/complexity.rs +36 -35
- package/native/src/container.rs +7 -8
- package/native/src/dep_degree.rs +190 -97
- package/native/src/functions.rs +5 -3
- package/native/src/languages.rs +6 -1
- package/native/src/measure.rs +157 -83
- package/native/src/ncss.rs +16 -24
- package/native/src/tree_index.rs +145 -44
- package/native/src/util.rs +36 -5
- package/package.json +8 -8
package/native/src/measure.rs
CHANGED
|
@@ -3,7 +3,7 @@ use std::sync::OnceLock;
|
|
|
3
3
|
use tree_sitter::Node;
|
|
4
4
|
|
|
5
5
|
use crate::complexity::{is_lambda_body_block, measure_function_body_metrics, LanguageSets};
|
|
6
|
-
use crate::dep_degree::
|
|
6
|
+
use crate::dep_degree::measure_dep_degrees;
|
|
7
7
|
use crate::duplication::{
|
|
8
8
|
collect_cross_file_file_data, hash_text, measure_duplication, tokenize, DuplicationSettings,
|
|
9
9
|
TokenizedSource,
|
|
@@ -12,7 +12,7 @@ use crate::functions::{
|
|
|
12
12
|
collect_nodes, count_parameters, find_function_name, is_implemented_function,
|
|
13
13
|
};
|
|
14
14
|
use crate::languages::LanguageDefinition;
|
|
15
|
-
use crate::tree_index::{NodeExt, TreeIndex};
|
|
15
|
+
use crate::tree_index::{KindSet, NodeExt, TreeIndex};
|
|
16
16
|
use crate::types::{
|
|
17
17
|
CrossFileFileData, FunctionMetrics, HalsteadCounts, LineMetrics, NativeMetrics,
|
|
18
18
|
};
|
|
@@ -20,6 +20,7 @@ use crate::util::{
|
|
|
20
20
|
all_children, is_js_whitespace, is_token, named_children, node_text, split_lines, Source,
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
// code-gauge-allow functionNcss 32 -- assembles the file's metrics from every pass in one place
|
|
23
24
|
pub fn measure(
|
|
24
25
|
code: &str,
|
|
25
26
|
language: &LanguageDefinition,
|
|
@@ -32,23 +33,26 @@ pub fn measure(
|
|
|
32
33
|
let _index = TreeIndex::new(&tree, language)?;
|
|
33
34
|
let root = tree.root_node();
|
|
34
35
|
let code = &source;
|
|
35
|
-
let sets = LanguageSets::
|
|
36
|
+
let sets = LanguageSets::of(language);
|
|
36
37
|
|
|
37
38
|
// One walk collects both function and initializer-block candidates (Ruby's `block` is both).
|
|
38
|
-
let candidates = collect_nodes(root, |
|
|
39
|
-
sets.function_nodes.contains(
|
|
39
|
+
let candidates = collect_nodes(root, |node| {
|
|
40
|
+
sets.function_nodes.contains(node) || INITIALIZER_KINDS.contains(node)
|
|
40
41
|
});
|
|
41
42
|
let initializer_block_count = count_initializer_blocks(&candidates);
|
|
42
43
|
let functions: Vec<Node<'_>> = candidates
|
|
43
44
|
.into_iter()
|
|
44
45
|
.filter(|node| {
|
|
45
|
-
sets.function_nodes.contains(node
|
|
46
|
+
sets.function_nodes.contains(*node)
|
|
46
47
|
&& !is_lambda_body_block(*node)
|
|
47
48
|
&& is_implemented_function(*node)
|
|
48
49
|
})
|
|
49
50
|
.collect();
|
|
50
51
|
|
|
51
|
-
let body_metrics = measure_function_body_metrics(root,
|
|
52
|
+
let body_metrics = measure_function_body_metrics(root, sets, code);
|
|
53
|
+
let (halstead_counts, mut halstead_counts_by_function) =
|
|
54
|
+
measure_halstead(root, &functions, code);
|
|
55
|
+
let dep_degrees = measure_dep_degrees(root, &functions, code, &sets.function_nodes);
|
|
52
56
|
let inline_namespaces = crate::container::find_inline_namespaces(root, code);
|
|
53
57
|
let function_metrics: Vec<FunctionMetrics> = functions
|
|
54
58
|
.iter()
|
|
@@ -86,14 +90,15 @@ pub fn measure(
|
|
|
86
90
|
nesting_depth: body_metrics.nesting_depth,
|
|
87
91
|
ncss: body_metrics.ncss,
|
|
88
92
|
parameter_count: count_parameters(*node, code),
|
|
89
|
-
halstead_counts:
|
|
90
|
-
|
|
93
|
+
halstead_counts: halstead_counts_by_function
|
|
94
|
+
.remove(&node.id())
|
|
95
|
+
.expect("every function's Halstead counts are measured"),
|
|
96
|
+
dep_degree: dep_degrees[&node.id()],
|
|
91
97
|
}
|
|
92
98
|
})
|
|
93
99
|
.collect();
|
|
94
100
|
|
|
95
101
|
let (lines, code_line_numbers) = classify_lines(code, root);
|
|
96
|
-
let halstead_counts = measure_halstead(root, code);
|
|
97
102
|
let tokenized = tokenize(root, code);
|
|
98
103
|
|
|
99
104
|
Ok(NativeMetrics {
|
|
@@ -144,16 +149,16 @@ pub fn measure(
|
|
|
144
149
|
/// Initializer blocks run code of their own, so each is a component like a function: Java static
|
|
145
150
|
/// and instance initializers, Kotlin `init` blocks, and JavaScript/TypeScript class `static`
|
|
146
151
|
/// blocks. Their decisions already count as decisions outside functions.
|
|
147
|
-
|
|
152
|
+
static INITIALIZER_KINDS: KindSet = KindSet::new(&[
|
|
148
153
|
"static_initializer",
|
|
149
154
|
"anonymous_initializer",
|
|
150
155
|
"class_static_block",
|
|
151
156
|
"block",
|
|
152
|
-
];
|
|
157
|
+
]);
|
|
153
158
|
|
|
154
159
|
/// A block of a class that runs when the class or an instance is initialized.
|
|
155
160
|
pub fn is_initializer_block(node: Node<'_>) -> bool {
|
|
156
|
-
|
|
161
|
+
INITIALIZER_KINDS.contains(node)
|
|
157
162
|
// A bare block is an initializer only as a direct member of a Java class or enum body.
|
|
158
163
|
&& (node.kind_name() != "block"
|
|
159
164
|
|| node.parent_node().is_some_and(|parent| {
|
|
@@ -270,9 +275,9 @@ pub fn collect_function_token_sequences(
|
|
|
270
275
|
let tree = parse_source(&source, language)?;
|
|
271
276
|
let _index = TreeIndex::new(&tree, language)?;
|
|
272
277
|
let root = tree.root_node();
|
|
273
|
-
let sets = LanguageSets::
|
|
278
|
+
let sets = LanguageSets::of(language);
|
|
274
279
|
Ok(
|
|
275
|
-
collect_nodes(root, |
|
|
280
|
+
collect_nodes(root, |node| sets.function_nodes.contains(node))
|
|
276
281
|
.into_iter()
|
|
277
282
|
.filter(|node| !is_lambda_body_block(*node) && is_implemented_function(*node))
|
|
278
283
|
.map(|node| {
|
|
@@ -321,13 +326,11 @@ fn collect_token_symbols(
|
|
|
321
326
|
}
|
|
322
327
|
// Remaining operand leaves are literals, normalized by kind; everything else (keywords,
|
|
323
328
|
// operators, punctuation) is kept verbatim.
|
|
324
|
-
symbols.push(hash_text(
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
},
|
|
330
|
-
));
|
|
329
|
+
symbols.push(hash_text(if OPERAND_KINDS.contains(node) {
|
|
330
|
+
node.kind_name()
|
|
331
|
+
} else {
|
|
332
|
+
node_text(node, code)
|
|
333
|
+
}));
|
|
331
334
|
}
|
|
332
335
|
|
|
333
336
|
/// Parses the source from UTF-16, matching node-tree-sitter's JavaScript string semantics:
|
|
@@ -566,7 +569,7 @@ const OPERATOR_TEXTS: &[&str] = &[
|
|
|
566
569
|
"continue",
|
|
567
570
|
];
|
|
568
571
|
|
|
569
|
-
|
|
572
|
+
static OPERAND_KINDS: KindSet = KindSet::new(&[
|
|
570
573
|
"identifier",
|
|
571
574
|
"implicit_parameter",
|
|
572
575
|
"property_identifier",
|
|
@@ -624,11 +627,11 @@ const OPERAND_NODE_TYPES: &[&str] = &[
|
|
|
624
627
|
"undefined",
|
|
625
628
|
"nil",
|
|
626
629
|
"none",
|
|
627
|
-
];
|
|
630
|
+
]);
|
|
628
631
|
|
|
629
632
|
/// Non-leaf literals counted as one Halstead operand without descending.
|
|
630
633
|
/// `character_literal` is a leaf in Java but wraps its content in C# and Kotlin.
|
|
631
|
-
|
|
634
|
+
static ATOMIC_OPERAND_KINDS: KindSet = KindSet::new(&[
|
|
632
635
|
"interpreted_string_literal",
|
|
633
636
|
"character_literal",
|
|
634
637
|
"regex",
|
|
@@ -637,89 +640,160 @@ const ATOMIC_OPERAND_NODE_TYPES: &[&str] = &[
|
|
|
637
640
|
"floating_point_type",
|
|
638
641
|
"sized_type_specifier",
|
|
639
642
|
"placeholder_type_specifier",
|
|
640
|
-
];
|
|
643
|
+
]);
|
|
641
644
|
|
|
642
645
|
fn operator_texts() -> &'static FxHashSet<&'static str> {
|
|
643
646
|
static SET: OnceLock<FxHashSet<&'static str>> = OnceLock::new();
|
|
644
647
|
SET.get_or_init(|| OPERATOR_TEXTS.iter().copied().collect())
|
|
645
648
|
}
|
|
646
649
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
SET.get_or_init(|| OPERAND_NODE_TYPES.iter().copied().collect())
|
|
650
|
-
}
|
|
650
|
+
/// Node kinds spelled like an operator.
|
|
651
|
+
static OPERATOR_KINDS: KindSet = KindSet::new(OPERATOR_TEXTS);
|
|
651
652
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
653
|
+
/// Halstead operator and operand occurrences by text.
|
|
654
|
+
#[derive(Default)]
|
|
655
|
+
struct HalsteadTally<'a> {
|
|
656
|
+
operators: FxHashMap<&'a str, u64>,
|
|
657
|
+
operands: FxHashMap<&'a str, u64>,
|
|
655
658
|
}
|
|
656
659
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
operators: &mut FxHashMap<&'a str, u64>,
|
|
665
|
-
operands: &mut FxHashMap<&'a str, u64>,
|
|
666
|
-
) {
|
|
667
|
-
if matches!(
|
|
668
|
-
node.kind_name(),
|
|
669
|
-
"comment" | "line_comment" | "block_comment"
|
|
670
|
-
) {
|
|
671
|
-
return;
|
|
660
|
+
impl<'a> HalsteadTally<'a> {
|
|
661
|
+
fn counts(&self) -> HalsteadCounts {
|
|
662
|
+
HalsteadCounts {
|
|
663
|
+
distinct_operators: self.operators.len(),
|
|
664
|
+
distinct_operands: self.operands.len(),
|
|
665
|
+
total_operators: self.operators.values().sum(),
|
|
666
|
+
total_operands: self.operands.values().sum(),
|
|
672
667
|
}
|
|
668
|
+
}
|
|
673
669
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
670
|
+
fn absorb(&mut self, other: HalsteadTally<'a>) {
|
|
671
|
+
for (text, count) in other.operators {
|
|
672
|
+
*self.operators.entry(text).or_insert(0) += count;
|
|
673
|
+
}
|
|
674
|
+
for (text, count) in other.operands {
|
|
675
|
+
*self.operands.entry(text).or_insert(0) += count;
|
|
677
676
|
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
678
679
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
680
|
+
/// Halstead counts of the subtree of `root` and, by node id, of each function in `functions`.
|
|
681
|
+
fn measure_halstead<'a>(
|
|
682
|
+
root: Node<'a>,
|
|
683
|
+
functions: &[Node<'a>],
|
|
684
|
+
code: &Source<'a>,
|
|
685
|
+
) -> (HalsteadCounts, FxHashMap<usize, HalsteadCounts>) {
|
|
686
|
+
let (counts, mut by_function) =
|
|
687
|
+
count_halstead(root, &functions.iter().map(Node::id).collect(), code);
|
|
688
|
+
for function in functions {
|
|
689
|
+
// The walk counts atomic operands and tokens without descending into them and skips
|
|
690
|
+
// comments, any of which can hold a function (a C++ lambda in a constrained `auto` type).
|
|
691
|
+
by_function
|
|
692
|
+
.entry(function.id())
|
|
693
|
+
.or_insert_with(|| count_halstead(*function, &FxHashSet::default(), code).0);
|
|
694
|
+
}
|
|
695
|
+
(counts, by_function)
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/// Halstead counts of the subtree of `root` and, by node id, of each function in `function_ids`
|
|
699
|
+
/// whose subtree the walk enters: a function's tokens are counted the same wherever the walk
|
|
700
|
+
/// starts, so its tally is kept apart while its subtree is walked and then joins the enclosing one.
|
|
701
|
+
fn count_halstead<'a>(
|
|
702
|
+
root: Node<'a>,
|
|
703
|
+
function_ids: &FxHashSet<usize>,
|
|
704
|
+
code: &Source<'a>,
|
|
705
|
+
) -> (HalsteadCounts, FxHashMap<usize, HalsteadCounts>) {
|
|
706
|
+
struct Walk<'a, 'w> {
|
|
707
|
+
code: &'w Source<'a>,
|
|
708
|
+
function_ids: &'w FxHashSet<usize>,
|
|
709
|
+
/// The tally of the walked subtree, then that of each function being walked.
|
|
710
|
+
tallies: Vec<HalsteadTally<'a>>,
|
|
711
|
+
by_function: FxHashMap<usize, HalsteadCounts>,
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
impl<'a> Walk<'a, '_> {
|
|
715
|
+
fn visit(&mut self, node: Node<'a>) {
|
|
716
|
+
let tally = self.tallies.last_mut().expect("the walked subtree's tally");
|
|
717
|
+
if !count_halstead_token(node, self.code, tally) {
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
let is_function = self.function_ids.contains(&node.id());
|
|
721
|
+
if is_function {
|
|
722
|
+
self.tallies.push(HalsteadTally::default());
|
|
723
|
+
}
|
|
724
|
+
for child in all_children(node) {
|
|
725
|
+
self.visit(child);
|
|
726
|
+
}
|
|
727
|
+
if is_function {
|
|
728
|
+
let tally = self.tallies.pop().expect("pushed above");
|
|
729
|
+
self.by_function.insert(node.id(), tally.counts());
|
|
730
|
+
self.tallies
|
|
731
|
+
.last_mut()
|
|
732
|
+
.expect("the walked subtree's tally")
|
|
733
|
+
.absorb(tally);
|
|
699
734
|
}
|
|
700
|
-
return;
|
|
701
735
|
}
|
|
736
|
+
}
|
|
702
737
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
738
|
+
let mut walk = Walk {
|
|
739
|
+
code,
|
|
740
|
+
function_ids,
|
|
741
|
+
tallies: vec![HalsteadTally::default()],
|
|
742
|
+
by_function: FxHashMap::default(),
|
|
743
|
+
};
|
|
744
|
+
walk.visit(root);
|
|
745
|
+
(walk.tallies[0].counts(), walk.by_function)
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/// Counts the node into `tally` if it is a token or an atomic operand, returning whether its
|
|
749
|
+
/// children are to be counted instead.
|
|
750
|
+
fn count_halstead_token<'a>(
|
|
751
|
+
node: Node<'a>,
|
|
752
|
+
code: &Source<'a>,
|
|
753
|
+
tally: &mut HalsteadTally<'a>,
|
|
754
|
+
) -> bool {
|
|
755
|
+
if matches!(
|
|
756
|
+
node.kind_name(),
|
|
757
|
+
"comment" | "line_comment" | "block_comment"
|
|
758
|
+
) {
|
|
759
|
+
return false;
|
|
706
760
|
}
|
|
707
761
|
|
|
708
|
-
|
|
762
|
+
if is_atomic_operand(node) {
|
|
763
|
+
*tally.operands.entry(node_text(node, code)).or_insert(0) += 1;
|
|
764
|
+
return false;
|
|
765
|
+
}
|
|
709
766
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
767
|
+
// Operators are counted from leaf tokens only: keyword-named nodes always contain a
|
|
768
|
+
// same-text anonymous keyword leaf, so counting the named node as well would double-count.
|
|
769
|
+
if !is_token(node) {
|
|
770
|
+
return true;
|
|
771
|
+
}
|
|
772
|
+
let text = node_text(node, code);
|
|
773
|
+
// Operands win over text matches so identifiers spelled like word operators stay operands;
|
|
774
|
+
// C# `nameof(x)` is the one keyword operator the grammar parses as a plain callee.
|
|
775
|
+
if is_csharp_nameof_callee(node, text) {
|
|
776
|
+
*tally.operators.entry(text).or_insert(0) += 1;
|
|
777
|
+
} else if OPERAND_KINDS.contains(node) {
|
|
778
|
+
*tally.operands.entry(text).or_insert(0) += 1;
|
|
779
|
+
} else if (operator_texts().contains(text) || OPERATOR_KINDS.contains(node))
|
|
780
|
+
&& is_countable_contextual_token(node, text)
|
|
781
|
+
{
|
|
782
|
+
let key = if text.is_empty() {
|
|
783
|
+
node.kind_name()
|
|
784
|
+
} else {
|
|
785
|
+
text
|
|
786
|
+
};
|
|
787
|
+
*tally.operators.entry(key).or_insert(0) += 1;
|
|
715
788
|
}
|
|
789
|
+
false
|
|
716
790
|
}
|
|
717
791
|
|
|
718
|
-
/// A literal counted as one operand without descending:
|
|
792
|
+
/// A literal counted as one operand without descending: ATOMIC_OPERAND_KINDS, or a Go raw
|
|
719
793
|
/// string, which wraps its content like Go's interpreted strings (the Rust and C++
|
|
720
794
|
/// `raw_string_literal`s count their content leaves instead).
|
|
721
795
|
fn is_atomic_operand(node: Node<'_>) -> bool {
|
|
722
|
-
|
|
796
|
+
ATOMIC_OPERAND_KINDS.contains(node)
|
|
723
797
|
|| (node.kind_name() == "raw_string_literal" && crate::tree_index::language_name() == "go")
|
|
724
798
|
}
|
|
725
799
|
|
package/native/src/ncss.rs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
use rustc_hash::FxHashSet;
|
|
2
1
|
use tree_sitter::Node;
|
|
3
2
|
|
|
4
|
-
use crate::tree_index::NodeExt;
|
|
3
|
+
use crate::tree_index::{KindSet, NodeExt};
|
|
5
4
|
use crate::util::find_children_by_field_name;
|
|
6
5
|
|
|
7
6
|
pub const COMMENT_NODE_TYPES: &[&str] = &["comment", "line_comment", "block_comment"];
|
|
@@ -12,7 +11,7 @@ pub const COMMENT_NODE_TYPES: &[&str] = &["comment", "line_comment", "block_comm
|
|
|
12
11
|
/// constants are not counted either), and Kotlin lambda parameters (siblings of the lambda's
|
|
13
12
|
/// statements). Kotlin's `try_expression` is excluded contextually below because Rust
|
|
14
13
|
/// shares its name: Rust's `?` operator must keep counting as a tail expression.
|
|
15
|
-
|
|
14
|
+
static POSITIONAL_EXCLUSION_KINDS: KindSet = KindSet::new(&[
|
|
16
15
|
"attribute_item",
|
|
17
16
|
"inner_attribute_item",
|
|
18
17
|
"empty_statement",
|
|
@@ -24,7 +23,7 @@ const POSITIONAL_EXCLUSION_TYPES: &[&str] = &[
|
|
|
24
23
|
"shebang",
|
|
25
24
|
"enum_entry",
|
|
26
25
|
"lambda_parameters",
|
|
27
|
-
];
|
|
26
|
+
]);
|
|
28
27
|
|
|
29
28
|
/// TypeScript interface members count like Java interface members, but the same node types appear
|
|
30
29
|
/// inside object-type annotations (`let x: { a: number }`), which are part of one declaration, so
|
|
@@ -59,9 +58,9 @@ const BODYLESS_NCSS_SPECIFIER_TYPES: &[&str] = &[
|
|
|
59
58
|
pub fn ncss_contribution(
|
|
60
59
|
node: Node<'_>,
|
|
61
60
|
parent: Option<Node<'_>>,
|
|
62
|
-
countable: &
|
|
63
|
-
containers: &
|
|
64
|
-
bare_body_parents: &
|
|
61
|
+
countable: &KindSet,
|
|
62
|
+
containers: &KindSet,
|
|
63
|
+
bare_body_parents: &KindSet,
|
|
65
64
|
) -> u64 {
|
|
66
65
|
if !node.is_named()
|
|
67
66
|
|| COMMENT_NODE_TYPES.contains(&node.kind_name())
|
|
@@ -82,8 +81,8 @@ pub fn ncss_contribution(
|
|
|
82
81
|
// it stands like any other expression.
|
|
83
82
|
let positional = (is_in_container_position(parent, containers)
|
|
84
83
|
|| is_bare_body(node, parent, bare_body_parents))
|
|
85
|
-
&& (!containers.contains(node
|
|
86
|
-
&& !
|
|
84
|
+
&& (!containers.contains(node) || node.kind_name() == "lambda_literal")
|
|
85
|
+
&& !POSITIONAL_EXCLUSION_KINDS.contains(node)
|
|
87
86
|
&& !crate::util::is_kotlin_try_expression(node);
|
|
88
87
|
if (counts_through_node_type(node, countable)
|
|
89
88
|
|| positional
|
|
@@ -109,8 +108,8 @@ pub fn ncss_contribution(
|
|
|
109
108
|
contribution
|
|
110
109
|
}
|
|
111
110
|
|
|
112
|
-
fn counts_through_node_type(node: Node<'_>, countable: &
|
|
113
|
-
if !countable.contains(node
|
|
111
|
+
fn counts_through_node_type(node: Node<'_>, countable: &KindSet) -> bool {
|
|
112
|
+
if !countable.contains(node) {
|
|
114
113
|
return false;
|
|
115
114
|
}
|
|
116
115
|
if BODYLESS_NCSS_SPECIFIER_TYPES.contains(&node.kind_name()) {
|
|
@@ -130,14 +129,11 @@ const TRANSPARENT_WRAPPER_TYPES: &[&str] = &["parenthesized_statements", "attrib
|
|
|
130
129
|
|
|
131
130
|
/// Direct container children count positionally, as do the children of TRANSPARENT_WRAPPER_TYPES
|
|
132
131
|
/// in that position.
|
|
133
|
-
fn is_in_container_position(
|
|
134
|
-
parent: Option<Node<'_>>,
|
|
135
|
-
containers: &FxHashSet<&'static str>,
|
|
136
|
-
) -> bool {
|
|
132
|
+
fn is_in_container_position(parent: Option<Node<'_>>, containers: &KindSet) -> bool {
|
|
137
133
|
let mut ancestor = parent;
|
|
138
134
|
while let Some(current) = ancestor {
|
|
139
135
|
if !TRANSPARENT_WRAPPER_TYPES.contains(¤t.kind_name()) {
|
|
140
|
-
return containers.contains(current
|
|
136
|
+
return containers.contains(current);
|
|
141
137
|
}
|
|
142
138
|
ancestor = current.parent_node();
|
|
143
139
|
}
|
|
@@ -146,12 +142,8 @@ fn is_in_container_position(
|
|
|
146
142
|
|
|
147
143
|
/// A braceless branch or loop body (`if (x) foo()`) stands for the single statement a braced body
|
|
148
144
|
/// would hold.
|
|
149
|
-
fn is_bare_body(
|
|
150
|
-
|
|
151
|
-
parent: Option<Node<'_>>,
|
|
152
|
-
bare_body_parents: &FxHashSet<&'static str>,
|
|
153
|
-
) -> bool {
|
|
154
|
-
parent.is_some_and(|parent| bare_body_parents.contains(parent.kind_name()))
|
|
145
|
+
fn is_bare_body(node: Node<'_>, parent: Option<Node<'_>>, bare_body_parents: &KindSet) -> bool {
|
|
146
|
+
parent.is_some_and(|parent| bare_body_parents.contains(parent))
|
|
155
147
|
&& ["consequence", "alternative", "body"]
|
|
156
148
|
.iter()
|
|
157
149
|
.any(|field_name| is_field_of_parent(node, parent, field_name))
|
|
@@ -159,13 +151,13 @@ fn is_bare_body(
|
|
|
159
151
|
|
|
160
152
|
/// `export const x = 1` nests a countable declaration inside `export_statement`; only the inner
|
|
161
153
|
/// declaration counts, mirroring how PMD counts one statement per declared entity.
|
|
162
|
-
fn is_declaration_wrapper(node: Node<'_>, countable: &
|
|
154
|
+
fn is_declaration_wrapper(node: Node<'_>, countable: &KindSet) -> bool {
|
|
163
155
|
if node.kind_name() != "export_statement" {
|
|
164
156
|
return false;
|
|
165
157
|
}
|
|
166
158
|
node.child_by_field_name("declaration")
|
|
167
159
|
.is_some_and(|declaration| {
|
|
168
|
-
countable.contains(declaration
|
|
160
|
+
countable.contains(declaration)
|
|
169
161
|
|| declaration.kind_name() == "internal_module"
|
|
170
162
|
|| declaration.kind_name() == "ambient_declaration"
|
|
171
163
|
})
|