2 * @fileoverview Rule to flag consistent return values
3 * @author Nicholas C. Zakas
7 //------------------------------------------------------------------------------
9 //------------------------------------------------------------------------------
11 const astUtils = require("./utils/ast-utils");
12 const { upperCaseFirst } = require("../shared/string-utils");
14 //------------------------------------------------------------------------------
16 //------------------------------------------------------------------------------
19 * Checks whether or not a given code path segment is unreachable.
20 * @param {CodePathSegment} segment A CodePathSegment to check.
21 * @returns {boolean} `true` if the segment is unreachable.
23 function isUnreachable(segment) {
24 return !segment.reachable;
28 * Checks whether a given node is a `constructor` method in an ES6 class
29 * @param {ASTNode} node A node to check
30 * @returns {boolean} `true` if the node is a `constructor` method
32 function isClassConstructor(node) {
33 return node.type === "FunctionExpression" &&
35 node.parent.type === "MethodDefinition" &&
36 node.parent.kind === "constructor";
39 //------------------------------------------------------------------------------
41 //------------------------------------------------------------------------------
48 description: "require `return` statements to either always or never specify values",
49 category: "Best Practices",
51 url: "https://eslint.org/docs/rules/consistent-return"
57 treatUndefinedAsUnspecified: {
62 additionalProperties: false
66 missingReturn: "Expected to return a value at the end of {{name}}.",
67 missingReturnValue: "{{name}} expected a return value.",
68 unexpectedReturnValue: "{{name}} expected no return value."
73 const options = context.options[0] || {};
74 const treatUndefinedAsUnspecified = options.treatUndefinedAsUnspecified === true;
78 * Checks whether of not the implicit returning is consistent if the last
79 * code path segment is reachable.
80 * @param {ASTNode} node A program/function node to check.
83 function checkLastSegment(node) {
87 * Skip if it expected no return value or unreachable.
88 * When unreachable, all paths are returned or thrown.
90 if (!funcInfo.hasReturnValue ||
91 funcInfo.codePath.currentSegments.every(isUnreachable) ||
92 astUtils.isES5Constructor(node) ||
93 isClassConstructor(node)
98 // Adjust a location and a message.
99 if (node.type === "Program") {
101 // The head of program.
102 loc = { line: 1, column: 0 };
104 } else if (node.type === "ArrowFunctionExpression") {
107 loc = context.getSourceCode().getTokenBefore(node.body, astUtils.isArrowToken).loc;
109 node.parent.type === "MethodDefinition" ||
110 (node.parent.type === "Property" && node.parent.method)
114 loc = node.parent.key.loc;
117 // Function name or `function` keyword.
118 loc = (node.id || context.getSourceCode().getFirstToken(node)).loc;
122 name = astUtils.getFunctionNameWithKind(node);
129 messageId: "missingReturn",
136 // Initializes/Disposes state of each code path.
137 onCodePathStart(codePath, node) {
142 hasReturnValue: false,
148 funcInfo = funcInfo.upper;
151 // Reports a given return statement if it's inconsistent.
152 ReturnStatement(node) {
153 const argument = node.argument;
154 let hasReturnValue = Boolean(argument);
156 if (treatUndefinedAsUnspecified && hasReturnValue) {
157 hasReturnValue = !astUtils.isSpecificId(argument, "undefined") && argument.operator !== "void";
160 if (!funcInfo.hasReturn) {
161 funcInfo.hasReturn = true;
162 funcInfo.hasReturnValue = hasReturnValue;
163 funcInfo.messageId = hasReturnValue ? "missingReturnValue" : "unexpectedReturnValue";
165 name: funcInfo.node.type === "Program"
167 : upperCaseFirst(astUtils.getFunctionNameWithKind(funcInfo.node))
169 } else if (funcInfo.hasReturnValue !== hasReturnValue) {
172 messageId: funcInfo.messageId,
178 // Reports a given program/function if the implicit returning is not consistent.
179 "Program:exit": checkLastSegment,
180 "FunctionDeclaration:exit": checkLastSegment,
181 "FunctionExpression:exit": checkLastSegment,
182 "ArrowFunctionExpression:exit": checkLastSegment