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 //------------------------------------------------------------------------------
43 /** @type {import('../shared/types').Rule} */
49 description
: "Require `return` statements to either always or never specify values",
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