minor adjustment to readme
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / consistent-return.js
1 /**
2  * @fileoverview Rule to flag consistent return values
3  * @author Nicholas C. Zakas
4  */
5 "use strict";
6
7 //------------------------------------------------------------------------------
8 // Requirements
9 //------------------------------------------------------------------------------
10
11 const lodash = require("lodash");
12
13 const astUtils = require("./utils/ast-utils");
14
15 //------------------------------------------------------------------------------
16 // Helpers
17 //------------------------------------------------------------------------------
18
19 /**
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.
24  */
25 function isIdentifier(node, name) {
26     return node.type === "Identifier" && node.name === name;
27 }
28
29 /**
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.
33  */
34 function isUnreachable(segment) {
35     return !segment.reachable;
36 }
37
38 /**
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
42  */
43 function isClassConstructor(node) {
44     return node.type === "FunctionExpression" &&
45         node.parent &&
46         node.parent.type === "MethodDefinition" &&
47         node.parent.kind === "constructor";
48 }
49
50 //------------------------------------------------------------------------------
51 // Rule Definition
52 //------------------------------------------------------------------------------
53
54 module.exports = {
55     meta: {
56         type: "suggestion",
57
58         docs: {
59             description: "require `return` statements to either always or never specify values",
60             category: "Best Practices",
61             recommended: false,
62             url: "https://eslint.org/docs/rules/consistent-return"
63         },
64
65         schema: [{
66             type: "object",
67             properties: {
68                 treatUndefinedAsUnspecified: {
69                     type: "boolean",
70                     default: false
71                 }
72             },
73             additionalProperties: false
74         }],
75
76         messages: {
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."
80         }
81     },
82
83     create(context) {
84         const options = context.options[0] || {};
85         const treatUndefinedAsUnspecified = options.treatUndefinedAsUnspecified === true;
86         let funcInfo = null;
87
88         /**
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.
92          * @returns {void}
93          */
94         function checkLastSegment(node) {
95             let loc, name;
96
97             /*
98              * Skip if it expected no return value or unreachable.
99              * When unreachable, all paths are returned or thrown.
100              */
101             if (!funcInfo.hasReturnValue ||
102                 funcInfo.codePath.currentSegments.every(isUnreachable) ||
103                 astUtils.isES5Constructor(node) ||
104                 isClassConstructor(node)
105             ) {
106                 return;
107             }
108
109             // Adjust a location and a message.
110             if (node.type === "Program") {
111
112                 // The head of program.
113                 loc = { line: 1, column: 0 };
114                 name = "program";
115             } else if (node.type === "ArrowFunctionExpression") {
116
117                 // `=>` token
118                 loc = context.getSourceCode().getTokenBefore(node.body, astUtils.isArrowToken).loc.start;
119             } else if (
120                 node.parent.type === "MethodDefinition" ||
121                 (node.parent.type === "Property" && node.parent.method)
122             ) {
123
124                 // Method name.
125                 loc = node.parent.key.loc.start;
126             } else {
127
128                 // Function name or `function` keyword.
129                 loc = (node.id || node).loc.start;
130             }
131
132             if (!name) {
133                 name = astUtils.getFunctionNameWithKind(node);
134             }
135
136             // Reports.
137             context.report({
138                 node,
139                 loc,
140                 messageId: "missingReturn",
141                 data: { name }
142             });
143         }
144
145         return {
146
147             // Initializes/Disposes state of each code path.
148             onCodePathStart(codePath, node) {
149                 funcInfo = {
150                     upper: funcInfo,
151                     codePath,
152                     hasReturn: false,
153                     hasReturnValue: false,
154                     messageId: "",
155                     node
156                 };
157             },
158             onCodePathEnd() {
159                 funcInfo = funcInfo.upper;
160             },
161
162             // Reports a given return statement if it's inconsistent.
163             ReturnStatement(node) {
164                 const argument = node.argument;
165                 let hasReturnValue = Boolean(argument);
166
167                 if (treatUndefinedAsUnspecified && hasReturnValue) {
168                     hasReturnValue = !isIdentifier(argument, "undefined") && argument.operator !== "void";
169                 }
170
171                 if (!funcInfo.hasReturn) {
172                     funcInfo.hasReturn = true;
173                     funcInfo.hasReturnValue = hasReturnValue;
174                     funcInfo.messageId = hasReturnValue ? "missingReturnValue" : "unexpectedReturnValue";
175                     funcInfo.data = {
176                         name: funcInfo.node.type === "Program"
177                             ? "Program"
178                             : lodash.upperFirst(astUtils.getFunctionNameWithKind(funcInfo.node))
179                     };
180                 } else if (funcInfo.hasReturnValue !== hasReturnValue) {
181                     context.report({
182                         node,
183                         messageId: funcInfo.messageId,
184                         data: funcInfo.data
185                     });
186                 }
187             },
188
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
194         };
195     }
196 };