eslint 7.0.0-alpha.2 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/CHANGELOG.md +332 -0
  2. package/README.md +9 -10
  3. package/bin/eslint.js +115 -77
  4. package/conf/category-list.json +0 -1
  5. package/conf/environments.js +2 -1
  6. package/lib/api.js +2 -0
  7. package/lib/cli-engine/cascading-config-array-factory.js +16 -2
  8. package/lib/cli-engine/cli-engine.js +53 -47
  9. package/lib/cli-engine/config-array/config-array.js +30 -1
  10. package/lib/cli-engine/config-array/ignore-pattern.js +7 -1
  11. package/lib/cli-engine/config-array-factory.js +244 -235
  12. package/lib/cli.js +181 -95
  13. package/lib/eslint/eslint.js +656 -0
  14. package/lib/eslint/index.js +7 -0
  15. package/lib/init/autoconfig.js +4 -4
  16. package/lib/init/config-file.js +2 -2
  17. package/lib/init/config-initializer.js +3 -4
  18. package/lib/init/source-code-utils.js +2 -2
  19. package/lib/linter/linter.js +2 -1
  20. package/lib/linter/node-event-generator.js +2 -2
  21. package/lib/options.js +0 -1
  22. package/lib/rule-tester/rule-tester.js +132 -22
  23. package/lib/rules/accessor-pairs.js +1 -1
  24. package/lib/rules/array-callback-return.js +3 -18
  25. package/lib/rules/arrow-parens.js +19 -3
  26. package/lib/rules/block-spacing.js +19 -2
  27. package/lib/rules/callback-return.js +4 -0
  28. package/lib/rules/camelcase.js +38 -1
  29. package/lib/rules/comma-style.js +3 -8
  30. package/lib/rules/func-call-spacing.js +4 -3
  31. package/lib/rules/getter-return.js +2 -12
  32. package/lib/rules/global-require.js +4 -0
  33. package/lib/rules/handle-callback-err.js +4 -0
  34. package/lib/rules/id-blacklist.js +138 -102
  35. package/lib/rules/index.js +1 -0
  36. package/lib/rules/key-spacing.js +1 -1
  37. package/lib/rules/linebreak-style.js +8 -2
  38. package/lib/rules/max-lines-per-function.js +1 -1
  39. package/lib/rules/new-cap.js +1 -1
  40. package/lib/rules/newline-per-chained-call.js +6 -3
  41. package/lib/rules/no-alert.js +5 -3
  42. package/lib/rules/no-buffer-constructor.js +4 -0
  43. package/lib/rules/no-empty-function.js +4 -2
  44. package/lib/rules/no-eval.js +2 -1
  45. package/lib/rules/no-extra-bind.js +1 -1
  46. package/lib/rules/no-extra-boolean-cast.js +102 -23
  47. package/lib/rules/no-extra-parens.js +9 -5
  48. package/lib/rules/no-implied-eval.js +83 -101
  49. package/lib/rules/no-inner-declarations.js +31 -39
  50. package/lib/rules/no-lone-blocks.js +1 -1
  51. package/lib/rules/no-loss-of-precision.js +198 -0
  52. package/lib/rules/no-magic-numbers.js +72 -37
  53. package/lib/rules/no-mixed-requires.js +4 -0
  54. package/lib/rules/no-new-func.js +22 -19
  55. package/lib/rules/no-new-object.js +15 -3
  56. package/lib/rules/no-new-require.js +4 -0
  57. package/lib/rules/no-new-symbol.js +2 -1
  58. package/lib/rules/no-new-wrappers.js +1 -1
  59. package/lib/rules/no-obj-calls.js +24 -5
  60. package/lib/rules/no-path-concat.js +4 -0
  61. package/lib/rules/no-plusplus.js +39 -3
  62. package/lib/rules/no-process-env.js +4 -0
  63. package/lib/rules/no-process-exit.js +4 -0
  64. package/lib/rules/no-prototype-builtins.js +1 -1
  65. package/lib/rules/no-restricted-modules.js +4 -0
  66. package/lib/rules/no-sync.js +4 -0
  67. package/lib/rules/no-unexpected-multiline.js +22 -12
  68. package/lib/rules/no-useless-concat.js +1 -1
  69. package/lib/rules/one-var-declaration-per-line.js +1 -1
  70. package/lib/rules/operator-assignment.js +3 -3
  71. package/lib/rules/operator-linebreak.js +4 -16
  72. package/lib/rules/padded-blocks.js +17 -4
  73. package/lib/rules/prefer-numeric-literals.js +3 -3
  74. package/lib/rules/prefer-object-spread.js +2 -2
  75. package/lib/rules/require-await.js +1 -1
  76. package/lib/rules/rest-spread-spacing.js +3 -6
  77. package/lib/rules/semi-spacing.js +32 -8
  78. package/lib/rules/space-before-function-paren.js +5 -2
  79. package/lib/rules/template-curly-spacing.js +59 -42
  80. package/lib/rules/utils/ast-utils.js +116 -10
  81. package/lib/rules/yoda.js +101 -51
  82. package/lib/shared/relative-module-resolver.js +1 -0
  83. package/lib/shared/types.js +9 -2
  84. package/lib/source-code/source-code.js +1 -0
  85. package/messages/extend-config-missing.txt +1 -1
  86. package/messages/no-config-found.txt +1 -1
  87. package/messages/plugin-conflict.txt +7 -0
  88. package/messages/plugin-missing.txt +1 -1
  89. package/messages/whitespace-found.txt +1 -1
  90. package/package.json +27 -26
