1 | /**
|
2 | * @fileoverview A rule to disallow `this` keywords outside of classes or class-like objects.
|
3 | * @author Toru Nagashima
|
4 | * @copyright 2015 Toru Nagashima. All rights reserved.
|
5 | * See LICENSE file in root directory for full license.
|
6 | */
|
7 |
|
8 | ;
|
9 |
|
10 | //------------------------------------------------------------------------------
|
11 | // Requirements
|
12 | //------------------------------------------------------------------------------
|
13 |
|
14 | var astUtils = require("../ast-utils");
|
15 |
|
16 | //------------------------------------------------------------------------------
|
17 | // Rule Definition
|
18 | //------------------------------------------------------------------------------
|
19 |
|
20 | module.exports = function(context) {
|
21 | var stack = [],
|
22 | sourceCode = context.getSourceCode();
|
23 |
|
24 | /**
|
25 | * Gets the current checking context.
|
26 | *
|
27 | * The return value has a flag that whether or not `this` keyword is valid.
|
28 | * The flag is initialized when got at the first time.
|
29 | *
|
30 | * @returns {{valid: boolean}}
|
31 | * an object which has a flag that whether or not `this` keyword is valid.
|
32 | */
|
33 | stack.getCurrent = function() {
|
34 | var current = this[this.length - 1];
|
35 | if (!current.init) {
|
36 | current.init = true;
|
37 | current.valid = !astUtils.isDefaultThisBinding(
|
38 | current.node,
|
39 | sourceCode);
|
40 | }
|
41 | return current;
|
42 | };
|
43 |
|
44 | /**
|
45 | * Pushs new checking context into the stack.
|
46 | *
|
47 | * The checking context is not initialized yet.
|
48 | * Because most functions don't have `this` keyword.
|
49 | * When `this` keyword was found, the checking context is initialized.
|
50 | *
|
51 | * @param {ASTNode} node - A function node that was entered.
|
52 | * @returns {void}
|
53 | */
|
54 | function enterFunction(node) {
|
55 | // `this` can be invalid only under strict mode.
|
56 | stack.push({
|
57 | init: !context.getScope().isStrict,
|
58 | node: node,
|
59 | valid: true
|
60 | });
|
61 | }
|
62 |
|
63 | /**
|
64 | * Pops the current checking context from the stack.
|
65 | * @returns {void}
|
66 | */
|
67 | function exitFunction() {
|
68 | stack.pop();
|
69 | }
|
70 |
|
71 | return {
|
72 | // `this` is invalid only under strict mode.
|
73 | // Modules is always strict mode.
|
74 | "Program": function(node) {
|
75 | var scope = context.getScope(),
|
76 | features = context.parserOptions.ecmaFeatures || {};
|
77 |
|
78 | stack.push({
|
79 | init: true,
|
80 | node: node,
|
81 | valid: !(
|
82 | scope.isStrict ||
|
83 | node.sourceType === "module" ||
|
84 | (features.globalReturn && scope.childScopes[0].isStrict)
|
85 | )
|
86 | });
|
87 | },
|
88 | "Program:exit": function() {
|
89 | stack.pop();
|
90 | },
|
91 |
|
92 | "FunctionDeclaration": enterFunction,
|
93 | "FunctionDeclaration:exit": exitFunction,
|
94 | "FunctionExpression": enterFunction,
|
95 | "FunctionExpression:exit": exitFunction,
|
96 |
|
97 | // Reports if `this` of the current context is invalid.
|
98 | "ThisExpression": function(node) {
|
99 | var current = stack.getCurrent();
|
100 | if (current && !current.valid) {
|
101 | context.report(node, "Unexpected 'this'.");
|
102 | }
|
103 | }
|
104 | };
|
105 | };
|
106 |
|
107 | module.exports.schema = [];
|