2 * @fileoverview Rule to flag consistent return values
3 * @author Nicholas C. Zakas
7 //------------------------------------------------------------------------------
9 //------------------------------------------------------------------------------
11 const lodash = require("lodash");
13 const astUtils = require("./utils/ast-utils");
15 //------------------------------------------------------------------------------
17 //------------------------------------------------------------------------------
20 * Checks whether or not a given node is an `Identifier` node which was named a given name.
21 * @param {ASTNode} node A node to check.
22 * @param {string} name An expected name of the node.
23 * @returns {boolean} `true` if the node is an `Identifier` node which was named as expected.
25 function isIdentifier(node, name) {
26 return node.type === "Identifier" && node.name === name;
30 * Checks whether or not a given code path segment is unreachable.
31 * @param {CodePathSegment} segment A CodePathSegment to check.
32 * @returns {boolean} `true` if the segment is unreachable.
34 function isUnreachable(segment) {
35 return !segment.reachable;
39 * Checks whether a given node is a `constructor` method in an ES6 class
40 * @param {ASTNode} node A node to check
41 * @returns {boolean} `true` if the node is a `constructor` method
43 function isClassConstructor(node) {
44 return node.type === "FunctionExpression" &&
46 node.parent.type === "MethodDefinition" &&
47 node.parent.kind === "constructor";
50 //------------------------------------------------------------------------------
52 //------------------------------------------------------------------------------
59 description: "require `return` statements to either always or never specify values",
60 category: "Best Practices",
62 url: "https://eslint.org/docs/rules/consistent-return"
68 treatUndefinedAsUnspecified: {
73 additionalProperties: false
77 missingReturn: "Expected to return a value at the end of {{name}}.",
78 missingReturnValue: "{{name}} expected a return value.",
79 unexpectedReturnValue: "{{name}} expected no return value."
84 const options = context.options[0] || {};
85 const treatUndefinedAsUnspecified = options.treatUndefinedAsUnspecified === true;
89 * Checks whether of not the implicit returning is consistent if the last
90 * code path segment is reachable.
91 * @param {ASTNode} node A program/function node to check.
94 function checkLastSegment(node) {
98 * Skip if it expected no return value or unreachable.
99 * When unreachable, all paths are returned or thrown.
101 if (!funcInfo.hasReturnValue ||
102 funcInfo.codePath.currentSegments.every(isUnreachable) ||
103 astUtils.isES5Constructor(node) ||
104 isClassConstructor(node)
109 // Adjust a location and a message.
110 if (node.type === "Program") {
112 // The head of program.
113 loc = { line: 1, column: 0 };
115 } else if (node.type === "ArrowFunctionExpression") {
118 loc = context.getSourceCode().getTokenBefore(node.body, astUtils.isArrowToken).loc.start;
120 node.parent.type === "MethodDefinition" ||
121 (node.parent.type === "Property" && node.parent.method)
125 loc = node.parent.key.loc.start;
128 // Function name or `function` keyword.
129 loc = (node.id || node).loc.start;
133 name = astUtils.getFunctionNameWithKind(node);
140 messageId: "missingReturn",
147 // Initializes/Disposes state of each code path.
148 onCodePathStart(codePath, node) {
153 hasReturnValue: false,
159 funcInfo = funcInfo.upper;
162 // Reports a given return statement if it's inconsistent.
163 ReturnStatement(node) {
164 const argument = node.argument;
165 let hasReturnValue = Boolean(argument);
167 if (treatUndefinedAsUnspecified && hasReturnValue) {
168 hasReturnValue = !isIdentifier(argument, "undefined") && argument.operator !== "void";
171 if (!funcInfo.hasReturn) {
172 funcInfo.hasReturn = true;
173 funcInfo.hasReturnValue = hasReturnValue;
174 funcInfo.messageId = hasReturnValue ? "missingReturnValue" : "unexpectedReturnValue";
176 name: funcInfo.node.type === "Program"
178 : lodash.upperFirst(astUtils.getFunctionNameWithKind(funcInfo.node))
180 } else if (funcInfo.hasReturnValue !== hasReturnValue) {
183 messageId: funcInfo.messageId,
189 // Reports a given program/function if the implicit returning is not consistent.
190 "Program:exit": checkLastSegment,
191 "FunctionDeclaration:exit": checkLastSegment,
192 "FunctionExpression:exit": checkLastSegment,
193 "ArrowFunctionExpression:exit": checkLastSegment