Skip to content

Commit 1a7fc97

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

File tree

1 file changed

+285
-50
lines changed

1 file changed

+285
-50
lines changed

src/metrics/nom.rs

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

71+
#[inline(always)]
72+
fn is_child(node: &Node, id: u16) -> bool {
73+
node.object()
74+
.children(&mut node.object().walk())
75+
.any(|child| child.kind_id() == id)
76+
}
77+
78+
#[inline(always)]
79+
fn has_sibling(node: &Node, id: u16) -> bool {
80+
let parent = node.object().parent();
81+
if let Some(parent) = parent {
82+
node.object()
83+
.children(&mut parent.walk())
84+
.any(|child| child.kind_id() == id)
85+
} else {
86+
false
87+
}
88+
}
89+
90+
macro_rules! function {
91+
($node: ident, $stats: ident) => {
92+
// If a function node has named ancestors or child then it is a function,
93+
// otherwise a closure.
94+
if count_specific_ancestors!(
95+
$node,
96+
VariableDeclarator | AssignmentExpression | LabeledStatement | Pair,
97+
StatementBlock | ReturnStatement | NewExpression | Arguments
98+
) > 0
99+
|| is_child($node, Identifier as u16)
100+
{
101+
$stats.functions += 1;
102+
} else {
103+
$stats.closures += 1;
104+
}
105+
};
106+
}
107+
108+
macro_rules! arrow_function {
109+
($node: ident, $stats: ident) => {
110+
// If an arrow function node has named ancestors then it is a function,
111+
// otherwise a closure.
112+
if count_specific_ancestors!(
113+
$node,
114+
VariableDeclarator | AssignmentExpression | LabeledStatement,
115+
StatementBlock | ReturnStatement | NewExpression | CallExpression
116+
) > 0
117+
|| has_sibling($node, PropertyIdentifier as u16)
118+
{
119+
$stats.functions += 1;
120+
} else {
121+
$stats.closures += 1;
122+
}
123+
};
124+
}
125+
126+
macro_rules! js_grammar {
127+
($grammar: ident, $node: ident, $stats: ident) => {
128+
use $grammar::*;
129+
130+
match $node.object().kind_id().into() {
131+
FunctionDeclaration | MethodDefinition => {
132+
$stats.functions += 1;
133+
}
134+
GeneratorFunction | GeneratorFunctionDeclaration => {
135+
$stats.closures += 1;
136+
}
137+
Function => {
138+
function!($node, $stats);
139+
}
140+
ArrowFunction => {
141+
arrow_function!($node, $stats);
142+
}
143+
_ => {}
144+
}
145+
};
146+
}
147+
148+
macro_rules! typescript_grammar {
149+
($grammar: ident, $node: ident, $stats: ident) => {
150+
use $grammar::*;
151+
152+
match $node.object().kind_id().into() {
153+
FunctionDeclaration | MethodDefinition => {
154+
$stats.functions += 1;
155+
}
156+
GeneratorFunction | GeneratorFunctionDeclaration => {
157+
$stats.closures += 1;
158+
}
159+
Function => {
160+
function!($node, $stats);
161+
}
162+
ArrowFunction => {
163+
arrow_function!($node, $stats);
164+
}
165+
_ => {}
166+
}
167+
};
168+
}
169+
71170
#[doc(hidden)]
72171
pub trait Nom
73172
where
@@ -94,65 +193,25 @@ impl Nom for PythonCode {
94193

95194
impl Nom for MozjsCode {
96195
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-
}
196+
js_grammar!(Mozjs, node, stats);
108197
}
109198
}
110199

111200
impl Nom for JavascriptCode {
112201
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-
}
202+
js_grammar!(Javascript, node, stats);
124203
}
125204
}
126205

127206
impl Nom for TypescriptCode {
128207
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-
}
208+
typescript_grammar!(Typescript, node, stats);
140209
}
141210
}
142211

143212
impl Nom for TsxCode {
144213
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-
}
214+
typescript_grammar!(Tsx, node, stats);
156215
}
157216
}
158217

@@ -279,15 +338,89 @@ mod tests {
279338
}
280339

281340
#[test]
282-
fn c_nom() {
341+
fn javascript_nom() {
283342
check_metrics!(
284-
"int foo();
343+
"function f(a, b) {
344+
function foo(a) {
345+
return a;
346+
}
347+
var bar = (function () {
348+
var counter = 0;
349+
return function () {
350+
counter += 1;
351+
return counter
352+
}
353+
})();
354+
return bar(foo(a), a);
355+
}",
356+
"foo.js",
357+
JavascriptParser,
358+
nom,
359+
[
360+
(functions, 3, usize), // f, foo, bar
361+
(closures, 1, usize), // return function ()
362+
(total, 4, usize)
363+
]
364+
);
365+
}
285366

