Skip to content

Commit 14d3bbc

Browse files
authored
Implement cloc metric (#110)
* Implement cloc metric * Update tests
1 parent 5069406 commit 14d3bbc

3 files changed

Lines changed: 79 additions & 40 deletions

File tree

src/metrics.rs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,23 @@ impl<'a> Serialize for FuncSpace<'a> {
102102

103103
impl<'a> FuncSpace<'a> {
104104
fn new<T: Getter>(node: &Node<'a>, code: &'a [u8], kind: NodeKind) -> Self {
105+
let (start_position, end_position) = match kind {
106+
NodeKind::Unit => {
107+
if node.child_count() == 0 {
108+
(0, 0)
109+
} else {
110+
(node.start_position().row + 1, node.end_position().row)
111+
}
112+
}
113+
_ => (node.start_position().row + 1, node.end_position().row + 1),
114+
};
105115
Self {
106116
name: T::get_func_space_name(&node, code),
107117
spaces: Vec::new(),
108118
metrics: CodeMetrics::default(),
109119
kind,
110-
start_line: node.start_position().row + 1,
111-
end_line: node.end_position().row + 1,
120+
start_line: start_position,
121+
end_line: end_position,
112122
}
113123
}
114124

@@ -251,7 +261,8 @@ impl<'a> FuncSpace<'a> {
251261

252262
let prefix = format!("{}{}", prefix, pref_child);
253263
Self::dump_value("sloc", stats.sloc(), &prefix, false, stdout)?;
254-
Self::dump_value("lloc", stats.lloc(), &prefix, true, stdout)
264+
Self::dump_value("lloc", stats.lloc(), &prefix, false, stdout)?;
265+
Self::dump_value("cloc", stats.cloc(), &prefix, true, stdout)
255266
}
256267

257268
fn dump_nargs(
@@ -359,14 +370,13 @@ pub fn metrics<'a, T: TSParserTrait>(parser: &'a T, path: &'a PathBuf) -> Option
359370
last_level = level;
360371
}
361372

373+
let kind = T::Getter::get_kind(&node);
374+
362375
let func_space = T::Checker::is_func(&node) || T::Checker::is_func_space(&node);
376+
let unit = kind == NodeKind::Unit;
363377

364378
let new_level = if func_space {
365-
space_stack.push(FuncSpace::new::<T::Getter>(
366-
&node,
367-
code,
368-
T::Getter::get_kind(&node),
369-
));
379+
space_stack.push(FuncSpace::new::<T::Getter>(&node, code, kind));
370380
last_level = level + 1;
371381
last_level
372382
} else {
@@ -376,7 +386,7 @@ pub fn metrics<'a, T: TSParserTrait>(parser: &'a T, path: &'a PathBuf) -> Option
376386
if let Some(last) = space_stack.last_mut() {
377387
T::Cyclomatic::compute(&node, &mut last.metrics.cyclomatic);
378388
T::Halstead::compute(&node, code, &mut last.metrics.halstead);
379-
T::SourceLoc::compute(&node, code, &mut last.metrics.sloc, func_space);
389+
T::SourceLoc::compute(&node, code, &mut last.metrics.sloc, func_space, unit);
380390
T::NArgs::compute(&node, &mut last.metrics.nargs);
381391
T::Exit::compute(&node, &mut last.metrics.nexits);
382392
}

src/sloc.rs

Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::*;
1111
pub struct Stats {
1212
start: usize,
1313
end: usize,
14+
unit: bool,
1415
lines: FxHashSet<usize>,
1516
}
1617

@@ -19,16 +20,23 @@ impl Serialize for Stats {
1920
where
2021
S: Serializer,
2122
{
22-
let mut st = serializer.serialize_struct("loc", 2)?;
23+
let mut st = serializer.serialize_struct("loc", 3)?;
2324
st.serialize_field("sloc", &self.sloc())?;
2425
st.serialize_field("lloc", &self.lloc())?;
26+
st.serialize_field("cloc", &self.cloc())?;
2527
st.end()
2628
}
2729
}
2830

2931
impl fmt::Display for Stats {
3032
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
31-
write!(f, "sloc {}, lloc: {}", self.sloc(), self.lloc())
33+
write!(
34+
f,
35+
"sloc: {}, lloc: {}, cloc: {}",
36+
self.sloc(),
37+
self.lloc(),
38+
self.cloc()
39+
)
3240
}
3341
}
3442

@@ -41,40 +49,60 @@ impl Stats {
4149

4250
#[inline(always)]
4351
pub fn sloc(&self) -> f64 {
44-
(self.end - self.start) as f64 + 1.
52+
// The if construct is needed to count the line that represents
53+
// the signature of a function in a function space
54+
let sloc = if self.unit {
55+
self.end - self.start
56+
} else {
57+
((self.end - self.start) + 1)
58+
};
59+
sloc as f64
4560
}
4661

4762
#[inline(always)]
4863
pub fn lloc(&self) -> f64 {
4964
self.lines.len() as f64
5065
}
66+
67+
#[inline(always)]
68+
pub fn cloc(&self) -> f64 {
69+
self.sloc() - self.lloc()
70+
}
5171
}
5272

5373
pub trait SourceLoc
5474
where
5575
Self: Checker,
5676
{
57-
fn compute(_node: &Node, _code: &[u8], _stats: &mut Stats, _is_func_space: bool) {}
77+
fn compute(
78+
_node: &Node,
79+
_code: &[u8],
80+
_stats: &mut Stats,
81+
_is_func_space: bool,
82+
_is_unit: bool,
83+
) {
84+
}
5885
}
5986

6087
#[inline(always)]
61-
fn init(node: &Node, stats: &mut Stats, is_func_space: bool) -> usize {
88+
fn init(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) -> usize {
6289
let start = node.start_position().row;
6390

6491
if is_func_space {
65-
stats.start = start;
66-
6792
let end = node.end_position().row;
93+
94+
stats.start = start;
6895
stats.end = end;
96+
stats.unit = is_unit;
6997
}
7098
start
7199
}
72100

73101
impl SourceLoc for PythonCode {
74-
fn compute(node: &Node, _code: &[u8], stats: &mut Stats, is_func_space: bool) {
102+
fn compute(node: &Node, _code: &[u8], stats: &mut Stats, is_func_space: bool, is_unit: bool) {
75103
use Python::*;
76104

77-
let start = init(node, stats, is_func_space);
105+
let start = init(node, stats, is_func_space, is_unit);
78106

79107
match node.kind_id().into() {
80108
Comment | String | DQUOTE | DQUOTE2 | ExpressionStatement | Block => {}
@@ -86,10 +114,10 @@ impl SourceLoc for PythonCode {
86114
}
87115

88116
impl SourceLoc for MozjsCode {
89-
fn compute(node: &Node, _code: &[u8], stats: &mut Stats, is_func_space: bool) {
117+
fn compute(node: &Node, _code: &[u8], stats: &mut Stats, is_func_space: bool, is_unit: bool) {
90118
use Mozjs::*;
91119

92-
let start = init(node, stats, is_func_space);
120+
let start = init(node, stats, is_func_space, is_unit);
93121

94122
match node.kind_id().into() {
95123
Comment | String | DQUOTE | ExpressionStatement | StatementBlock => {}
@@ -101,10 +129,10 @@ impl SourceLoc for MozjsCode {
101129
}
102130

103131
impl SourceLoc for JavascriptCode {
104-
fn compute(node: &Node, _code: &[u8], stats: &mut Stats, is_func_space: bool) {
132+
fn compute(node: &Node, _code: &[u8], stats: &mut Stats, is_func_space: bool, is_unit: bool) {
105133
use Javascript::*;
106134

107-
let start = init(node, stats, is_func_space);
135+
let start = init(node, stats, is_func_space, is_unit);
108136

109137
match node.kind_id().into() {
110138
Comment | String | DQUOTE | ExpressionStatement | StatementBlock => {}
@@ -116,10 +144,10 @@ impl SourceLoc for JavascriptCode {
116144
}
117145

118146
impl SourceLoc for TypescriptCode {
119-
fn compute(node: &Node, _code: &[u8], stats: &mut Stats, is_func_space: bool) {
147+
fn compute(node: &Node, _code: &[u8], stats: &mut Stats, is_func_space: bool, is_unit: bool) {
120148
use Typescript::*;
121149

122-
let start = init(node, stats, is_func_space);
150+
let start = init(node, stats, is_func_space, is_unit);
123151

124152
match node.kind_id().into() {
125153
Comment | String | DQUOTE | ExpressionStatement | StatementBlock => {}
@@ -131,10 +159,10 @@ impl SourceLoc for TypescriptCode {
131159
}
132160

133161
impl SourceLoc for TsxCode {
134-
fn compute(node: &Node, _code: &[u8], stats: &mut Stats, is_func_space: bool) {
162+
fn compute(node: &Node, _code: &[u8], stats: &mut Stats, is_func_space: bool, is_unit: bool) {
135163
use Tsx::*;
136164

137-
let start = init(node, stats, is_func_space);
165+
let start = init(node, stats, is_func_space, is_unit);
138166

139167
match node.kind_id().into() {
140168
Comment | String | DQUOTE | ExpressionStatement | StatementBlock => {}
@@ -146,14 +174,14 @@ impl SourceLoc for TsxCode {
146174
}
147175

148176
impl SourceLoc for RustCode {
149-
fn compute(node: &Node, _code: &[u8], stats: &mut Stats, is_func_space: bool) {
177+
fn compute(node: &Node, _code: &[u8], stats: &mut Stats, is_func_space: bool, is_unit: bool) {
150178
use Rust::*;
151179

152-
let start = init(node, stats, is_func_space);
180+
let start = init(node, stats, is_func_space, is_unit);
153181

154182
match node.kind_id().into() {
155183
LineComment | BlockComment | StringLiteral | RawStringLiteral | ExpressionStatement
156-
| Block => {}
184+
| Block | SourceFile => {}
157185
_ => {
158186
stats.lines.insert(start);
159187
}
@@ -162,14 +190,15 @@ impl SourceLoc for RustCode {
162190
}
163191

164192
impl SourceLoc for CppCode {
165-
fn compute(node: &Node, _code: &[u8], stats: &mut Stats, is_func_space: bool) {
193+
fn compute(node: &Node, _code: &[u8], stats: &mut Stats, is_func_space: bool, is_unit: bool) {
166194
use Cpp::*;
167195

168-
let start = init(node, stats, is_func_space);
196+
let start = init(node, stats, is_func_space, is_unit);
169197

170198
match node.kind_id().into() {
171199
Comment | RawStringLiteral | StringLiteral | ExpressionStatement
172-
| CompoundStatement | LabeledStatement | DeclarationList | FieldDeclarationList => {}
200+
| CompoundStatement | LabeledStatement | DeclarationList | FieldDeclarationList
201+
| TranslationUnit => {}
173202
_ => {
174203
stats.lines.insert(start);
175204
}

src/web/server.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,7 @@ mod tests {
602602
"id": "1234",
603603
"spaces": {"kind": "unit",
604604
"start_line": 1,
605-
"end_line": 3,
605+
"end_line": 2,
606606
"metrics": {"cyclomatic": 1.0,
607607
"nargs": 0.,
608608
"nexits": 0.,
@@ -618,7 +618,7 @@ mod tests {
618618
"unique_operands": 1.0,
619619
"unique_operators": 2.0,
620620
"volume": 4.754_887_502_163_468},
621-
"loc": {"lloc": 2.0, "sloc": 3.0}},
621+
"loc": {"cloc": 0.0, "lloc": 2.0, "sloc": 2.0}},
622622
"name": "test.py",
623623
"spaces": [{"kind": "function",
624624
"start_line": 1,
@@ -638,7 +638,7 @@ mod tests {
638638
"unique_operands": 1.0,
639639
"unique_operators": 2.0,
640640
"volume": 4.754_887_502_163_468},
641-
"loc": {"lloc": 2.0, "sloc": 2.0}},
641+
"loc": {"cloc": 0.0, "lloc": 2.0, "sloc": 2.0}},
642642
"name": "foo",
643643
"spaces": []}]}
644644
});
@@ -667,7 +667,7 @@ mod tests {
667667
"id": "1234",
668668
"spaces": {"kind": "unit",
669669
"start_line": 1,
670-
"end_line": 3,
670+
"end_line": 2,
671671
"metrics": {"cyclomatic": 1.0,
672672
"nargs": 0.,
673673
"nexits": 0.,
@@ -683,7 +683,7 @@ mod tests {
683683
"unique_operands": 1.0,
684684
"unique_operators": 2.0,
685685
"volume": 4.754_887_502_163_468},
686-
"loc": {"lloc": 2.0, "sloc": 3.0}},
686+
"loc": {"cloc": 0.0, "lloc": 2.0, "sloc": 2.0}},
687687
"name": "test.py",
688688
"spaces": []}
689689
});
@@ -708,7 +708,7 @@ mod tests {
708708
"id": "",
709709
"spaces": {"kind": "unit",
710710
"start_line": 1,
711-
"end_line": 3,
711+
"end_line": 2,
712712
"metrics": {"cyclomatic": 1.0,
713713
"nargs": 0.,
714714
"nexits": 0.,
@@ -724,7 +724,7 @@ mod tests {
724724
"unique_operands": 1.0,
725725
"unique_operators": 2.0,
726726
"volume": 4.754_887_502_163_468},
727-
"loc": {"lloc": 2.0, "sloc": 3.0}},
727+
"loc": {"cloc": 0.0, "lloc": 2.0, "sloc": 2.0}},
728728
"name": "test.py",
729729
"spaces": [{"kind": "function",
730730
"start_line": 1,
@@ -744,7 +744,7 @@ mod tests {
744744
"unique_operands": 1.0,
745745
"unique_operators": 2.0,
746746
"volume": 4.754_887_502_163_468},
747-
"loc": {"lloc": 2.0, "sloc": 2.0}},
747+
"loc": {"cloc": 0.0, "lloc": 2.0, "sloc": 2.0}},
748748
"name": "foo",
749749
"spaces": []}]}
750750
});

0 commit comments

Comments
 (0)