]>
git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/array-callback-return.js
2 * @fileoverview Rule to enforce return statements in callbacks of array's methods
3 * @author Toru Nagashima
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const astUtils
= require("./utils/ast-utils");
14 //------------------------------------------------------------------------------
16 //------------------------------------------------------------------------------
18 const TARGET_NODE_TYPE
= /^(?:Arrow)?FunctionExpression$/u;
19 const TARGET_METHODS
= /^(?:every|filter|find(?:Index)?|flatMap|forEach|map|reduce(?:Right)?|some|sort)$/u;
22 * Checks a given code path segment is reachable.
23 * @param {CodePathSegment} segment A segment to check.
24 * @returns {boolean} `true` if the segment is reachable.
26 function isReachable(segment
) {
27 return segment
.reachable
;
31 * Checks a given node is a member access which has the specified name's
33 * @param {ASTNode} node A node to check.
34 * @returns {boolean} `true` if the node is a member access which has
35 * the specified name's property. The node may be a `(Chain|Member)Expression` node.
37 function isTargetMethod(node
) {
38 return astUtils
.isSpecificMemberAccess(node
, null, TARGET_METHODS
);
42 * Returns a human-legible description of an array method
43 * @param {string} arrayMethodName A method name to fully qualify
44 * @returns {string} the method name prefixed with `Array.` if it is a class method,
45 * or else `Array.prototype.` if it is an instance method.
47 function fullMethodName(arrayMethodName
) {
48 if (["from", "of", "isArray"].includes(arrayMethodName
)) {
49 return "Array.".concat(arrayMethodName
);
51 return "Array.prototype.".concat(arrayMethodName
);
55 * Checks whether or not a given node is a function expression which is the
56 * callback of an array method, returning the method name.
57 * @param {ASTNode} node A node to check. This is one of
58 * FunctionExpression or ArrowFunctionExpression.
59 * @returns {string} The method name if the node is a callback method,
62 function getArrayMethodName(node
) {
63 let currentNode
= node
;
66 const parent
= currentNode
.parent
;
68 switch (parent
.type
) {
71 * Looks up the destination. e.g.,
72 * foo.every(nativeFoo || function foo() { ... });
74 case "LogicalExpression":
75 case "ConditionalExpression":
76 case "ChainExpression":
81 * If the upper function is IIFE, checks the destination of the return value.
83 * foo.every((function() {
85 * return function callback() { ... };
88 case "ReturnStatement": {
89 const func
= astUtils
.getUpperFunction(parent
);
91 if (func
=== null || !astUtils
.isCallee(func
)) {
94 currentNode
= func
.parent
;
100 * Array.from([], function() {});
101 * list.every(function() {});
103 case "CallExpression":
104 if (astUtils
.isArrayFromMethod(parent
.callee
)) {
106 parent
.arguments
.length
>= 2 &&
107 parent
.arguments
[1] === currentNode
112 if (isTargetMethod(parent
.callee
)) {
114 parent
.arguments
.length
>= 1 &&
115 parent
.arguments
[0] === currentNode
117 return astUtils
.getStaticPropertyName(parent
.callee
);
122 // Otherwise this node is not target.
128 /* istanbul ignore next: unreachable */
132 //------------------------------------------------------------------------------
134 //------------------------------------------------------------------------------
141 description
: "enforce `return` statements in callbacks of array methods",
143 url
: "https://eslint.org/docs/rules/array-callback-return"
159 additionalProperties
: false
164 expectedAtEnd
: "{{arrayMethodName}}() expects a value to be returned at the end of {{name}}.",
165 expectedInside
: "{{arrayMethodName}}() expects a return value from {{name}}.",
166 expectedReturnValue
: "{{arrayMethodName}}() expects a return value from {{name}}.",
167 expectedNoReturnValue
: "{{arrayMethodName}}() expects no useless return value from {{name}}."
173 const options
= context
.options
[0] || { allowImplicit
: false, checkForEach
: false };
174 const sourceCode
= context
.getSourceCode();
177 arrayMethodName
: null,
186 * Checks whether or not the last code path segment is reachable.
187 * Then reports this function if the segment is reachable.
189 * If the last code path segment is reachable, there are paths which are not
190 * returned or thrown.
191 * @param {ASTNode} node A node to check.
194 function checkLastSegment(node
) {
196 if (!funcInfo
.shouldCheck
) {
200 let messageId
= null;
202 if (funcInfo
.arrayMethodName
=== "forEach") {
203 if (options
.checkForEach
&& node
.type
=== "ArrowFunctionExpression" && node
.expression
) {
204 messageId
= "expectedNoReturnValue";
207 if (node
.body
.type
=== "BlockStatement" && funcInfo
.codePath
.currentSegments
.some(isReachable
)) {
208 messageId
= funcInfo
.hasReturn
? "expectedAtEnd" : "expectedInside";
213 const name
= astUtils
.getFunctionNameWithKind(node
);
217 loc
: astUtils
.getFunctionHeadLoc(node
, sourceCode
),
219 data
: { name
, arrayMethodName
: fullMethodName(funcInfo
.arrayMethodName
) }
226 // Stacks this function's information.
227 onCodePathStart(codePath
, node
) {
229 let methodName
= null;
231 if (TARGET_NODE_TYPE
.test(node
.type
)) {
232 methodName
= getArrayMethodName(node
);
236 arrayMethodName
: methodName
,
248 // Pops this function's information.
250 funcInfo
= funcInfo
.upper
;
253 // Checks the return statement is valid.
254 ReturnStatement(node
) {
256 if (!funcInfo
.shouldCheck
) {
260 funcInfo
.hasReturn
= true;
262 let messageId
= null;
264 if (funcInfo
.arrayMethodName
=== "forEach") {
266 // if checkForEach: true, returning a value at any path inside a forEach is not allowed
267 if (options
.checkForEach
&& node
.argument
) {
268 messageId
= "expectedNoReturnValue";
272 // if allowImplicit: false, should also check node.argument
273 if (!options
.allowImplicit
&& !node
.argument
) {
274 messageId
= "expectedReturnValue";
283 name
: astUtils
.getFunctionNameWithKind(funcInfo
.node
),
284 arrayMethodName
: fullMethodName(funcInfo
.arrayMethodName
)
290 // Reports a given function if the last path is reachable.
291 "FunctionExpression:exit": checkLastSegment
,
292 "ArrowFunctionExpression:exit": checkLastSegment