@@ -7,6 +7,10 @@ import {
7
7
closureTags ,
8
8
typeScriptTags ,
9
9
} from './tagNames' ;
10
+ import {
11
+ hasReturnValue ,
12
+ hasValueOrExecutorHasNonEmptyResolveValue ,
13
+ } from './utils/hasReturnValue' ;
10
14
11
15
/**
12
16
* @typedef {"jsdoc"|"typescript"|"closure" } ParserMode
@@ -669,355 +673,6 @@ const tagMissingRequiredTypeOrNamepath = (tag, tagMap = tagStructure) => {
669
673
return mustHaveEither && ! hasEither && ! mustHaveTypePosition ;
670
674
} ;
671
675
672
- /**
673
- * Checks if a node is a promise but has no resolve value or an empty value.
674
- * An `undefined` resolve does not count.
675
- *
676
- * @param {object } node
677
- * @returns {boolean }
678
- */
679
- const isNewPromiseExpression = ( node ) => {
680
- return node && node . type === 'NewExpression' && node . callee . type === 'Identifier' &&
681
- node . callee . name === 'Promise' ;
682
- } ;
683
-
684
- const isVoidPromise = ( node ) => {
685
- return node ?. typeParameters ?. params ?. [ 0 ] ?. type === 'TSVoidKeyword' ;
686
- } ;
687
-
688
- /**
689
- * @callback PromiseFilter
690
- * @param {object } node
691
- * @returns {boolean }
692
- */
693
-
694
- /**
695
- * Checks if a node has a return statement. Void return does not count.
696
- *
697
- * @param {object } node
698
- * @param {PromiseFilter } promFilter
699
- * @returns {boolean|Node }
700
- */
701
- // eslint-disable-next-line complexity
702
- const hasReturnValue = ( node , promFilter ) => {
703
- if ( ! node ) {
704
- return false ;
705
- }
706
-
707
- switch ( node . type ) {
708
- case 'TSFunctionType' :
709
- case 'TSMethodSignature' :
710
- return ! [
711
- 'TSVoidKeyword' ,
712
- 'TSUndefinedKeyword' ,
713
- ] . includes ( node ?. returnType ?. typeAnnotation ?. type ) ;
714
- case 'MethodDefinition' :
715
- return hasReturnValue ( node . value , promFilter ) ;
716
- case 'FunctionExpression' :
717
- case 'FunctionDeclaration' :
718
- case 'ArrowFunctionExpression' : {
719
- return node . expression && ( ! isNewPromiseExpression ( node . body ) || ! isVoidPromise ( node . body ) ) ||
720
- hasReturnValue ( node . body , promFilter ) ;
721
- }
722
-
723
- case 'BlockStatement' : {
724
- return node . body . some ( ( bodyNode ) => {
725
- return bodyNode . type !== 'FunctionDeclaration' && hasReturnValue ( bodyNode , promFilter ) ;
726
- } ) ;
727
- }
728
-
729
- case 'LabeledStatement' :
730
- case 'WhileStatement' :
731
- case 'DoWhileStatement' :
732
- case 'ForStatement' :
733
- case 'ForInStatement' :
734
- case 'ForOfStatement' :
735
- case 'WithStatement' : {
736
- return hasReturnValue ( node . body , promFilter ) ;
737
- }
738
-
739
- case 'IfStatement' : {
740
- return hasReturnValue ( node . consequent , promFilter ) || hasReturnValue ( node . alternate , promFilter ) ;
741
- }
742
-
743
- case 'TryStatement' : {
744
- return hasReturnValue ( node . block , promFilter ) ||
745
- hasReturnValue ( node . handler && node . handler . body , promFilter ) ||
746
- hasReturnValue ( node . finalizer , promFilter ) ;
747
- }
748
-
749
- case 'SwitchStatement' : {
750
- return node . cases . some (
751
- ( someCase ) => {
752
- return someCase . consequent . some ( ( nde ) => {
753
- return hasReturnValue ( nde , promFilter ) ;
754
- } ) ;
755
- } ,
756
- ) ;
757
- }
758
-
759
- case 'ReturnStatement' : {
760
- // void return does not count.
761
- if ( node . argument === null ) {
762
- return false ;
763
- }
764
-
765
- if ( promFilter && isNewPromiseExpression ( node . argument ) ) {
766
- // Let caller decide how to filter, but this is, at the least,
767
- // a return of sorts and truthy
768
- return promFilter ( node . argument ) ;
769
- }
770
-
771
- return true ;
772
- }
773
-
774
- default : {
775
- return false ;
776
- }
777
- }
778
- } ;
779
-
780
- /**
781
- * Avoids further checking child nodes if a nested function shadows the
782
- * resolver, but otherwise, if name is used (by call or passed in as an
783
- * argument to another function), will be considered as non-empty.
784
- *
785
- * This could check for redeclaration of the resolver, but as such is
786
- * unlikely, we avoid the performance cost of checking everywhere for
787
- * (re)declarations or assignments.
788
- *
789
- * @param {AST } node
790
- * @param {string } resolverName
791
- * @returns {boolean }
792
- */
793
- // eslint-disable-next-line complexity
794
- const hasNonEmptyResolverCall = ( node , resolverName ) => {
795
- if ( ! node ) {
796
- return false ;
797
- }
798
-
799
- // Arrow function without block
800
- switch ( node . type ) {
801
- // istanbul ignore next -- In Babel?
802
- case 'OptionalCallExpression' :
803
- case 'CallExpression' :
804
- return node . callee . name === resolverName && (
805
-
806
- // Implicit or explicit undefined
807
- node . arguments . length > 1 || node . arguments [ 0 ] !== undefined
808
- ) ||
809
- node . arguments . some ( ( nde ) => {
810
- // Being passed in to another function (which might invoke it)
811
- return nde . type === 'Identifier' && nde . name === resolverName ||
812
-
813
- // Handle nested items
814
- hasNonEmptyResolverCall ( nde , resolverName ) ;
815
- } ) ;
816
- case 'ChainExpression' :
817
- case 'Decorator' :
818
- case 'ExpressionStatement' :
819
- return hasNonEmptyResolverCall ( node . expression , resolverName ) ;
820
- case 'ClassBody' :
821
- case 'BlockStatement' :
822
- return node . body . some ( ( bodyNode ) => {
823
- return hasNonEmptyResolverCall ( bodyNode , resolverName ) ;
824
- } ) ;
825
- case 'FunctionExpression' :
826
- case 'FunctionDeclaration' :
827
- case 'ArrowFunctionExpression' : {
828
- // Shadowing
829
- if ( node . params [ 0 ] ?. name === resolverName ) {
830
- return false ;
831
- }
832
-
833
- return hasNonEmptyResolverCall ( node . body , resolverName ) ;
834
- }
835
-
836
- case 'LabeledStatement' :
837
- case 'WhileStatement' :
838
- case 'DoWhileStatement' :
839
- case 'ForStatement' :
840
- case 'ForInStatement' :
841
- case 'ForOfStatement' :
842
- case 'WithStatement' : {
843
- return hasNonEmptyResolverCall ( node . body , resolverName ) ;
844
- }
845
-
846
- case 'ConditionalExpression' :
847
- case 'IfStatement' : {
848
- return hasNonEmptyResolverCall ( node . test , resolverName ) ||
849
- hasNonEmptyResolverCall ( node . consequent , resolverName ) ||
850
- hasNonEmptyResolverCall ( node . alternate , resolverName ) ;
851
- }
852
-
853
- case 'TryStatement' : {
854
- return hasNonEmptyResolverCall ( node . block , resolverName ) ||
855
- hasNonEmptyResolverCall ( node . handler && node . handler . body , resolverName ) ||
856
- hasNonEmptyResolverCall ( node . finalizer , resolverName ) ;
857
- }
858
-
859
- case 'SwitchStatement' : {
860
- return node . cases . some (
861
- ( someCase ) => {
862
- return someCase . consequent . some ( ( nde ) => {
863
- return hasNonEmptyResolverCall ( nde , resolverName ) ;
864
- } ) ;
865
- } ,
866
- ) ;
867
- }
868
-
869
- case 'ArrayPattern' :
870
- case 'ArrayExpression' :
871
- return node . elements . some ( ( element ) => {
872
- return hasNonEmptyResolverCall ( element , resolverName ) ;
873
- } ) ;
874
-
875
- case 'AssignmentPattern' :
876
- return hasNonEmptyResolverCall ( node . right , resolverName ) ;
877
-
878
- case 'AssignmentExpression' :
879
- case 'BinaryExpression' :
880
- case 'LogicalExpression' : {
881
- return hasNonEmptyResolverCall ( node . left , resolverName ) ||
882
- hasNonEmptyResolverCall ( node . right , resolverName ) ;
883
- }
884
-
885
- // Comma
886
- case 'SequenceExpression' :
887
- case 'TemplateLiteral' :
888
- return node . expressions . some ( ( subExpression ) => {
889
- return hasNonEmptyResolverCall ( subExpression , resolverName ) ;
890
- } ) ;
891
-
892
- case 'ObjectPattern' :
893
- case 'ObjectExpression' :
894
- return node . properties . some ( ( property ) => {
895
- return hasNonEmptyResolverCall ( property , resolverName ) ;
896
- } ) ;
897
- // istanbul ignore next -- In Babel?
898
- case 'ClassMethod' :
899
- case 'MethodDefinition' :
900
- return node . decorators && node . decorators . some ( ( decorator ) => {
901
- return hasNonEmptyResolverCall ( decorator , resolverName ) ;
902
- } ) ||
903
- node . computed && hasNonEmptyResolverCall ( node . key , resolverName ) ||
904
- hasNonEmptyResolverCall ( node . value , resolverName ) ;
905
-
906
- // istanbul ignore next -- In Babel?
907
- case 'ObjectProperty' :
908
- /* eslint-disable no-fallthrough */
909
- // istanbul ignore next -- In Babel?
910
- case 'PropertyDefinition' :
911
- // istanbul ignore next -- In Babel?
912
- case 'ClassProperty' :
913
- /* eslint-enable no-fallthrough */
914
- case 'Property' :
915
- return node . computed && hasNonEmptyResolverCall ( node . key , resolverName ) ||
916
- hasNonEmptyResolverCall ( node . value , resolverName ) ;
917
- // istanbul ignore next -- In Babel?
918
- case 'ObjectMethod' :
919
- // istanbul ignore next -- In Babel?
920
- return node . computed && hasNonEmptyResolverCall ( node . key , resolverName ) ||
921
- node . arguments . some ( ( nde ) => {
922
- return hasNonEmptyResolverCall ( nde , resolverName ) ;
923
- } ) ;
924
-
925
- case 'ClassExpression' :
926
- case 'ClassDeclaration' :
927
- return hasNonEmptyResolverCall ( node . body , resolverName ) ;
928
-
929
- case 'AwaitExpression' :
930
- case 'SpreadElement' :
931
- case 'UnaryExpression' :
932
- case 'YieldExpression' :
933
- return hasNonEmptyResolverCall ( node . argument , resolverName ) ;
934
-
935
- case 'VariableDeclaration' : {
936
- return node . declarations . some ( ( nde ) => {
937
- return hasNonEmptyResolverCall ( nde , resolverName ) ;
938
- } ) ;
939
- }
940
-
941
- case 'VariableDeclarator' : {
942
- return hasNonEmptyResolverCall ( node . id , resolverName ) ||
943
- hasNonEmptyResolverCall ( node . init , resolverName ) ;
944
- }
945
-
946
- case 'TaggedTemplateExpression' :
947
- return hasNonEmptyResolverCall ( node . quasi , resolverName ) ;
948
-
949
- // ?.
950
- // istanbul ignore next -- In Babel?
951
- case 'OptionalMemberExpression' :
952
- case 'MemberExpression' :
953
- return hasNonEmptyResolverCall ( node . object , resolverName ) ||
954
- hasNonEmptyResolverCall ( node . property , resolverName ) ;
955
-
956
- // istanbul ignore next -- In Babel?
957
- case 'Import' :
958
- case 'ImportExpression' :
959
- return hasNonEmptyResolverCall ( node . source , resolverName ) ;
960
-
961
- case 'ReturnStatement' : {
962
- if ( node . argument === null ) {
963
- return false ;
964
- }
965
-
966
- return hasNonEmptyResolverCall ( node . argument , resolverName ) ;
967
- }
968
-
969
- /*
970
- // Shouldn't need to parse literals/literal components, etc.
971
-
972
- case 'Identifier':
973
- case 'TemplateElement':
974
- case 'Super':
975
- // Exports not relevant in this context
976
- */
977
- default :
978
- return false ;
979
- }
980
- } ;
981
-
982
- /**
983
- * Checks if a Promise executor has no resolve value or an empty value.
984
- * An `undefined` resolve does not count.
985
- *
986
- * @param {object } node
987
- * @param {boolean } anyPromiseAsReturn
988
- * @returns {boolean }
989
- */
990
- const hasValueOrExecutorHasNonEmptyResolveValue = ( node , anyPromiseAsReturn ) => {
991
- return hasReturnValue ( node , ( prom ) => {
992
- if ( anyPromiseAsReturn ) {
993
- return true ;
994
- }
995
-
996
- if ( isVoidPromise ( prom ) ) {
997
- return false ;
998
- }
999
-
1000
- const [
1001
- {
1002
- params,
1003
- body,
1004
- } = { } ,
1005
- ] = prom . arguments ;
1006
-
1007
- if ( ! params ?. length ) {
1008
- return false ;
1009
- }
1010
-
1011
- const [
1012
- {
1013
- name : resolverName ,
1014
- } ,
1015
- ] = params ;
1016
-
1017
- return hasNonEmptyResolverCall ( body , resolverName ) ;
1018
- } ) ;
1019
- } ;
1020
-
1021
676
// eslint-disable-next-line complexity
1022
677
const hasNonFunctionYield = ( node , checkYieldReturnValue ) => {
1023
678
if ( ! node ) {
0 commit comments