@@ -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) ]
72171pub trait Nom
73172where
@@ -94,65 +193,25 @@ impl Nom for PythonCode {
94193
95194impl 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
111200impl 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
127206impl 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
143212impl 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