Skip to content

Commit 5be9166

Browse files
committed
Add code metrics (mccabe, halstead, loc) for Python
1 parent b573307 commit 5be9166

19 files changed

Lines changed: 1303 additions & 85 deletions

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ aho-corasick = "^0.7"
1717
bytes = "^0.4"
1818
clap = "^2.33"
1919
enum-iterator = "^0.5"
20+
fxhash = "0.2"
2021
globset = "^0.4"
2122
json = "^0.12"
2223
tree-sitter = "^0.5"

src/asttools.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
use tree_sitter::Node;
2+
3+
pub fn get_parent<'a>(node: &'a Node<'a>, level: usize) -> Option<Node<'a>> {
4+
let mut level = level;
5+
let mut node = *node;
6+
while level != 0 {
7+
if let Some(parent) = node.parent() {
8+
node = parent;
9+
} else {
10+
return None;
11+
}
12+
level -= 1;
13+
}
14+
15+
Some(node)
16+
}
17+
18+
macro_rules! has_ancestors {
19+
($node:expr, $( $typs:pat )|*, $( $typ:pat ),+) => {{
20+
let mut res = false;
21+
loop {
22+
let mut node = *$node;
23+
$(
24+
if let Some(parent) = node.parent() {
25+
match parent.kind_id().into() {
26+
$typ => {
27+
node = parent;
28+
},
29+
_ => {
30+
break;
31+
}
32+
}
33+
} else {
34+
break;
35+
}
36+
)*
37+
if let Some(parent) = node.parent() {
38+
match parent.kind_id().into() {
39+
$( $typs )|+ => {
40+
res = true;
41+
},
42+
_ => {
43+
break;
44+
}
45+
}
46+
} else {
47+
break;
48+
}
49+
break;
50+
}
51+
res
52+
}};
53+
}

src/checker.rs

Lines changed: 163 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,6 @@ use tree_sitter::Node;
55
use crate::*;
66

