]>
Commit | Line | Data |
---|---|---|
6f036462 TL |
1 | /** |
2 | * @fileoverview Rule to disallow returning values from Promise executor functions | |
3 | * @author Milos Djermanovic | |
4 | */ | |
5 | ||
6 | "use strict"; | |
7 | ||
8 | //------------------------------------------------------------------------------ | |
9 | // Requirements | |
10 | //------------------------------------------------------------------------------ | |
11 | ||
12 | const { findVariable } = require("eslint-utils"); | |
13 | ||
14 | //------------------------------------------------------------------------------ | |
15 | // Helpers | |
16 | //------------------------------------------------------------------------------ | |
17 | ||
18 | const functionTypesToCheck = new Set(["ArrowFunctionExpression", "FunctionExpression"]); | |
19 | ||
20 | /** | |
21 | * Determines whether the given identifier node is a reference to a global variable. | |
22 | * @param {ASTNode} node `Identifier` node to check. | |
23 | * @param {Scope} scope Scope to which the node belongs. | |
24 | * @returns {boolean} True if the identifier is a reference to a global variable. | |
25 | */ | |
26 | function isGlobalReference(node, scope) { | |
27 | const variable = findVariable(scope, node); | |
28 | ||
29 | return variable !== null && variable.scope.type === "global" && variable.defs.length === 0; | |
30 | } | |
31 | ||
32 | /** | |
33 | * Finds function's outer scope. | |
34 | * @param {Scope} scope Function's own scope. | |
35 | * @returns {Scope} Function's outer scope. | |
36 | */ | |
37 | function getOuterScope(scope) { | |
38 | const upper = scope.upper; | |
39 | ||
40 | if (upper.type === "function-expression-name") { | |
41 | return upper.upper; | |
42 | } | |
43 | return upper; | |
44 | } | |
45 | ||
46 | /** | |
47 | * Determines whether the given function node is used as a Promise executor. | |
48 | * @param {ASTNode} node The node to check. | |
49 | * @param {Scope} scope Function's own scope. | |
50 | * @returns {boolean} `true` if the node is a Promise executor. | |
51 | */ | |
52 | function isPromiseExecutor(node, scope) { | |
53 | const parent = node.parent; | |
54 | ||
55 | return parent.type === "NewExpression" && | |
56 | parent.arguments[0] === node && | |
57 | parent.callee.type === "Identifier" && | |
58 | parent.callee.name === "Promise" && | |
59 | isGlobalReference(parent.callee, getOuterScope(scope)); | |
60 | } | |
61 | ||
62 | //------------------------------------------------------------------------------ | |
63 | // Rule Definition | |
64 | //------------------------------------------------------------------------------ | |
65 | ||
66 | module.exports = { | |
67 | meta: { | |
68 | type: "problem", | |
69 | ||
70 | docs: { | |
71 | description: "disallow returning values from Promise executor functions", | |
72 | category: "Possible Errors", | |
73 | recommended: false, | |
74 | url: "https://eslint.org/docs/rules/no-promise-executor-return" | |
75 | }, | |
76 | ||
77 | schema: [], | |
78 | ||
79 | messages: { | |
80 | returnsValue: "Return values from promise executor functions cannot be read." | |
81 | } | |
82 | }, | |
83 | ||
84 | create(context) { | |
85 | ||
86 | let funcInfo = null; | |
87 | ||
88 | /** | |
89 | * Reports the given node. | |
90 | * @param {ASTNode} node Node to report. | |
91 | * @returns {void} | |
92 | */ | |
93 | function report(node) { | |
94 | context.report({ node, messageId: "returnsValue" }); | |
95 | } | |
96 | ||
97 | return { | |
98 | ||
99 | onCodePathStart(_, node) { | |
100 | funcInfo = { | |
101 | upper: funcInfo, | |
102 | shouldCheck: functionTypesToCheck.has(node.type) && isPromiseExecutor(node, context.getScope()) | |
103 | }; | |
104 | ||
105 | if (funcInfo.shouldCheck && node.type === "ArrowFunctionExpression" && node.expression) { | |
106 | report(node.body); | |
107 | } | |
108 | }, | |
109 | ||
110 | onCodePathEnd() { | |
111 | funcInfo = funcInfo.upper; | |
112 | }, | |
113 | ||
114 | ReturnStatement(node) { | |
115 | if (funcInfo.shouldCheck && node.argument) { | |
116 | report(node); | |
117 | } | |
118 | } | |
119 | }; | |
120 | } | |
121 | }; |