]> git.proxmox.com Git - pve-eslint.git/blame - eslint/lib/rules/getter-return.js
import 8.41.0 source
[pve-eslint.git] / eslint / lib / rules / getter-return.js
CommitLineData
eb39fafa
DC
1/**
2 * @fileoverview Enforces that a return statement is present in property getters.
3 * @author Aladdin-ADD(hh_2013@foxmail.com)
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const astUtils = require("./utils/ast-utils");
13
14//------------------------------------------------------------------------------
15// Helpers
16//------------------------------------------------------------------------------
17const TARGET_NODE_TYPE = /^(?:Arrow)?FunctionExpression$/u;
18
19/**
20 * Checks a given code path segment is reachable.
21 * @param {CodePathSegment} segment A segment to check.
22 * @returns {boolean} `true` if the segment is reachable.
23 */
24function isReachable(segment) {
25 return segment.reachable;
26}
27
eb39fafa
DC
28//------------------------------------------------------------------------------
29// Rule Definition
30//------------------------------------------------------------------------------
31
34eeec05 32/** @type {import('../shared/types').Rule} */
eb39fafa
DC
33module.exports = {
34 meta: {
35 type: "problem",
36
37 docs: {
8f9d1d4d 38 description: "Enforce `return` statements in getters",
eb39fafa 39 recommended: true,
f2a92ac6 40 url: "https://eslint.org/docs/latest/rules/getter-return"
eb39fafa
DC
41 },
42
43 fixable: null,
44
45 schema: [
46 {
47 type: "object",
48 properties: {
49 allowImplicit: {
50 type: "boolean",
51 default: false
52 }
53 },
54 additionalProperties: false
55 }
56 ],
57
58 messages: {
59 expected: "Expected to return a value in {{name}}.",
60 expectedAlways: "Expected {{name}} to always return a value."
61 }
62 },
63
64 create(context) {
65
66 const options = context.options[0] || { allowImplicit: false };
f2a92ac6 67 const sourceCode = context.sourceCode;
eb39fafa
DC
68
69 let funcInfo = {
70 upper: null,
71 codePath: null,
72 hasReturn: false,
73 shouldCheck: false,
74 node: null
75 };
76
77 /**
78 * Checks whether or not the last code path segment is reachable.
79 * Then reports this function if the segment is reachable.
80 *
81 * If the last code path segment is reachable, there are paths which are not
82 * returned or thrown.
83 * @param {ASTNode} node A node to check.
84 * @returns {void}
85 */
86 function checkLastSegment(node) {
87 if (funcInfo.shouldCheck &&
88 funcInfo.codePath.currentSegments.some(isReachable)
89 ) {
90 context.report({
91 node,
56c4a2cb 92 loc: astUtils.getFunctionHeadLoc(node, sourceCode),
eb39fafa
DC
93 messageId: funcInfo.hasReturn ? "expectedAlways" : "expected",
94 data: {
95 name: astUtils.getFunctionNameWithKind(funcInfo.node)
96 }
97 });
98 }
99 }
100
101 /**
102 * Checks whether a node means a getter function.
103 * @param {ASTNode} node a node to check.
104 * @returns {boolean} if node means a getter, return true; else return false.
105 */
106 function isGetter(node) {
107 const parent = node.parent;
108
109 if (TARGET_NODE_TYPE.test(node.type) && node.body.type === "BlockStatement") {
110 if (parent.kind === "get") {
111 return true;
112 }
113 if (parent.type === "Property" && astUtils.getStaticPropertyName(parent) === "get" && parent.parent.type === "ObjectExpression") {
114
f2a92ac6
DC
115 // Object.defineProperty() or Reflect.defineProperty()
116 if (parent.parent.parent.type === "CallExpression") {
117 const callNode = parent.parent.parent.callee;
118
119 if (astUtils.isSpecificMemberAccess(callNode, "Object", "defineProperty") ||
120 astUtils.isSpecificMemberAccess(callNode, "Reflect", "defineProperty")) {
121 return true;
122 }
eb39fafa
DC
123 }
124
f2a92ac6 125 // Object.defineProperties() or Object.create()
eb39fafa
DC
126 if (parent.parent.parent.type === "Property" &&
127 parent.parent.parent.parent.type === "ObjectExpression" &&
f2a92ac6
DC
128 parent.parent.parent.parent.parent.type === "CallExpression") {
129 const callNode = parent.parent.parent.parent.parent.callee;
130
131 return astUtils.isSpecificMemberAccess(callNode, "Object", "defineProperties") ||
132 astUtils.isSpecificMemberAccess(callNode, "Object", "create");
eb39fafa
DC
133 }
134 }
135 }
136 return false;
137 }
138 return {
139
140 // Stacks this function's information.
141 onCodePathStart(codePath, node) {
142 funcInfo = {
143 upper: funcInfo,
144 codePath,
145 hasReturn: false,
146 shouldCheck: isGetter(node),
147 node
148 };
149 },
150
151 // Pops this function's information.
152 onCodePathEnd() {
153 funcInfo = funcInfo.upper;
154 },
155
156 // Checks the return statement is valid.
157 ReturnStatement(node) {
158 if (funcInfo.shouldCheck) {
159 funcInfo.hasReturn = true;
160
161 // if allowImplicit: false, should also check node.argument
162 if (!options.allowImplicit && !node.argument) {
163 context.report({
164 node,
165 messageId: "expected",
166 data: {
167 name: astUtils.getFunctionNameWithKind(funcInfo.node)
168 }
169 });
170 }
171 }
172 },
173
174 // Reports a given function if the last path is reachable.
175 "FunctionExpression:exit": checkLastSegment,
176 "ArrowFunctionExpression:exit": checkLastSegment
177 };
178 }
179};