@@ -7,6 +7,9 @@
7
7
 
8
8
  const { isNumericLiteral } = require("./utils/ast-utils");
9
9
 
10
+ // Maximum array length by the ECMAScript Specification.
11
+ const MAX_ARRAY_LENGTH = 2 ** 32 - 1;
12
+
10
13
  //------------------------------------------------------------------------------
11
14
  // Rule Definition
12
15
  //------------------------------------------------------------------------------
@@ -76,83 +79,115 @@ module.exports = {
76
79
  ignore = (config.ignore || []).map(normalizeIgnoreValue),
77
80
  ignoreArrayIndexes = !!config.ignoreArrayIndexes;
78
81
 
82
+ const okTypes = detectObjects ? [] : ["ObjectExpression", "Property", "AssignmentExpression"];
83
+
79
84
  /**
80
- * Returns whether the number should be ignored
81
- * @param {number} num the number
82
- * @returns {boolean} true if the number should be ignored
85
+ * Returns whether the rule is configured to ignore the given value
86
+ * @param {bigint|number} value The value to check
87
+ * @returns {boolean} true if the value is ignored
83
88
  */
84
- function shouldIgnoreNumber(num) {
85
- return ignore.indexOf(num) !== -1;
89
+ function isIgnoredValue(value) {
90
+ return ignore.indexOf(value) !== -1;
86
91
  }
87
92
 
88
93
  /**
89
- * Returns whether the number should be ignored when used as a radix within parseInt() or Number.parseInt()
90
- * @param {ASTNode} parent the non-"UnaryExpression" parent
91
- * @param {ASTNode} node the node literal being evaluated
92
- * @returns {boolean} true if the number should be ignored
94
+ * Returns whether the given node is used as a radix within parseInt() or Number.parseInt()
95
+ * @param {ASTNode} fullNumberNode `Literal` or `UnaryExpression` full number node
96
+ * @returns {boolean} true if the node is radix
93
97
  */
94
- function shouldIgnoreParseInt(parent, node) {
95
- return parent.type === "CallExpression" && node === parent.arguments[1] &&
96
- (parent.callee.name === "parseInt" ||
97
- parent.callee.type === "MemberExpression" &&
98
- parent.callee.object.name === "Number" &&
99
- parent.callee.property.name === "parseInt");
98
+ function isParseIntRadix(fullNumberNode) {
99
+ const parent = fullNumberNode.parent;
100
+
101
+ return parent.type === "CallExpression" && fullNumberNode === parent.arguments[1] &&
102
+ (
103
+ parent.callee.name === "parseInt" ||
104
+ (
105
+ parent.callee.type === "MemberExpression" &&
106
+ parent.callee.object.name === "Number" &&
107
+ parent.callee.property.name === "parseInt"
108
+ )
109
+ );
100
110
  }
101
111
 
102
112
  /**
103
- * Returns whether the number should be ignored when used to define a JSX prop
104
- * @param {ASTNode} parent the non-"UnaryExpression" parent
105
- * @returns {boolean} true if the number should be ignored
113
+ * Returns whether the given node is a direct child of a JSX node.
114
+ * In particular, it aims to detect numbers used as prop values in JSX tags.
115
+ * Example: <input maxLength={10} />
116
+ * @param {ASTNode} fullNumberNode `Literal` or `UnaryExpression` full number node
117
+ * @returns {boolean} true if the node is a JSX number
106
118
  */
107
- function shouldIgnoreJSXNumbers(parent) {
108
- return parent.type.indexOf("JSX") === 0;
119
+ function isJSXNumber(fullNumberNode) {
120
+ return fullNumberNode.parent.type.indexOf("JSX") === 0;
109
121
  }
110
122
 
111
123
  /**
112
- * Returns whether the number should be ignored when used as an array index with enabled 'ignoreArrayIndexes' option.
113
- * @param {ASTNode} node Node to check
114
- * @returns {boolean} true if the number should be ignored
124
+ * Returns whether the given node is used as an array index.
125
+ * Value must coerce to a valid array index name: "0", "1", "2" ... "4294967294".
126
+ *
127
+ * All other values, like "-1", "2.5", or "4294967295", are just "normal" object properties,
128
+ * which can be created and accessed on an array in addition to the array index properties,
129
+ * but they don't affect array's length and are not considered by methods such as .map(), .forEach() etc.
130
+ *
131
+ * The maximum array length by the specification is 2 ** 32 - 1 = 4294967295,
132
+ * thus the maximum valid index is 2 ** 32 - 2 = 4294967294.
133
+ *
134
+ * All notations are allowed, as long as the value coerces to one of "0", "1", "2" ... "4294967294".
135
+ *
136
+ * Valid examples:
137
+ * a[0], a[1], a[1.2e1], a[0xAB], a[0n], a[1n]
138
+ * a[-0] (same as a[0] because -0 coerces to "0")
139
+ * a[-0n] (-0n evaluates to 0n)
140
+ *
141
+ * Invalid examples:
142
+ * a[-1], a[-0xAB], a[-1n], a[2.5], a[1.23e1], a[12e-1]
143
+ * a[4294967295] (above the max index, it's an access to a regular property a["4294967295"])
144
+ * a[999999999999999999999] (even if it wasn't above the max index, it would be a["1e+21"])
145
+ * a[1e310] (same as a["Infinity"])
146
+ * @param {ASTNode} fullNumberNode `Literal` or `UnaryExpression` full number node
147
+ * @param {bigint|number} value Value expressed by the fullNumberNode
148
+ * @returns {boolean} true if the node is a valid array index
115
149
  */
116
- function shouldIgnoreArrayIndexes(node) {
117
- const parent = node.parent;
150
+ function isArrayIndex(fullNumberNode, value) {
151
+ const parent = fullNumberNode.parent;
118
152
 
119
- return ignoreArrayIndexes &&
120
- parent.type === "MemberExpression" && parent.property === node;
153
+ return parent.type === "MemberExpression" && parent.property === fullNumberNode &&
154
+ (Number.isInteger(value) || typeof value === "bigint") &&
155
+ value >= 0 && value < MAX_ARRAY_LENGTH;
121
156
  }
122
157
 
123
158
  return {
124
159
  Literal(node) {
125
- const okTypes = detectObjects ? [] : ["ObjectExpression", "Property", "AssignmentExpression"];
126
-
127
160
  if (!isNumericLiteral(node)) {
128
161
  return;
129
162
  }
130
163
 
131
164
  let fullNumberNode;
132
- let parent;
133
165
  let value;
134
166
  let raw;
135
167
 
136
- // For negative magic numbers: update the value and parent node
168
+ // Treat unary minus as a part of the number
137
169
  if (node.parent.type === "UnaryExpression" && node.parent.operator === "-") {
138
170
  fullNumberNode = node.parent;
139
- parent = fullNumberNode.parent;
140
171
  value = -node.value;
141
172
  raw = `-${node.raw}`;
142
173
  } else {
143
174
  fullNumberNode = node;
144
- parent = node.parent;
145
175
  value = node.value;
146
176
  raw = node.raw;
147
177
  }
148
178
 
149
- if (shouldIgnoreNumber(value) ||
150
- shouldIgnoreParseInt(parent, fullNumberNode) ||
151
- shouldIgnoreArrayIndexes(fullNumberNode) ||
152
- shouldIgnoreJSXNumbers(parent)) {
179
+ // Always allow radix arguments and JSX props
180
+ if (
181
+ isIgnoredValue(value) ||
182
+ isParseIntRadix(fullNumberNode) ||
183
+ isJSXNumber(fullNumberNode) ||
184
+ (ignoreArrayIndexes && isArrayIndex(fullNumberNode, value))
185
+ ) {
153
186
  return;
154
187
  }
155
188
 
189
+ const parent = fullNumberNode.parent;
190
+
156
191
  if (parent.type === "VariableDeclarator") {
157
192
  if (enforceConst && parent.parent.kind !== "const") {
158
193
  context.report({
@@ -11,6 +11,10 @@
11
11
 
12
12
  module.exports = {
13
13
  meta: {
14
+ deprecated: true,
15
+
16
+ replacedBy: [],
17
+
14
18
  type: "suggestion",
15
19
 
16
20
  docs: {
@@ -29,26 +29,29 @@ module.exports = {
29
29
 
30
30
  create(context) {
31
31
 
32
- //--------------------------------------------------------------------------
33
- // Helpers
34
- //--------------------------------------------------------------------------
35
-
36
- /**
37
- * Reports a node.
38
- * @param {ASTNode} node The node to report
39
- * @returns {void}
40
- * @private
41
- */
42
- function report(node) {
43
- context.report({
44
- node,
45
- messageId: "noFunctionConstructor"
46
- });
47
- }
48
-
49
32
  return {
50
- "NewExpression[callee.name = 'Function']": report,
51
- "CallExpression[callee.name = 'Function']": report
33
+ "Program:exit"() {
34
+ const globalScope = context.getScope();
35
+ const variable = globalScope.set.get("Function");
36
+
37
+ if (variable && variable.defs.length === 0) {
38
+ variable.references.forEach(ref => {
39
+ const node = ref.identifier;
40
+ const { parent } = node;
41
+
42
+ if (
43
+ parent &&
44
+ (parent.type === "NewExpression" || parent.type === "CallExpression") &&
45
+ node === parent.callee
46
+ ) {
47
+ context.report({
48
+ node: parent,
49
+ messageId: "noFunctionConstructor"
50
+ });
51
+ }
52
+ });
53
+ }
54
+ }
52
55
  };
53
56
 
54
57
  }
@@ -5,6 +5,12 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ const astUtils = require("./utils/ast-utils");
13
+
8
14
  //------------------------------------------------------------------------------
9
15
  // Rule Definition
10
16
  //------------------------------------------------------------------------------
@@ -28,10 +34,17 @@ module.exports = {
28
34
  },
29
35
 
30
36
  create(context) {
31
-
32
37
  return {
33
-
34
38
  NewExpression(node) {
39
+ const variable = astUtils.getVariableByName(
40
+ context.getScope(),
41
+ node.callee.name
42
+ );
43
+
44
+ if (variable && variable.identifiers.length > 0) {
45
+ return;
46
+ }
47
+
35
48
  if (node.callee.name === "Object") {
36
49
  context.report({
37
50
  node,
@@ -40,6 +53,5 @@ module.exports = {
40
53
  }
41
54
  }
42
55
  };
43
-
44
56
  }
45
57
  };
@@ -11,6 +11,10 @@
11
11
 
12
12
  module.exports = {
13
13
  meta: {
14
+ deprecated: true,
15
+
16
+ replacedBy: [],
17
+
14
18
  type: "suggestion",
15
19
 
16
20
  docs: {
@@ -37,8 +37,9 @@ module.exports = {
37
37
  if (variable && variable.defs.length === 0) {
38
38
  variable.references.forEach(ref => {
39
39
  const node = ref.identifier;
40
+ const parent = node.parent;
40
41
 
41
- if (node.parent && node.parent.type === "NewExpression") {
42
+ if (parent && parent.type === "NewExpression" && parent.callee === node) {
42
43
  context.report({
43
44
  node,
44
45
  messageId: "noNewSymbol"
@@ -32,7 +32,7 @@ module.exports = {
32
32
  return {
33
33
 
34
34
  NewExpression(node) {
35
- const wrapperObjects = ["String", "Number", "Boolean", "Math", "JSON"];
35
+ const wrapperObjects = ["String", "Number", "Boolean"];
36
36
 
37
37
  if (wrapperObjects.indexOf(node.callee.name) > -1) {
38
38
  context.report({
@@ -9,7 +9,8 @@
9
9
  // Requirements
10
10
  //------------------------------------------------------------------------------
11
11
 
12
- const { CALL, ReferenceTracker } = require("eslint-utils");
12
+ const { CALL, CONSTRUCT, ReferenceTracker } = require("eslint-utils");
13
+ const getPropertyName = require("./utils/ast-utils").getStaticPropertyName;
13
14
 
14
15
  //------------------------------------------------------------------------------
15
16
  // Helpers
@@ -17,6 +18,18 @@ const { CALL, ReferenceTracker } = require("eslint-utils");
17
18
 
18
19
  const nonCallableGlobals = ["Atomics", "JSON", "Math", "Reflect"];
19
20
 
21
+ /**
22
+ * Returns the name of the node to report
23
+ * @param {ASTNode} node A node to report
24
+ * @returns {string} name to report
25
+ */
26
+ function getReportNodeName(node) {
27
+ if (node.callee.type === "MemberExpression") {
28
+ return getPropertyName(node.callee);
29
+ }
30
+ return node.callee.name;
31
+ }
32
+
20
33
  //------------------------------------------------------------------------------
21
34
  // Rule Definition
22
35
  //------------------------------------------------------------------------------
@@ -35,7 +48,8 @@ module.exports = {
35
48
  schema: [],
36
49
 
37
50
  messages: {
38
- unexpectedCall: "'{{name}}' is not a function."
51
+ unexpectedCall: "'{{name}}' is not a function.",
52
+ unexpectedRefCall: "'{{name}}' is reference to '{{ref}}', which is not a function."
39
53
  }
40
54
  },
41
55
 
@@ -49,12 +63,17 @@ module.exports = {
49
63
 
50
64
  for (const g of nonCallableGlobals) {
51
65
  traceMap[g] = {
52
- [CALL]: true
66
+ [CALL]: true,
67
+ [CONSTRUCT]: true
53
68
  };
54
69
  }
55
70
 
56
- for (const { node } of tracker.iterateGlobalReferences(traceMap)) {
57
- context.report({ node, messageId: "unexpectedCall", data: { name: node.callee.name } });
71
+ for (const { node, path } of tracker.iterateGlobalReferences(traceMap)) {
72
+ const name = getReportNodeName(node);
73
+ const ref = path[0];
74
+ const messageId = name === ref ? "unexpectedCall" : "unexpectedRefCall";
75
+
76
+ context.report({ node, messageId, data: { name, ref } });
58
77
  }
59
78
  }
60
79
  };
@@ -10,6 +10,10 @@
10
10
 
11
11
  module.exports = {
12
12
  meta: {
13
+ deprecated: true,
14
+
15
+ replacedBy: [],
16
+
13
17
  type: "suggestion",
14
18
 
15
19
  docs: {
@@ -6,6 +6,41 @@
6
6
 
7
7
  "use strict";
8
8
 
9
+ //------------------------------------------------------------------------------
10
+ // Helpers
11
+ //------------------------------------------------------------------------------
12
+
13
+ /**
14
+ * Determines whether the given node is the update node of a `ForStatement`.
15
+ * @param {ASTNode} node The node to check.
16
+ * @returns {boolean} `true` if the node is `ForStatement` update.
17
+ */
18
+ function isForStatementUpdate(node) {
19
+ const parent = node.parent;
20
+
21
+ return parent.type === "ForStatement" && parent.update === node;
22
+ }
23
+
24
+ /**
25
+ * Determines whether the given node is considered to be a for loop "afterthought" by the logic of this rule.
26
+ * In particular, it returns `true` if the given node is either:
27
+ * - The update node of a `ForStatement`: for (;; i++) {}
28
+ * - An operand of a sequence expression that is the update node: for (;; foo(), i++) {}
29
+ * - An operand of a sequence expression that is child of another sequence expression, etc.,
30
+ * up to the sequence expression that is the update node: for (;; foo(), (bar(), (baz(), i++))) {}
31
+ * @param {ASTNode} node The node to check.
32
+ * @returns {boolean} `true` if the node is a for loop afterthought.
33
+ */
34
+ function isForLoopAfterthought(node) {
35
+ const parent = node.parent;
36
+
37
+ if (parent.type === "SequenceExpression") {
38
+ return isForLoopAfterthought(parent);
39
+ }
40
+
41
+ return isForStatementUpdate(node);
42
+ }
43
+
9
44
  //------------------------------------------------------------------------------
10
45
  // Rule Definition
11
46
  //------------------------------------------------------------------------------
@@ -42,18 +77,19 @@ module.exports = {
42
77
  create(context) {
43
78
 
44
79
  const config = context.options[0];
45
- let allowInForAfterthought = false;
80
+ let allowForLoopAfterthoughts = false;
46
81
 
47
82
  if (typeof config === "object") {
48
- allowInForAfterthought = config.allowForLoopAfterthoughts === true;
83
+ allowForLoopAfterthoughts = config.allowForLoopAfterthoughts === true;
49
84
  }
50
85
 
51
86
  return {
52
87
 
53
88
  UpdateExpression(node) {
54
- if (allowInForAfterthought && node.parent.type === "ForStatement") {
89
+ if (allowForLoopAfterthoughts && isForLoopAfterthought(node)) {
55
90
  return;
56
91
  }
92
+
57
93
  context.report({
58
94
  node,
59
95
  messageId: "unexpectedUnaryOp",
@@ -10,6 +10,10 @@
10
10
 
11
11
  module.exports = {
12
12
  meta: {
13
+ deprecated: true,
14
+
15
+ replacedBy: [],
16
+
13
17
  type: "suggestion",
14
18
 
15
19
  docs: {
@@ -10,6 +10,10 @@
10
10
 
11
11
  module.exports = {
12
12
  meta: {
13
+ deprecated: true,
14
+
15
+ replacedBy: [],
16
+
13
17
  type: "suggestion",
14
18
 
15
19
  docs: {
@@ -47,7 +47,7 @@ module.exports = {
47
47
  if (DISALLOWED_PROPS.indexOf(propName) > -1) {
48
48
  context.report({
49
49
  messageId: "prototypeBuildIn",
50
- loc: node.callee.property.loc.start,
50
+ loc: node.callee.property.loc,
51
51
  data: { prop: propName },
52
52
  node
53
53
  });
@@ -40,6 +40,10 @@ const arrayOfStringsOrObjects = {
40
40
 
41
41
  module.exports = {
42
42
  meta: {
43
+ deprecated: true,
44
+
45
+ replacedBy: [],
46
+
43
47
  type: "suggestion",
44
48
 
45
49
  docs: {
@@ -13,6 +13,10 @@
13
13
 
14
14
  module.exports = {
15
15
  meta: {
16
+ deprecated: true,
17
+
18
+ replacedBy: [],
19
+
16
20
  type: "suggestion",
17
21
 
18
22
  docs: {
@@ -53,7 +53,11 @@ module.exports = {
53
53
  const nodeExpressionEnd = sourceCode.getTokenBefore(openParen);
54
54
 
55
55
  if (openParen.loc.start.line !== nodeExpressionEnd.loc.end.line) {
56
- context.report({ node, loc: openParen.loc.start, messageId, data: { char: openParen.value } });
56
+ context.report({
57
+ node,
58
+ loc: openParen.loc,
59
+ messageId
60
+ });
57
61
  }
58
62
  }
59
63
 
@@ -71,18 +75,24 @@ module.exports = {
71
75
  },
72
76
 
73
77
  TaggedTemplateExpression(node) {
74
- if (node.tag.loc.end.line === node.quasi.loc.start.line) {
75
- return;
76
- }
77
-
78
- // handle generics type parameters on template tags
79
- const tokenBefore = sourceCode.getTokenBefore(node.quasi);
80
-
81
- if (tokenBefore.loc.end.line === node.quasi.loc.start.line) {
82
- return;
78
+ const { quasi } = node;
79
+
80
+ // handles common tags, parenthesized tags, and typescript's generic type arguments
81
+ const tokenBefore = sourceCode.getTokenBefore(quasi);
82
+
83
+ if (tokenBefore.loc.end.line !== quasi.loc.start.line) {
84
+ context.report({
85
+ node,
86
+ loc: {
87
+ start: quasi.loc.start,
88
+ end: {
89
+ line: quasi.loc.start.line,
90
+ column: quasi.loc.start.column + 1
91
+ }
92
+ },
93
+ messageId: "taggedTemplate"
94
+ });
83
95
  }
84
-
85
- context.report({ node, loc: node.loc.start, messageId: "taggedTemplate" });
86
96
  },
87
97
 
88
98
  CallExpression(node) {
@@ -105,7 +105,7 @@ module.exports = {
105
105
 
106
106
  context.report({
107
107
  node,
108
- loc: operatorToken.loc.start,
108
+ loc: operatorToken.loc,
109
109
  messageId: "unexpectedConcat"
110
110
  });
111
111
  }
@@ -71,7 +71,7 @@ module.exports = {
71
71
  context.report({
72
72
  node,
73
73
  messageId: "expectVarOnNewline",
74
- loc: current.loc.start,
74
+ loc: current.loc,
75
75
  fix: fixer => fixer.insertTextBefore(current, "\n")
76
76
  });
77
77
  }
@@ -214,12 +214,12 @@ module.exports = {
214
214
  ) {
215
215
  rightText = `${sourceCode.text.slice(operatorToken.range[1], node.right.range[0])}(${sourceCode.getText(node.right)})`;
216
216
  } else {
217
- const firstRightToken = sourceCode.getFirstToken(node.right);
217
+ const tokenAfterOperator = sourceCode.getTokenAfter(operatorToken, { includeComments: true });
218
218
  let rightTextPrefix = "";
219
219
 
220
220
  if (
221
- operatorToken.range[1] === firstRightToken.range[0] &&
222
- !astUtils.canTokensBeAdjacent({ type: "Punctuator", value: newOperator }, firstRightToken)
221
+ operatorToken.range[1] === tokenAfterOperator.range[0] &&
222
+ !astUtils.canTokensBeAdjacent({ type: "Punctuator", value: newOperator }, tokenAfterOperator)
223
223
  ) {
224
224
  rightTextPrefix = " "; // foo+=+bar -> foo= foo+ +bar
225
225
  }
@@ -172,10 +172,7 @@ module.exports = {
172
172
  // lone operator
173
173
  context.report({
174
174
  node,
175
- loc: {
176
- line: operatorToken.loc.end.line,
177
- column: operatorToken.loc.end.column
178
- },
175
+ loc: operatorToken.loc,
179
176
  messageId: "badLinebreak",
180
177
  data: {
181
178
  operator
@@ -187,10 +184,7 @@ module.exports = {
187
184
 
188
185
  context.report({
189
186
  node,
190
- loc: {
191
- line: operatorToken.loc.end.line,
192
- column: operatorToken.loc.end.column
193
- },
187
+ loc: operatorToken.loc,
194
188
  messageId: "operatorAtBeginning",
195
189
  data: {
196
190
  operator
@@ -202,10 +196,7 @@ module.exports = {
202
196
 
203
197
  context.report({
204
198
  node,
205
- loc: {
206
- line: operatorToken.loc.end.line,
207
- column: operatorToken.loc.end.column
208
- },
199
+ loc: operatorToken.loc,
209
200
  messageId: "operatorAtEnd",
210
201
  data: {
211
202
  operator
@@ -217,10 +208,7 @@ module.exports = {
217
208
 
218
209
  context.report({
219
210
  node,
220
- loc: {
221
- line: operatorToken.loc.end.line,
222
- column: operatorToken.loc.end.column
223
- },
211
+ loc: operatorToken.loc,
224
212
  messageId: "noLinebreak",
225
213
  data: {
226
214
  operator