]> git.proxmox.com Git - pve-eslint.git/blame - eslint/lib/rules/no-invalid-this.js
import 8.23.1 source
[pve-eslint.git] / eslint / lib / rules / no-invalid-this.js
CommitLineData
eb39fafa 1/**
8f9d1d4d 2 * @fileoverview A rule to disallow `this` keywords in contexts where the value of `this` is `undefined`.
eb39fafa
DC
3 * @author Toru Nagashima
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const astUtils = require("./utils/ast-utils");
13
8f9d1d4d
DC
14//------------------------------------------------------------------------------
15// Helpers
16//------------------------------------------------------------------------------
17
18/**
19 * Determines if the given code path is a code path with lexical `this` binding.
20 * That is, if `this` within the code path refers to `this` of surrounding code path.
21 * @param {CodePath} codePath Code path.
22 * @param {ASTNode} node Node that started the code path.
23 * @returns {boolean} `true` if it is a code path with lexical `this` binding.
24 */
25function isCodePathWithLexicalThis(codePath, node) {
26 return codePath.origin === "function" && node.type === "ArrowFunctionExpression";
27}
28
eb39fafa
DC
29//------------------------------------------------------------------------------
30// Rule Definition
31//------------------------------------------------------------------------------
32
34eeec05 33/** @type {import('../shared/types').Rule} */
eb39fafa
DC
34module.exports = {
35 meta: {
36 type: "suggestion",
37
38 docs: {
8f9d1d4d 39 description: "Disallow use of `this` in contexts where the value of `this` is `undefined`",
eb39fafa
DC
40 recommended: false,
41 url: "https://eslint.org/docs/rules/no-invalid-this"
42 },
43
44 schema: [
45 {
46 type: "object",
47 properties: {
48 capIsConstructor: {
49 type: "boolean",
50 default: true
51 }
52 },
53 additionalProperties: false
54 }
55 ],
56
57 messages: {
58 unexpectedThis: "Unexpected 'this'."
59 }
60 },
61
62 create(context) {
63 const options = context.options[0] || {};
64 const capIsConstructor = options.capIsConstructor !== false;
65 const stack = [],
66 sourceCode = context.getSourceCode();
67
68 /**
69 * Gets the current checking context.
70 *
71 * The return value has a flag that whether or not `this` keyword is valid.
72 * The flag is initialized when got at the first time.
73 * @returns {{valid: boolean}}
74 * an object which has a flag that whether or not `this` keyword is valid.
75 */
76 stack.getCurrent = function() {
77 const current = this[this.length - 1];
78
79 if (!current.init) {
80 current.init = true;
81 current.valid = !astUtils.isDefaultThisBinding(
82 current.node,
83 sourceCode,
84 { capIsConstructor }
85 );
86 }
87 return current;
88 };
89
8f9d1d4d 90 return {
eb39fafa 91
8f9d1d4d
DC
92 onCodePathStart(codePath, node) {
93 if (isCodePathWithLexicalThis(codePath, node)) {
94 return;
95 }
eb39fafa 96
8f9d1d4d
DC
97 if (codePath.origin === "program") {
98 const scope = context.getScope();
99 const features = context.parserOptions.ecmaFeatures || {};
100
101 // `this` at the top level of scripts always refers to the global object
102 stack.push({
103 init: true,
104 node,
105 valid: !(
106 node.sourceType === "module" ||
107 (features.globalReturn && scope.childScopes[0].isStrict)
108 )
109 });
eb39fafa 110
8f9d1d4d
DC
111 return;
112 }
eb39fafa 113
8f9d1d4d
DC
114 /*
115 * `init: false` means that `valid` isn't determined yet.
116 * Most functions don't use `this`, and the calculation for `valid`
117 * is relatively costly, so we'll calculate it lazily when the first
118 * `this` within the function is traversed. A special case are non-strict
119 * functions, because `this` refers to the global object and therefore is
120 * always valid, so we can set `init: true` right away.
121 */
eb39fafa 122 stack.push({
8f9d1d4d 123 init: !context.getScope().isStrict,
eb39fafa 124 node,
8f9d1d4d 125 valid: true
eb39fafa
DC
126 });
127 },
128
8f9d1d4d
DC
129 onCodePathEnd(codePath, node) {
130 if (isCodePathWithLexicalThis(codePath, node)) {
131 return;
132 }
133
eb39fafa
DC
134 stack.pop();
135 },
136
eb39fafa
DC
137 // Reports if `this` of the current context is invalid.
138 ThisExpression(node) {
139 const current = stack.getCurrent();
140
141 if (current && !current.valid) {
142 context.report({
143 node,
144 messageId: "unexpectedThis"
145 });
146 }
147 }
148 };
149 }
150};