@@ -238,237 +238,63 @@ const isTagWithType = (tagName) => {
238
238
return tagsWithTypes . includes ( tagName ) ;
239
239
} ;
240
240
241
- const LOOP_STATEMENTS = [ 'WhileStatement' , 'DoWhileStatement' , 'ForStatement' , 'ForInStatement' , 'ForOfStatement' ] ;
242
-
243
- const STATEMENTS_WITH_CHILDREN = [
244
- '@loop' ,
245
- 'SwitchStatement' ,
246
- 'IfStatement' ,
247
- 'BlockStatement' ,
248
- 'TryStatement' ,
249
- 'WithStatement'
250
- ] ;
251
-
252
- const RETURNFREE_STATEMENTS = [
253
- 'VariableDeclaration' ,
254
- 'ThrowStatement' ,
255
- 'FunctionDeclaration' ,
256
- 'BreakStatement' ,
257
- 'ContinueStatement' ,
258
- 'LabeledStatement' ,
259
- 'DebuggerStatement' ,
260
- 'EmptyStatement' ,
261
- 'ThrowStatement' ,
262
- 'ExpressionStatement'
263
- ] ;
264
-
265
- const ENTRY_POINTS = [ 'FunctionDeclaration' , 'ArrowFunctionExpression' , 'FunctionExpression' ] ;
266
-
267
- /* eslint-disable sort-keys */
268
- const lookupTable = {
269
- ReturnStatement : {
270
- is ( node ) {
271
- return node . type === 'ReturnStatement' ;
272
- } ,
273
- check ( node ) {
274
- /* istanbul ignore next */
275
- if ( ! lookupTable . ReturnStatement . is ( node ) ) {
276
- return false ;
277
- }
278
-
279
- // A return without any arguments just exits the function
280
- // and is typically not documented at all in jsdoc.
281
- if ( node . argument === null ) {
282
- return false ;
283
- }
284
-
285
- return true ;
286
- }
287
- } ,
288
- WithStatement : {
289
- is ( node ) {
290
- return node . type === 'WithStatement' ;
291
- } ,
292
- check ( node , context ) {
293
- return lookupTable . BlockStatement . check ( node . body , context ) ;
294
- }
295
- } ,
296
- IfStatement : {
297
- is ( node ) {
298
- return node . type === 'IfStatement' ;
299
- } ,
300
- check ( node ) {
301
- /* istanbul ignore next */
302
- if ( ! lookupTable . IfStatement . is ( node ) ) {
303
- return false ;
304
- }
305
-
306
- if ( lookupTable [ '@default' ] . check ( node . consequent ) ) {
307
- return true ;
308
- }
309
-
310
- if ( node . alternate && lookupTable [ '@default' ] . check ( node . alternate ) ) {
311
- return true ;
312
- }
313
-
314
- return false ;
315
- }
316
- } ,
317
- '@loop' : {
318
- is ( node ) {
319
- return LOOP_STATEMENTS . includes ( node . type ) ;
320
- } ,
321
- check ( node ) {
322
- return lookupTable [ '@default' ] . check ( node . body ) ;
323
- }
324
- } ,
325
- SwitchStatement : {
326
- is ( node ) {
327
- return node . type === 'SwitchStatement' ;
328
- } ,
329
- check ( node ) {
330
- for ( const item of node . cases ) {
331
- for ( const statement of item . consequent ) {
332
- if ( lookupTable [ '@default' ] . check ( statement ) ) {
333
- return true ;
334
- }
335
- }
336
- }
337
-
338
- return false ;
339
- }
340
- } ,
341
- TryStatement : {
342
- is ( node ) {
343
- return node . type === 'TryStatement' ;
344
- } ,
345
- check ( node ) {
346
- /* istanbul ignore next */
347
- if ( ! lookupTable . TryStatement . is ( node ) ) {
348
- return false ;
349
- }
350
-
351
- if ( lookupTable . BlockStatement . check ( node . block ) ) {
352
- return true ;
353
- }
354
-
355
- if ( node . handler && node . handler . body ) {
356
- if ( lookupTable [ '@default' ] . check ( node . handler . body ) ) {
357
- return true ;
358
- }
359
- }
360
- if ( lookupTable . BlockStatement . check ( node . finalizer ) ) {
361
- return true ;
362
- }
363
-
364
- return false ;
365
- }
366
- } ,
367
- BlockStatement : {
368
- is ( node ) {
369
- return node . type === 'BlockStatement' ;
370
- } ,
371
- check ( node , context ) {
372
- // E.g. the catch block statement is optional.
373
- /* istanbul ignore next */
374
- if ( typeof node === 'undefined' || node === null ) {
375
- return false ;
376
- }
377
-
378
- /* istanbul ignore next */
379
- if ( ! lookupTable . BlockStatement . is ( node ) ) {
380
- return false ;
381
- }
382
-
383
- for ( const item of node . body ) {
384
- if ( lookupTable [ '@default' ] . check ( item , context ) ) {
385
- return true ;
386
- }
387
- }
388
-
389
- return false ;
390
- }
391
- } ,
392
- FunctionExpression : {
393
- is ( node ) {
394
- return node . type === 'FunctionExpression' ;
395
- } ,
396
- check ( node , context , ignoreAsync ) {
397
- return ! ignoreAsync && node . async || lookupTable . BlockStatement . check ( node . body , context ) ;
398
- }
399
- } ,
400
- ArrowFunctionExpression : {
401
- is ( node ) {
402
- return node . type === 'ArrowFunctionExpression' ;
403
- } ,
404
- check ( node , context , ignoreAsync ) {
405
- // An expression always has a return value.
406
- return node . expression ||
407
- ! ignoreAsync && node . async ||
408
- lookupTable . BlockStatement . check ( node . body , context ) ;
409
- }
410
- } ,
411
- FunctionDeclaration : {
412
- is ( node ) {
413
- return node . type === 'FunctionDeclaration' ;
414
- } ,
415
- check ( node , context , ignoreAsync ) {
416
- return ! ignoreAsync && node . async || lookupTable . BlockStatement . check ( node . body , context ) ;
417
- }
418
- } ,
419
- '@default' : {
420
- check ( node , context ) {
421
- // In case it is a `ReturnStatement`, we found what we were looking for
422
- if ( lookupTable . ReturnStatement . is ( node ) ) {
423
- return lookupTable . ReturnStatement . check ( node , context ) ;
424
- }
425
-
426
- // In case the element has children, we need to traverse them.
427
- // Examples are BlockStatement, Choices, TryStatement, Loops, ...
428
- for ( const item of STATEMENTS_WITH_CHILDREN ) {
429
- if ( lookupTable [ item ] . is ( node ) ) {
430
- return lookupTable [ item ] . check ( node , context ) ;
431
- }
432
- }
433
-
434
- // Everything else cannot return anything.
435
- /* istanbul ignore next */
436
- if ( RETURNFREE_STATEMENTS . includes ( node . type ) ) {
437
- return false ;
438
- }
439
-
440
- /* istanbul ignore next */
441
- // If we end up here, we stumbled upon an unknown element.
442
- // Most likely it is enough to add it to the blacklist.
443
- //
444
- // throw new Error('Unknown node type: ' + node.type);
445
- return false ;
446
- }
447
- }
448
- } ;
449
-
450
241
/**
451
- * Checks if the source code returns a return value.
452
- * It traverses the parsed source code and returns as
453
- * soon as it stumbles upon the first return statement.
242
+ * Checks if a node has a return statement. Void return does not count.
454
243
*
455
244
* @param {object } node
456
- * the node which should be checked.
457
- * @param {object } context
458
- * @param {boolean } ignoreAsync
459
- * ignore implicit async return.
460
245
* @returns {boolean }
461
- * true in case the code returns a return value
462
246
*/
463
- const hasReturnValue = ( node , context , ignoreAsync ) => {
464
- // Loop through all of our entry points
465
- for ( const item of ENTRY_POINTS ) {
466
- if ( lookupTable [ item ] . is ( node ) ) {
467
- return lookupTable [ item ] . check ( node , context , ignoreAsync ) ;
247
+ // eslint-disable-next-line complexity
248
+ const hasReturnValue = ( node ) => {
249
+ if ( ! node ) {
250
+ return false ;
251
+ }
252
+ switch ( node . type ) {
253
+ case 'FunctionExpression' :
254
+ case 'FunctionDeclaration' :
255
+ case 'ArrowFunctionExpression' : {
256
+ return node . expression || hasReturnValue ( node . body ) ;
257
+ }
258
+ case 'BlockStatement' : {
259
+ return node . body . some ( ( bodyNode ) => {
260
+ return bodyNode . type !== 'FunctionDeclaration' && hasReturnValue ( bodyNode ) ;
261
+ } ) ;
262
+ }
263
+ case 'WhileStatement' :
264
+ case 'DoWhileStatement' :
265
+ case 'ForStatement' :
266
+ case 'ForInStatement' :
267
+ case 'ForOfStatement' :
268
+ case 'WithStatement' : {
269
+ return hasReturnValue ( node . body ) ;
270
+ }
271
+ case 'IfStatement' : {
272
+ return hasReturnValue ( node . consequent ) || hasReturnValue ( node . alternate ) ;
273
+ }
274
+ case 'TryStatement' : {
275
+ return hasReturnValue ( node . block ) ||
276
+ hasReturnValue ( node . handler && node . handler . body ) ||
277
+ hasReturnValue ( node . finalizer ) ;
278
+ }
279
+ case 'SwitchStatement' : {
280
+ return node . cases . some (
281
+ ( someCase ) => {
282
+ return someCase . consequent . some ( hasReturnValue ) ;
283
+ }
284
+ ) ;
285
+ }
286
+ case 'ReturnStatement' : {
287
+ // void return does not count.
288
+ if ( node . argument === null ) {
289
+ return false ;
468
290
}
291
+
292
+ return true ;
293
+ }
294
+ default : {
295
+ return false ;
296
+ }
469
297
}
470
- /* istanbul ignore next */
471
- throw new Error ( `Unknown element ${ node . type } ` ) ;
472
298
} ;
473
299
474
300
/** @param {string } tag */
@@ -553,8 +379,8 @@ const getTagsByType = (tags, tagPreference) => {
553
379
} ) ;
554
380
555
381
return {
556
- tagsWithoutNames ,
557
- tagsWithNames
382
+ tagsWithNames ,
383
+ tagsWithoutNames
558
384
} ;
559
385
} ;
560
386
0 commit comments