UNPKG

5.27 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to flag statements that use != and == instead of !== and ===
3 * @author Nicholas C. Zakas
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Rule Definition
10//------------------------------------------------------------------------------
11
12module.exports = {
13 meta: {
14 docs: {
15 description: "require the use of `===` and `!==`",
16 category: "Best Practices",
17 recommended: false
18 },
19
20 schema: {
21 anyOf: [
22 {
23 type: "array",
24 items: [
25 {
26 enum: ["always"]
27 },
28 {
29 type: "object",
30 properties: {
31 null: {
32 enum: ["always", "never", "ignore"]
33 }
34 },
35 additionalProperties: false
36 }
37 ],
38 additionalItems: false
39 },
40 {
41 type: "array",
42 items: [
43 {
44 enum: ["smart", "allow-null"]
45 }
46 ],
47 additionalItems: false
48 }
49 ]
50 }
51 },
52
53 create(context) {
54 const config = context.options[0] || "always";
55 const options = context.options[1] || {};
56 const sourceCode = context.getSourceCode();
57
58 const nullOption = (config === "always") ?
59 options.null || "always" :
60 "ignore";
61 const enforceRuleForNull = (nullOption === "always");
62 const enforceInverseRuleForNull = (nullOption === "never");
63
64 /**
65 * Checks if an expression is a typeof expression
66 * @param {ASTNode} node The node to check
67 * @returns {boolean} if the node is a typeof expression
68 */
69 function isTypeOf(node) {
70 return node.type === "UnaryExpression" && node.operator === "typeof";
71 }
72
73 /**
74 * Checks if either operand of a binary expression is a typeof operation
75 * @param {ASTNode} node The node to check
76 * @returns {boolean} if one of the operands is typeof
77 * @private
78 */
79 function isTypeOfBinary(node) {
80 return isTypeOf(node.left) || isTypeOf(node.right);
81 }
82
83 /**
84 * Checks if operands are literals of the same type (via typeof)
85 * @param {ASTNode} node The node to check
86 * @returns {boolean} if operands are of same type
87 * @private
88 */
89 function areLiteralsAndSameType(node) {
90 return node.left.type === "Literal" && node.right.type === "Literal" &&
91 typeof node.left.value === typeof node.right.value;
92 }
93
94 /**
95 * Checks if one of the operands is a literal null
96 * @param {ASTNode} node The node to check
97 * @returns {boolean} if operands are null
98 * @private
99 */
100 function isNullCheck(node) {
101 return (node.right.type === "Literal" && node.right.value === null) ||
102 (node.left.type === "Literal" && node.left.value === null);
103 }
104
105 /**
106 * Gets the location (line and column) of the binary expression's operator
107 * @param {ASTNode} node The binary expression node to check
108 * @param {string} operator The operator to find
109 * @returns {Object} { line, column } location of operator
110 * @private
111 */
112 function getOperatorLocation(node) {
113 const opToken = sourceCode.getTokenAfter(node.left);
114
115 return {line: opToken.loc.start.line, column: opToken.loc.start.column};
116 }
117
118 /**
119 * Reports a message for this rule.
120 * @param {ASTNode} node The binary expression node that was checked
121 * @param {string} message The message to report
122 * @returns {void}
123 * @private
124 */
125 function report(node, message) {
126 context.report({
127 node,
128 loc: getOperatorLocation(node),
129 message,
130 data: { op: node.operator.charAt(0) }
131 });
132 }
133
134 return {
135 BinaryExpression(node) {
136 const isNull = isNullCheck(node);
137
138 if (node.operator !== "==" && node.operator !== "!=") {
139 if (enforceInverseRuleForNull && isNull) {
140 report(node, "Expected '{{op}}=' and instead saw '{{op}}=='.");
141 }
142 return;
143 }
144
145 if (config === "smart" && (isTypeOfBinary(node) ||
146 areLiteralsAndSameType(node) || isNull)) {
147 return;
148 }
149
150 if (!enforceRuleForNull && isNull) {
151 return;
152 }
153
154 report(node, "Expected '{{op}}==' and instead saw '{{op}}='.");
155 }
156 };
157
158 }
159};