286-
int foo() {
287-
return 0;
367+
#[test]
368+
fn javascript_call_nom() {
369+
check_metrics!(
370+
"add_task(async function test_safe_mode() {
371+
gAppInfo.inSafeMode = true;
372+
});",
373+
"foo.js",
374+
JavascriptParser,
375+
nom,
376+
[
377+
(functions, 1, usize), // test_safe_mode
378+
(closures, 0, usize),
379+
(total, 1, usize)
380+
]
381+
);
382+
}
383+
384+
#[test]
385+
fn javascript_assignment_nom() {
386+
check_metrics!(
387+
"AnimationTest.prototype.enableDisplay = function(element) {};",
388+
"foo.js",
389+
JavascriptParser,
390+
nom,
391+
[
392+
(functions, 1, usize),
393+
(closures, 0, usize),
394+
(total, 1, usize)
395+
]
396+
);
397+
}
398+
399+
#[test]
400+
fn javascript_labeled_nom() {
401+
check_metrics!(
402+
"toJSON: function() {
403+
return this.inspect(true);
288404
}",
289-
"foo.c",
290-
CppParser,
405+
"foo.js",
406+
JavascriptParser,
407+
nom,
408+
[
409+
(functions, 1, usize),
410+
(closures, 0, usize),
411+
(total, 1, usize)
412+
]
413+
);
414+
}
415+
416+
#[test]
417+
fn javascript_labeled_arrow_nom() {
418+
check_metrics!(
419+
"const dimConverters = {
420+
pt: x => x,
421+
};",
422+
"foo.js",
423+
JavascriptParser,
291424
nom,
292425
[
293426
(functions, 1, usize),
@@ -296,4 +429,106 @@ mod tests {
296429
]
297430
);
298431
}
432+
433+
#[test]
434+
fn javascript_pair_nom() {
435+
check_metrics!(
436+
"return {
437+
initialize: function(object) {
438+
this._object = object.toObject();
439+
},
440+
}",
441+
"foo.js",
442+
JavascriptParser,
443+
nom,
444+
[
445+
(functions, 1, usize),
446+
(closures, 0, usize),
447+
(total, 1, usize)
448+
]
449+
);
450+
}
451+
452+
#[test]
453+
fn javascript_unnamed_nom() {
454+
check_metrics!(
455+
"Ajax.getTransport = Try.these(
456+
function() {
457+
return function(){ return new XMLHttpRequest()}
458+
}
459+
);",
460+
"foo.js",
461+
JavascriptParser,
462+
nom,
463+
[
464+
(functions, 0, usize),
465+
(closures, 2, usize),
466+
(total, 2, usize)
467+
]
468+
);
469+
}
470+
471+
#[test]
472+
fn javascript_arrow_nom() {
473+
check_metrics!(
474+
"var materials = [\"Hydrogen\"];
475+
materials.map(material => material.length);
476+
let add = (a, b) => a + b;",
477+
"foo.js",
478+
JavascriptParser,
479+
nom,
480+
[
481+
(functions, 1, usize), // add
482+
(closures, 1, usize), // materials.map
483+
(total, 2, usize)
484+
]
485+
);
486+
}
487+
488+
#[test]
489+
fn javascript_arrow_assignment_nom() {
490+
check_metrics!(
491+
"sink.onPull = () => { };",
492+
"foo.js",
493+
JavascriptParser,
494+
nom,
495+
[
496+
(functions, 1, usize),
497+
(closures, 0, usize),
498+
(total, 1, usize)
499+
]
500+
);
501+
}
502+
503+
#[test]
504+
fn javascript_arrow_new_nom() {
505+
check_metrics!(
506+
"const response = new Promise(resolve => channel.port1.onmessage = resolve);",
507+
"foo.js",
508+
JavascriptParser,
509+
nom,
510+
[
511+
(functions, 0, usize),
512+
(closures, 1, usize),
513+
(total, 1, usize)
514+
]
515+
);
516+
}
517+
518+
#[test]
519+
fn javascript_arrow_call_nom() {
520+
check_metrics!(
521+
"let notDisabled = TestUtils.waitForCondition(
522+
() => !backbutton.hasAttribute(\"disabled\")
523+
);",
524+
"foo.js",
525+
JavascriptParser,
526+
nom,
527+
[
528+
(functions, 0, usize),
529+
(closures, 1, usize),
530+
(total, 1, usize)
531+
]
532+
);
533+
}
299534
}

0 commit comments

Comments
 (0)