Skip to content

Commit f5458f1

Browse files
committed
Fix Javascript Nom
1 parent 8ace6c2 commit f5458f1

File tree

1 file changed

+259
-50
lines changed

1 file changed

+259
-50
lines changed

src/metrics/nom.rs

Lines changed: 259 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,96 @@ impl Stats {
6868
}
6969
}
7070

71+
#[inline(always)]
72+
fn is_child_identifier(node: &Node, id: u16) -> bool {
73+
let mut cursor = node.object().walk();
74+
for child in node.object().children(&mut cursor) {
75+
if child.kind_id() == id {
76+
return true;
77+
}
78+
}
79+
false
80+
}
81+
82+
macro_rules! function {
83+
($node: ident, $stats: ident) => {
84+
// Consider named functions as functions and unnamed ones as
85+
// closures.
86+
if count_specific_ancestors!(
87+
$node,
88+
VariableDeclarator | AssignmentExpression | LabeledStatement | Pair,
89+
StatementBlock | ReturnStatement | NewExpression | Arguments
90+
) > 0
91+
|| is_child_identifier($node, Identifier as u16)
92+
{
93+
$stats.functions += 1;
94+
} else {
95+
$stats.closures += 1;
96+
}
97+
};
98+
}
99+
100+
macro_rules! arrow_function {
101+
($node: ident, $stats: ident) => {
102+
// Consider named functions as functions and unnamed ones as
103+
// closures.
104+
if count_specific_ancestors!(
105+
$node,
106+
VariableDeclarator | AssignmentExpression | LabeledStatement,
107+
StatementBlock | ReturnStatement | NewExpression | CallExpression
108+
) > 0
109+
{
110+
$stats.functions += 1;
111+
} else {
112+
$stats.closures += 1;
113+
}
114+
};
115+
}
116+
117+
macro_rules! js_grammar {
118+
($grammar: ident, $node: ident, $stats: ident) => {
119+
use $grammar::*;
120+
121+
match $node.object().kind_id().into() {
122+
FunctionDeclaration | MethodDefinition => {
123+
$stats.functions += 1;
124+
}
125+
GeneratorFunction | GeneratorFunctionDeclaration => {
126+
$stats.closures += 1;
127+
}
128+
Function => {
129+
function!($node, $stats);
130+
}
131+
ArrowFunction => {
132+
arrow_function!($node, $stats);
133+
}
134+
_ => {}
135+
}
136+
};
137+
}
138+
139+
macro_rules! typescript_grammar {
140+
($grammar: ident, $node: ident, $stats: ident) => {
141+
use $grammar::*;
142+
143+
match $node.object().kind_id().into() {
144+
FunctionDeclaration | MethodDefinition => {
145+
$stats.functions += 1;
146+
}
147+
GeneratorFunction | GeneratorFunctionDeclaration => {
148+
$stats.closures += 1;
149+
}
150+
Function => {
151+
function!($node, $stats);
152+
}
153+
ArrowFunction => {
154+
arrow_function!($node, $stats);
155+
}
156+
_ => {}
157+
}
158+
};
159+
}
160+
71161
#[doc(hidden)]
72162
pub trait Nom
73163
where
@@ -94,65 +184,25 @@ impl Nom for PythonCode {
94184

95185
impl Nom for MozjsCode {
96186
fn compute(node: &Node, stats: &mut Stats) {
97-
use Mozjs::*;
98-
99-
match node.object().kind_id().into() {
100-
Function | FunctionDeclaration | MethodDefinition => {
101-
stats.functions += 1;
102-
}
103-
GeneratorFunction | GeneratorFunctionDeclaration | ArrowFunction => {
104-
stats.closures += 1;
105-
}
106-
_ => {}
107-
}
187+
js_grammar!(Mozjs, node, stats);
108188
}
109189
}
110190

111191
impl Nom for JavascriptCode {
112192
fn compute(node: &Node, stats: &mut Stats) {
113-
use Javascript::*;
114-
115-
match node.object().kind_id().into() {
116-
Function | FunctionDeclaration | MethodDefinition => {
117-
stats.functions += 1;
118-
}
119-
GeneratorFunction | GeneratorFunctionDeclaration | ArrowFunction => {
120-
stats.closures += 1;
121-
}
122-
_ => {}
123-
}
193+
js_grammar!(Javascript, node, stats);
124194
}
125195
}
126196

127197
impl Nom for TypescriptCode {
128198
fn compute(node: &Node, stats: &mut Stats) {
129-
use Typescript::*;
130-
131-
match node.object().kind_id().into() {
132-
Function | FunctionDeclaration | MethodDefinition => {
133-
stats.functions += 1;
134-
}
135-
GeneratorFunction | GeneratorFunctionDeclaration | ArrowFunction => {
136-
stats.closures += 1;
137-
}
138-
_ => {}
139-
}
199+
typescript_grammar!(Typescript, node, stats);
140200
}
141201
}
142202

143203
impl Nom for TsxCode {
144204
fn compute(node: &Node, stats: &mut Stats) {
145-
use Tsx::*;
146-
147-
match node.object().kind_id().into() {
148-
Function | FunctionDeclaration | MethodDefinition => {
149-
stats.functions += 1;
150-
}
151-
GeneratorFunction | GeneratorFunctionDeclaration | ArrowFunction => {
152-
stats.closures += 1;
153-
}
154-
_ => {}
155-
}
205+
typescript_grammar!(Tsx, node, stats);
156206
}
157207
}
158208

@@ -279,15 +329,72 @@ mod tests {
279329
}
280330

281331
#[test]
282-
fn c_nom() {
332+
fn javascript_nom() {
283333
check_metrics!(
284-
"int foo();
334+
"function f(a, b) {
335+
function foo(a) {
336+
return a;
337+
}
338+
var bar = (function () {
339+
var counter = 0;
340+
return function () {
341+
counter += 1;
342+
return counter
343+
}
344+
})();
345+
return bar(foo(a), a);
346+
}",
347+
"foo.js",
348+
JavascriptParser,
349+
nom,
350+
[
351+
(functions, 3, usize), // f, foo, bar
352+
(closures, 1, usize), // return function ()
353+
(total, 4, usize)
354+
]
355+
);
356+
}
285357

286-
int foo() {
287-
return 0;
358+
#[test]
359+
fn javascript_call_nom() {
360+
check_metrics!(
361+
"add_task(async function test_safe_mode() {
362+
gAppInfo.inSafeMode = true;
363+
});",
364+
"foo.js",
365+
JavascriptParser,
366+
nom,
367+
[
368+
(functions, 1, usize), // test_safe_mode
369+
(closures, 0, usize),
370+
(total, 1, usize)
371+
]
372+
);
373+
}
374+
375+
#[test]
376+
fn javascript_assignment_nom() {
377+
check_metrics!(
378+
"AnimationTest.prototype.enableDisplay = function(element) {};",
379+
"foo.js",
380+
JavascriptParser,
381+
nom,
382+
[
383+
(functions, 1, usize),
384+
(closures, 0, usize),
385+
(total, 1, usize)
386+
]
387+
);
388+
}
389+
390+
#[test]
391+
fn javascript_labeled_nom() {
392+
check_metrics!(
393+
"toJSON: function() {
394+
return this.inspect(true);
288395
}",
289-
"foo.c",
290-
CppParser,
396+
"foo.js",
397+
JavascriptParser,
291398
nom,
292399
[
293400
(functions, 1, usize),
@@ -296,4 +403,106 @@ mod tests {
296403
]
297404
);
298405
}
406+
407+
#[test]
408+
fn javascript_pair_nom() {
409+
check_metrics!(
410+
"return {
411+
initialize: function(object) {
412+
this._object = object.toObject();
413+
},
414+
}",
415+
"foo.js",
416+
JavascriptParser,
417+
nom,
418+
[
419+
(functions, 1, usize),
420+
(closures, 0, usize),
421+
(total, 1, usize)
422+
]
423+
);
424+
}
425+
426+
#[test]
427+
fn javascript_unnamed_nom() {
428+
check_metrics!(
429+
"Ajax.getTransport = Try.these(
430+
function() {
431+
return function(){ return new XMLHttpRequest()}
432+
}
433+
);",
434+
"foo.js",
435+
JavascriptParser,
436+
nom,
437+
[
438+
(functions, 0, usize),
439+
(closures, 2, usize),
440+
(total, 2, usize)
441+
]
442+
);
443+
}
444+
445+
#[test]
446+
fn javascript_arrow_nom() {
447+
check_metrics!(
448+
"var materials = [\"Hydrogen\"];
449+
materials.map(material => material.length);
450+
let add = (a, b) => a + b;",
451+
"foo.js",
452+
JavascriptParser,
453+
nom,
454+
[
455+
(functions, 1, usize), // add
456+
(closures, 1, usize), // materials.map
457+
(total, 2, usize)
458+
]
459+
);
460+
}
461+
462+
#[test]
463+
fn javascript_arrow_assignment_nom() {
464+
check_metrics!(
465+
"sink.onPull = () => { };",
466+
"foo.js",
467+
JavascriptParser,
468+
nom,
469+
[
470+
(functions, 1, usize),
471+
(closures, 0, usize),
472+
(total, 1, usize)
473+
]
474+
);
475+
}
476+
477+
#[test]
478+
fn javascript_arrow_new_nom() {
479+
check_metrics!(
480+
"const response = new Promise(resolve => channel.port1.onmessage = resolve);",
481+
"foo.js",
482+
JavascriptParser,
483+
nom,
484+
[
485+
(functions, 0, usize),
486+
(closures, 1, usize),
487+
(total, 1, usize)
488+
]
489+
);
490+
}
491+
492+
#[test]
493+
fn javascript_arrow_call_nom() {
494+
check_metrics!(
495+
"let notDisabled = TestUtils.waitForCondition(
496+
() => !backbutton.hasAttribute(\"disabled\")
497+
);",
498+
"foo.js",
499+
JavascriptParser,
500+
nom,
501+
[
502+
(functions, 0, usize),
503+
(closures, 1, usize),
504+
(total, 1, usize)
505+
]
506+
);
507+
}
299508
}

0 commit comments

Comments
 (0)