77
pub trait Checker {
8-
// TODO: at some point use a type BaseLang = language to avoid to pass
9-
// lang as first arg of mk_checker
10-
// right now: error: enum variants on type aliases are experimental
11-
// ideally add the type in TSLanguage and then <Self as TSLanguage>::BaseLang
12-
138
fn is_comment(node: &Node) -> bool;
149

1510
#[inline(always)]
@@ -19,22 +14,28 @@ pub trait Checker {
1914

2015
fn is_string(node: &Node) -> bool;
2116
fn is_call(node: &Node) -> bool;
17+
fn is_func(node: &Node) -> bool;
18+
fn is_func_space(node: &Node) -> bool;
2219

2320
fn is_error(node: &Node) -> bool {
2421
node.is_error()
2522
}
2623
}
2724

2825
impl Checker for PreprocCode {
29-
mk_checker!(Preproc, is_comment, Comment);
30-
mk_checker!(Preproc, is_string, StringLiteral, RawStringLiteral);
31-
mk_checker!(Preproc, is_call,);
26+
mk_checker!(is_comment, Comment);
27+
mk_checker!(is_string, StringLiteral, RawStringLiteral);
28+
mk_checker!(is_call,);
29+
mk_checker!(is_func,);
30+
mk_checker!(is_func_space,);
3231
}
3332

3433
impl Checker for CcommentCode {
35-
mk_checker!(Ccomment, is_comment, Comment);
36-
mk_checker!(Ccomment, is_string, StringLiteral, RawStringLiteral);
37-
mk_checker!(Ccomment, is_call,);
34+
mk_checker!(is_comment, Comment);
35+
mk_checker!(is_string, StringLiteral, RawStringLiteral);
36+
mk_checker!(is_call,);
37+
mk_checker!(is_func,);
38+
mk_checker!(is_func_space,);
3839

3940
fn is_useful_comment(node: &Node, code: &[u8]) -> bool {
4041
lazy_static! {
@@ -46,9 +47,11 @@ impl Checker for CcommentCode {
4647
}
4748

4849
impl Checker for CCode {
49-
mk_checker!(C, is_comment, Comment);
50-
mk_checker!(C, is_string, StringLiteral, ConcatenatedString);
51-
mk_checker!(C, is_call, CallExpression);
50+
mk_checker!(is_comment, Comment);
51+
mk_checker!(is_string, StringLiteral, ConcatenatedString);
52+
mk_checker!(is_call, CallExpression);
53+
mk_checker!(is_func, FunctionDefinition);
54+
mk_checker!(is_func_space, TranslationUnit);
5255

5356
fn is_useful_comment(node: &Node, code: &[u8]) -> bool {
5457
lazy_static! {
@@ -60,15 +63,23 @@ impl Checker for CCode {
6063
}
6164

6265
impl Checker for CppCode {
63-
mk_checker!(Cpp, is_comment, Comment);
66+
mk_checker!(is_comment, Comment);
6467
mk_checker!(
65-
Cpp,
6668
is_string,
6769
StringLiteral,
6870
ConcatenatedString,
6971
RawStringLiteral
7072
);
71-
mk_checker!(Cpp, is_call, CallExpression);
73+
mk_checker!(is_call, CallExpression);
74+
mk_checker!(is_func, FunctionDefinition);
75+
mk_checker!(
76+
is_func_space,
77+
TranslationUnit,
78+
FunctionDefinition,
79+
StructSpecifier,
80+
ClassSpecifier,
81+
NamespaceDefinition
82+
);
7283

7384
fn is_useful_comment(node: &Node, code: &[u8]) -> bool {
7485
lazy_static! {
@@ -80,13 +91,30 @@ impl Checker for CppCode {
8091
}
8192

8293
impl Checker for CSharpCode {
83-
mk_checker!(CSharp, is_comment, Comment);
84-
mk_checker!(CSharp, is_string, StringLiteral);
85-
mk_checker!(CSharp, is_call,); // TODO not implemented in ts
94+
mk_checker!(is_comment, Comment);
95+
mk_checker!(is_string, StringLiteral);
96+
mk_checker!(is_call, CallExpression);
97+
mk_checker!(
98+
is_func,
99+
MethodDeclaration,
100+
ConstructorDeclaration,
101+
ConversionOperatorDeclaration,
102+
DestructorDeclaration,
103+
OperatorDeclaration,
104+
AccessorDeclaration,
105+
LocalFunctionStatement
106+
);
107+
mk_checker!(
108+
is_func_space,
109+
CompilationUnit,
110+
ClassDeclaration,
111+
StructDeclaration,
112+
NamespaceDeclaration
113+
);
86114
}
87115

88116
impl Checker for PythonCode {
89-
mk_checker!(Python, is_comment, Comment);
117+
mk_checker!(is_comment, Comment);
90118

91119
fn is_useful_comment(node: &Node, code: &[u8]) -> bool {
92120
lazy_static! {
@@ -96,60 +124,143 @@ impl Checker for PythonCode {
96124
node.start_position().row <= 1 && RE.is_match(&code[node.start_byte()..node.end_byte()])
97125
}
98126

99-
mk_checker!(Python, is_string, String, ConcatenatedString);
100-
mk_checker!(Python, is_call, Call);
127+
mk_checker!(is_string, String, ConcatenatedString);
128+
mk_checker!(is_call, Call);
129+
mk_checker!(is_func, FunctionDefinition);
130+
mk_checker!(is_func_space, Module, FunctionDefinition, ClassDefinition);
101131
}
102132

103133
impl Checker for JavaCode {
104-
mk_checker!(Java, is_comment, Comment);
105-
mk_checker!(Java, is_string, StringLiteral);
106-
mk_checker!(Java, is_call, MethodInvocation);
134+
mk_checker!(is_comment, Comment);
135+
mk_checker!(is_string, StringLiteral);
136+
mk_checker!(is_call, MethodInvocation);
137+
mk_checker!(is_func, MethodDeclaration);
138+
mk_checker!(is_func_space, Program, ClassDeclaration);
107139
}
108140

109141
impl Checker for MozjsCode {
110-
mk_checker!(Mozjs, is_comment, Comment);
111-
mk_checker!(Mozjs, is_string, String, TemplateString);
112-
mk_checker!(Mozjs, is_call, CallExpression);
142+
mk_checker!(is_comment, Comment);
143+
mk_checker!(is_string, String, TemplateString);
144+
mk_checker!(is_call, CallExpression);
145+
mk_checker!(
146+
is_func,
147+
Function,
148+
FunctionDeclaration,
149+
GeneratorFunction,
150+
GeneratorFunctionDeclaration
151+
);
152+
mk_checker!(
153+
is_func_space,
154+
Function,
155+
FunctionDeclaration,
156+
GeneratorFunction,
157+
GeneratorFunctionDeclaration,
158+
Class,
159+
ClassDeclaration
160+
);
113161
}
114162

115163
impl Checker for JavascriptCode {
116-
mk_checker!(Javascript, is_comment, Comment);
117-
mk_checker!(Javascript, is_string, String, TemplateString);
118-
mk_checker!(Javascript, is_call, CallExpression);
164+
mk_checker!(is_comment, Comment);
165+
mk_checker!(is_string, String, TemplateString);
166+
mk_checker!(is_call, CallExpression);
167+
mk_checker!(
168+
is_func,
169+
Function,
170+
FunctionDeclaration,
171+
GeneratorFunction,
172+
GeneratorFunctionDeclaration
173+
);
174+
mk_checker!(
175+
is_func_space,
176+
Program,
177+
Function,
178+
FunctionDeclaration,
179+
GeneratorFunction,
180+
GeneratorFunctionDeclaration,
181+
Class,
182+
ClassDeclaration
183+
);
119184
}
120185

121186
impl Checker for TypescriptCode {
122-
mk_checker!(Typescript, is_comment, Comment);
123-
mk_checker!(Typescript, is_string, String, TemplateString);
124-
mk_checker!(Typescript, is_call, CallExpression);
187+
mk_checker!(is_comment, Comment);
188+
mk_checker!(is_string, String, TemplateString);
189+
mk_checker!(is_call, CallExpression);
190+
mk_checker!(
191+
is_func,
192+
Function,
193+
FunctionDeclaration,
194+
GeneratorFunction,
195+
GeneratorFunctionDeclaration
196+
);
197+
mk_checker!(
198+
is_func_space,
199+
Program,
200+
Function,
201+
FunctionDeclaration,
202+
GeneratorFunction,
203+
GeneratorFunctionDeclaration,
204+
Class,
205+
ClassDeclaration
206+
);
125207
}
126208

127209
impl Checker for TsxCode {
128-
mk_checker!(Tsx, is_comment, Comment);
129-
mk_checker!(Tsx, is_string, String, TemplateString);
130-
mk_checker!(Tsx, is_call, CallExpression);
210+
mk_checker!(is_comment, Comment);
211+
mk_checker!(is_string, String, TemplateString);
212+
mk_checker!(is_call, CallExpression);
213+
mk_checker!(
214+
is_func,
215+
Function,
216+
FunctionDeclaration,
217+
GeneratorFunction,
218+
GeneratorFunctionDeclaration
219+
);
220+
mk_checker!(
221+
is_func_space,
222+
Program,
223+
Function,
224+
FunctionDeclaration,
225+
GeneratorFunction,
226+
GeneratorFunctionDeclaration,
227+
Class,
228+
ClassDeclaration
229+
);
131230
}
132231

133232
impl Checker for GoCode {
134-
mk_checker!(Go, is_comment, Comment);
135-
mk_checker!(Go, is_string, StringLiteral);
136-
mk_checker!(Go, is_call, CallExpression);
233+
mk_checker!(is_comment, Comment);
234+
mk_checker!(is_string, StringLiteral);
235+
mk_checker!(is_call, CallExpression);
236+
mk_checker!(is_func, FunctionDeclaration, MethodDeclaration, FuncLiteral);
237+
mk_checker!(
238+
is_func_space,
239+
SourceFile,
240+
FunctionDeclaration,
241+
MethodDeclaration,
242+
FuncLiteral
243+
);
137244
}
138245

139246
impl Checker for CssCode {
140-
mk_checker!(Css, is_comment, Comment);
141-
mk_checker!(Css, is_string, StringValue);
142-
mk_checker!(Css, is_call, CallExpression);
247+
mk_checker!(is_comment, Comment);
248+
mk_checker!(is_string, StringValue);
249+
mk_checker!(is_call, CallExpression);
250+
mk_checker!(is_func,);
251+
mk_checker!(is_func_space,);
143252
}
144253

145254
impl Checker for HtmlCode {
146-
mk_checker!(Html, is_comment, Comment);
147-
mk_checker!(Html, is_string,);
148-
mk_checker!(Html, is_call,);
255+
mk_checker!(is_comment, Comment);
256+
mk_checker!(is_string,);
257+
mk_checker!(is_call,);
258+
mk_checker!(is_func,);
259+
mk_checker!(is_func_space,);
149260
}
150261

151262
impl Checker for RustCode {
152-
mk_checker!(Rust, is_comment, LineComment, BlockComment);
263+
mk_checker!(is_comment, LineComment, BlockComment);
153264

154265
fn is_useful_comment(node: &Node, code: &[u8]) -> bool {
155266
if let Some(parent) = node.parent() {
@@ -162,6 +273,8 @@ impl Checker for RustCode {
162273
code.starts_with(b"/// cbindgen:")
163274
}
164275

165-
mk_checker!(Rust, is_string, StringLiteral, RawStringLiteral);
166-
mk_checker!(Rust, is_call, CallExpression);
276+
mk_checker!(is_string, StringLiteral, RawStringLiteral);
277+
mk_checker!(is_call, CallExpression);
278+
mk_checker!(is_func, FunctionItem);
279+
mk_checker!(is_func_space, SourceFile, FunctionItem, ImplItem);
167280
}

0 commit comments

Comments
 (0)