eslint 9.0.0-alpha.0 → 9.0.0-alpha.2

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 (89) hide show
  1. package/README.md +6 -1
  2. package/conf/ecma-version.js +16 -0
  3. package/lib/cli-engine/cli-engine.js +1 -1
  4. package/lib/cli-engine/lint-result-cache.js +2 -2
  5. package/lib/cli.js +14 -16
  6. package/lib/eslint/eslint.js +7 -0
  7. package/lib/linter/apply-disable-directives.js +2 -2
  8. package/lib/linter/code-path-analysis/code-path.js +32 -30
  9. package/lib/linter/code-path-analysis/fork-context.js +1 -1
  10. package/lib/linter/config-comment-parser.js +7 -10
  11. package/lib/linter/linter.js +105 -4
  12. package/lib/linter/report-translator.js +2 -2
  13. package/lib/linter/source-code-fixer.js +1 -1
  14. package/lib/rule-tester/rule-tester.js +45 -26
  15. package/lib/rules/array-bracket-newline.js +1 -1
  16. package/lib/rules/array-bracket-spacing.js +1 -1
  17. package/lib/rules/block-scoped-var.js +1 -1
  18. package/lib/rules/callback-return.js +2 -2
  19. package/lib/rules/comma-dangle.js +1 -1
  20. package/lib/rules/comma-style.js +2 -2
  21. package/lib/rules/complexity.js +1 -1
  22. package/lib/rules/constructor-super.js +1 -1
  23. package/lib/rules/default-case.js +1 -1
  24. package/lib/rules/eol-last.js +2 -2
  25. package/lib/rules/function-paren-newline.js +2 -2
  26. package/lib/rules/indent-legacy.js +5 -5
  27. package/lib/rules/indent.js +5 -5
  28. package/lib/rules/index.js +1 -0
  29. package/lib/rules/key-spacing.js +2 -2
  30. package/lib/rules/line-comment-position.js +1 -1
  31. package/lib/rules/lines-around-directive.js +2 -2
  32. package/lib/rules/max-depth.js +1 -1
  33. package/lib/rules/max-len.js +3 -3
  34. package/lib/rules/max-lines.js +3 -3
  35. package/lib/rules/max-nested-callbacks.js +1 -1
  36. package/lib/rules/max-params.js +1 -1
  37. package/lib/rules/max-statements.js +1 -1
  38. package/lib/rules/multiline-comment-style.js +7 -7
  39. package/lib/rules/new-cap.js +1 -1
  40. package/lib/rules/newline-after-var.js +1 -1
  41. package/lib/rules/newline-before-return.js +1 -1
  42. package/lib/rules/no-constant-binary-expression.js +5 -5
  43. package/lib/rules/no-constructor-return.js +1 -1
  44. package/lib/rules/no-dupe-class-members.js +2 -2
  45. package/lib/rules/no-else-return.js +1 -1
  46. package/lib/rules/no-empty-function.js +2 -2
  47. package/lib/rules/no-fallthrough.js +1 -1
  48. package/lib/rules/no-implicit-coercion.js +51 -25
  49. package/lib/rules/no-inner-declarations.js +22 -1
  50. package/lib/rules/no-invalid-this.js +1 -1
  51. package/lib/rules/no-lone-blocks.js +2 -2
  52. package/lib/rules/no-loss-of-precision.js +1 -1
  53. package/lib/rules/no-misleading-character-class.js +174 -65
  54. package/lib/rules/no-multiple-empty-lines.js +1 -1
  55. package/lib/rules/no-restricted-globals.js +1 -1
  56. package/lib/rules/no-restricted-imports.js +54 -44
  57. package/lib/rules/no-restricted-modules.js +2 -2
  58. package/lib/rules/no-return-await.js +1 -1
  59. package/lib/rules/no-this-before-super.js +17 -4
  60. package/lib/rules/no-trailing-spaces.js +2 -3
  61. package/lib/rules/no-unneeded-ternary.js +1 -1
  62. package/lib/rules/no-unsafe-optional-chaining.js +1 -1
  63. package/lib/rules/no-unused-vars.js +6 -8
  64. package/lib/rules/no-useless-assignment.js +566 -0
  65. package/lib/rules/no-useless-backreference.js +1 -1
  66. package/lib/rules/object-curly-spacing.js +3 -3
  67. package/lib/rules/object-property-newline.js +1 -1
  68. package/lib/rules/one-var.js +5 -5
  69. package/lib/rules/padded-blocks.js +7 -7
  70. package/lib/rules/prefer-arrow-callback.js +3 -3
  71. package/lib/rules/prefer-reflect.js +1 -1
  72. package/lib/rules/prefer-regex-literals.js +1 -1
  73. package/lib/rules/prefer-template.js +1 -1
  74. package/lib/rules/radix.js +2 -2
  75. package/lib/rules/semi-style.js +1 -1
  76. package/lib/rules/sort-imports.js +1 -1
  77. package/lib/rules/sort-keys.js +1 -1
  78. package/lib/rules/sort-vars.js +1 -1
  79. package/lib/rules/space-unary-ops.js +1 -1
  80. package/lib/rules/strict.js +1 -1
  81. package/lib/rules/utils/ast-utils.js +7 -7
  82. package/lib/rules/yield-star-spacing.js +1 -1
  83. package/lib/shared/serialization.js +55 -0
  84. package/lib/source-code/source-code.js +4 -4
  85. package/lib/source-code/token-store/index.js +2 -2
  86. package/package.json +7 -7
  87. package/conf/config-schema.js +0 -93
  88. package/lib/shared/config-validator.js +0 -380
  89. package/lib/shared/relative-module-resolver.js +0 -50
@@ -188,6 +188,7 @@ function getNonEmptyOperand(node) {
188
188
  /** @type {import('../shared/types').Rule} */
189
189
  module.exports = {
190
190
  meta: {
191
+ hasSuggestions: true,
191
192
  type: "suggestion",
192
193
 
193
194
  docs: {
@@ -229,7 +230,8 @@ module.exports = {
229
230
  }],
230
231
 
231
232
  messages: {
232
- useRecommendation: "use `{{recommendation}}` instead."
233
+ implicitCoercion: "Unexpected implicit coercion encountered. Use `{{recommendation}}` instead.",
234
+ useRecommendation: "Use `{{recommendation}}` instead."
233
235
  }
234
236
  },
235
237
 
@@ -241,32 +243,54 @@ module.exports = {
241
243
  * Reports an error and autofixes the node
242
244
  * @param {ASTNode} node An ast node to report the error on.
243
245
  * @param {string} recommendation The recommended code for the issue
246
+ * @param {bool} shouldSuggest Whether this report should offer a suggestion
244
247
  * @param {bool} shouldFix Whether this report should fix the node
245
248
  * @returns {void}
246
249
  */
247
- function report(node, recommendation, shouldFix) {
250
+ function report(node, recommendation, shouldSuggest, shouldFix) {
251
+
252
+ /**
253
+ * Fix function
254
+ * @param {RuleFixer} fixer The fixer to fix.
255
+ * @returns {Fix} The fix object.
256
+ */
257
+ function fix(fixer) {
258
+ const tokenBefore = sourceCode.getTokenBefore(node);
259
+
260
+ if (
261
+ tokenBefore?.range[1] === node.range[0] &&
262
+ !astUtils.canTokensBeAdjacent(tokenBefore, recommendation)
263
+ ) {
264
+ return fixer.replaceText(node, ` ${recommendation}`);
265
+ }
266
+
267
+ return fixer.replaceText(node, recommendation);
268
+ }
269
+
248
270
  context.report({
249
271
  node,
250
- messageId: "useRecommendation",
251
- data: {
252
- recommendation
253
- },
272
+ messageId: "implicitCoercion",
273
+ data: { recommendation },
254
274
  fix(fixer) {
255
275
  if (!shouldFix) {
256
276
  return null;
257
277
  }
258
278
 
259
- const tokenBefore = sourceCode.getTokenBefore(node);
260
-
261
- if (
262
- tokenBefore &&
263
- tokenBefore.range[1] === node.range[0] &&
264
- !astUtils.canTokensBeAdjacent(tokenBefore, recommendation)
265
- ) {
266
- return fixer.replaceText(node, ` ${recommendation}`);
279
+ return fix(fixer);
280
+ },
281
+ suggest: [
282
+ {
283
+ messageId: "useRecommendation",
284
+ data: { recommendation },
285
+ fix(fixer) {
286
+ if (shouldFix || !shouldSuggest) {
287
+ return null;
288
+ }
289
+
290
+ return fix(fixer);
291
+ }
267
292
  }
268
- return fixer.replaceText(node, recommendation);
269
- }
293
+ ]
270
294
  });
271
295
  }
272
296
 
@@ -278,8 +302,10 @@ module.exports = {
278
302
  operatorAllowed = options.allow.includes("!!");
279
303
  if (!operatorAllowed && options.boolean && isDoubleLogicalNegating(node)) {
280
304
  const recommendation = `Boolean(${sourceCode.getText(node.argument.argument)})`;
305
+ const variable = astUtils.getVariableByName(sourceCode.getScope(node), "Boolean");
306
+ const booleanExists = variable?.identifiers.length === 0;
281
307
 
282
- report(node, recommendation, true);
308
+ report(node, recommendation, true, booleanExists);
283
309
  }
284
310
 
285
311
  // ~foo.indexOf(bar)
@@ -290,7 +316,7 @@ module.exports = {
290
316
  const comparison = node.argument.type === "ChainExpression" ? ">= 0" : "!== -1";
291
317
  const recommendation = `${sourceCode.getText(node.argument)} ${comparison}`;
292
318
 
293
- report(node, recommendation, false);
319
+ report(node, recommendation, false, false);
294
320
  }
295
321
 
296
322
  // +foo
@@ -298,7 +324,7 @@ module.exports = {
298
324
  if (!operatorAllowed && options.number && node.operator === "+" && !isNumeric(node.argument)) {
299
325
  const recommendation = `Number(${sourceCode.getText(node.argument)})`;
300
326
 
301
- report(node, recommendation, true);
327
+ report(node, recommendation, true, false);
302
328
  }
303
329
 
304
330
  // -(-foo)
@@ -306,7 +332,7 @@ module.exports = {
306
332
  if (!operatorAllowed && options.number && node.operator === "-" && node.argument.type === "UnaryExpression" && node.argument.operator === "-" && !isNumeric(node.argument.argument)) {
307
333
  const recommendation = `Number(${sourceCode.getText(node.argument.argument)})`;
308
334
 
309
- report(node, recommendation, false);
335
+ report(node, recommendation, true, false);
310
336
  }
311
337
  },
312
338
 
@@ -322,7 +348,7 @@ module.exports = {
322
348
  if (nonNumericOperand) {
323
349
  const recommendation = `Number(${sourceCode.getText(nonNumericOperand)})`;
324
350
 
325
- report(node, recommendation, true);
351
+ report(node, recommendation, true, false);
326
352
  }
327
353
 
328
354
  // foo - 0
@@ -330,7 +356,7 @@ module.exports = {
330
356
  if (!operatorAllowed && options.number && node.operator === "-" && node.right.type === "Literal" && node.right.value === 0 && !isNumeric(node.left)) {
331
357
  const recommendation = `Number(${sourceCode.getText(node.left)})`;
332
358
 
333
- report(node, recommendation, true);
359
+ report(node, recommendation, true, false);
334
360
  }
335
361
 
336
362
  // "" + foo
@@ -338,7 +364,7 @@ module.exports = {
338
364
  if (!operatorAllowed && options.string && isConcatWithEmptyString(node)) {
339
365
  const recommendation = `String(${sourceCode.getText(getNonEmptyOperand(node))})`;
340
366
 
341
- report(node, recommendation, true);
367
+ report(node, recommendation, true, false);
342
368
  }
343
369
  },
344
370
 
@@ -351,7 +377,7 @@ module.exports = {
351
377
  const code = sourceCode.getText(getNonEmptyOperand(node));
352
378
  const recommendation = `${code} = String(${code})`;
353
379
 
354
- report(node, recommendation, true);
380
+ report(node, recommendation, true, false);
355
381
  }
356
382
  },
357
383
 
@@ -389,7 +415,7 @@ module.exports = {
389
415
  const code = sourceCode.getText(node.expressions[0]);
390
416
  const recommendation = `String(${code})`;
391
417
 
392
- report(node, recommendation, true);
418
+ report(node, recommendation, true, false);
393
419
  }
394
420
  };
395
421
  }
@@ -56,6 +56,15 @@ module.exports = {
56
56
  schema: [
57
57
  {
58
58
  enum: ["functions", "both"]
59
+ },
60
+ {
61
+ type: "object",
62
+ properties: {
63
+ blockScopedFunctions: {
64
+ enum: ["allow", "disallow"]
65
+ }
66
+ },
67
+ additionalProperties: false
59
68
  }
60
69
  ],
61
70
 
@@ -66,6 +75,10 @@ module.exports = {
66
75
 
67
76
  create(context) {
68
77
 
78
+ const sourceCode = context.sourceCode;
79
+ const ecmaVersion = context.languageOptions.ecmaVersion;
80
+ const blockScopedFunctions = context.options[1]?.blockScopedFunctions ?? "allow";
81
+
69
82
  /**
70
83
  * Ensure that a given node is at a program or function body's root.
71
84
  * @param {ASTNode} node Declaration node to check.
@@ -97,7 +110,15 @@ module.exports = {
97
110
 
98
111
  return {
99
112
 
100
- FunctionDeclaration: check,
113
+ FunctionDeclaration(node) {
114
+ const isInStrictCode = sourceCode.getScope(node).upper.isStrict;
115
+
116
+ if (blockScopedFunctions === "allow" && ecmaVersion >= 2015 && isInStrictCode) {
117
+ return;
118
+ }
119
+
120
+ check(node);
121
+ },
101
122
  VariableDeclaration(node) {
102
123
  if (context.options[0] === "both" && node.kind === "var") {
103
124
  check(node);
@@ -74,7 +74,7 @@ module.exports = {
74
74
  * an object which has a flag that whether or not `this` keyword is valid.
75
75
  */
76
76
  stack.getCurrent = function() {
77
- const current = this[this.length - 1];
77
+ const current = this.at(-1);
78
78
 
79
79
  if (!current.init) {
80
80
  current.init = true;
@@ -78,7 +78,7 @@ module.exports = {
78
78
 
79
79
  const block = node.parent;
80
80
 
81
- if (loneBlocks[loneBlocks.length - 1] === block) {
81
+ if (loneBlocks.at(-1) === block) {
82
82
  loneBlocks.pop();
83
83
  }
84
84
  }
@@ -101,7 +101,7 @@ module.exports = {
101
101
  }
102
102
  },
103
103
  "BlockStatement:exit"(node) {
104
- if (loneBlocks.length > 0 && loneBlocks[loneBlocks.length - 1] === node) {
104
+ if (loneBlocks.length > 0 && loneBlocks.at(-1) === node) {
105
105
  loneBlocks.pop();
106
106
  report(node);
107
107
  } else if (
@@ -64,7 +64,7 @@ module.exports = {
64
64
  */
65
65
  function notBaseTenLosesPrecision(node) {
66
66
  const rawString = getRaw(node).toUpperCase();
67
- let base = 0;
67
+ let base;
68
68
 
69
69
  if (rawString.startsWith("0B")) {
70
70
  base = 2;
@@ -62,7 +62,6 @@ function *iterateCharacterSequence(nodes) {
62
62
  }
63
63
  }
64
64
 
65
-
66
65
  /**
67
66
  * Checks whether the given character node is a Unicode code point escape or not.
68
67
  * @param {Character} char the character node to check.
@@ -73,80 +72,126 @@ function isUnicodeCodePointEscape(char) {
73
72
  }
74
73
 
75
74
  /**
76
- * Each function returns `true` if it detects that kind of problem.
77
- * @type {Record<string, (chars: Character[]) => boolean>}
75
+ * Each function returns matched characters if it detects that kind of problem.
76
+ * @type {Record<string, (chars: Character[]) => IterableIterator<Character[]>>}
78
77
  */
79
- const hasCharacterSequence = {
80
- surrogatePairWithoutUFlag(chars) {
81
- return chars.some((c, i) => {
82
- if (i === 0) {
83
- return false;
78
+ const findCharacterSequences = {
79
+ *surrogatePairWithoutUFlag(chars) {
80
+ for (const [index, char] of chars.entries()) {
81
+ if (index === 0) {
82
+ continue;
83
+ }
84
+ const previous = chars[index - 1];
85
+
86
+ if (
87
+ isSurrogatePair(previous.value, char.value) &&
88
+ !isUnicodeCodePointEscape(previous) &&
89
+ !isUnicodeCodePointEscape(char)
90
+ ) {
91
+ yield [previous, char];
84
92
  }
85
- const c1 = chars[i - 1];
86
-
87
- return (
88
- isSurrogatePair(c1.value, c.value) &&
89
- !isUnicodeCodePointEscape(c1) &&
90
- !isUnicodeCodePointEscape(c)
91
- );
92
- });
93
+ }
93
94
  },
94
95
 
95
- surrogatePair(chars) {
96
- return chars.some((c, i) => {
97
- if (i === 0) {
98
- return false;
96
+ *surrogatePair(chars) {
97
+ for (const [index, char] of chars.entries()) {
98
+ if (index === 0) {
99
+ continue;
99
100
  }
100
- const c1 = chars[i - 1];
101
+ const previous = chars[index - 1];
101
102
 
102
- return (
103
- isSurrogatePair(c1.value, c.value) &&
103
+ if (
104
+ isSurrogatePair(previous.value, char.value) &&
104
105
  (
105
- isUnicodeCodePointEscape(c1) ||
106
- isUnicodeCodePointEscape(c)
106
+ isUnicodeCodePointEscape(previous) ||
107
+ isUnicodeCodePointEscape(char)
107
108
  )
108
- );
109
- });
109
+ ) {
110
+ yield [previous, char];
111
+ }
112
+ }
110
113
  },
111
114
 
112
- combiningClass(chars) {
113
- return chars.some((c, i) => (
114
- i !== 0 &&
115
- isCombiningCharacter(c.value) &&
116
- !isCombiningCharacter(chars[i - 1].value)
117
- ));
115
+ *combiningClass(chars) {
116
+ for (const [index, char] of chars.entries()) {
117
+ if (index === 0) {
118
+ continue;
119
+ }
120
+ const previous = chars[index - 1];
121
+
122
+ if (
123
+ isCombiningCharacter(char.value) &&
124
+ !isCombiningCharacter(previous.value)
125
+ ) {
126
+ yield [previous, char];
127
+ }
128
+ }
118
129
  },
119
130
 
120
- emojiModifier(chars) {
121
- return chars.some((c, i) => (
122
- i !== 0 &&
123
- isEmojiModifier(c.value) &&
124
- !isEmojiModifier(chars[i - 1].value)
125
- ));
131
+ *emojiModifier(chars) {
132
+ for (const [index, char] of chars.entries()) {
133
+ if (index === 0) {
134
+ continue;
135
+ }
136
+ const previous = chars[index - 1];
137
+
138
+ if (
139
+ isEmojiModifier(char.value) &&
140
+ !isEmojiModifier(previous.value)
141
+ ) {
142
+ yield [previous, char];
143
+ }
144
+ }
126
145
  },
127
146
 
128
- regionalIndicatorSymbol(chars) {
129
- return chars.some((c, i) => (
130
- i !== 0 &&
131
- isRegionalIndicatorSymbol(c.value) &&
132
- isRegionalIndicatorSymbol(chars[i - 1].value)
133
- ));
147
+ *regionalIndicatorSymbol(chars) {
148
+ for (const [index, char] of chars.entries()) {
149
+ if (index === 0) {
150
+ continue;
151
+ }
152
+ const previous = chars[index - 1];
153
+
154
+ if (
155
+ isRegionalIndicatorSymbol(char.value) &&
156
+ isRegionalIndicatorSymbol(previous.value)
157
+ ) {
158
+ yield [previous, char];
159
+ }
160
+ }
134
161
  },
135
162
 
136
- zwj(chars) {
137
- const lastIndex = chars.length - 1;
163
+ *zwj(chars) {
164
+ let sequence = null;
138
165
 
139
- return chars.some((c, i) => (
140
- i !== 0 &&
141
- i !== lastIndex &&
142
- c.value === 0x200d &&
143
- chars[i - 1].value !== 0x200d &&
144
- chars[i + 1].value !== 0x200d
145
- ));
166
+ for (const [index, char] of chars.entries()) {
167
+ if (index === 0 || index === chars.length - 1) {
168
+ continue;
169
+ }
170
+ if (
171
+ char.value === 0x200d &&
172
+ chars[index - 1].value !== 0x200d &&
173
+ chars[index + 1].value !== 0x200d
174
+ ) {
175
+ if (sequence) {
176
+ if (sequence.at(-1) === chars[index - 1]) {
177
+ sequence.push(char, chars[index + 1]); // append to the sequence
178
+ } else {
179
+ yield sequence;
180
+ sequence = chars.slice(index - 1, index + 2);
181
+ }
182
+ } else {
183
+ sequence = chars.slice(index - 1, index + 2);
184
+ }
185
+ }
186
+ }
187
+
188
+ if (sequence) {
189
+ yield sequence;
190
+ }
146
191
  }
147
192
  };
148
193
 
149
- const kinds = Object.keys(hasCharacterSequence);
194
+ const kinds = Object.keys(findCharacterSequences);
150
195
 
151
196
  //------------------------------------------------------------------------------
152
197
  // Rule Definition
@@ -181,6 +226,62 @@ module.exports = {
181
226
  const sourceCode = context.sourceCode;
182
227
  const parser = new RegExpParser();
183
228
 
229
+ /**
230
+ * Generates a granular loc for context.report, if directly calculable.
231
+ * @param {Character[]} chars Individual characters being reported on.
232
+ * @param {Node} node Parent string node to report within.
233
+ * @returns {Object | null} Granular loc for context.report, if directly calculable.
234
+ * @see https://github.com/eslint/eslint/pull/17515
235
+ */
236
+ function generateReportLocation(chars, node) {
237
+
238
+ // Limit to to literals and expression-less templates with raw values === their value.
239
+ switch (node.type) {
240
+ case "TemplateLiteral":
241
+ if (node.expressions.length || sourceCode.getText(node).slice(1, -1) !== node.quasis[0].value.cooked) {
242
+ return null;
243
+ }
244
+ break;
245
+
246
+ case "Literal":
247
+ if (typeof node.value === "string" && node.value !== node.raw.slice(1, -1)) {
248
+ return null;
249
+ }
250
+ break;
251
+
252
+ default:
253
+ return null;
254
+ }
255
+
256
+ return {
257
+ start: sourceCode.getLocFromIndex(node.range[0] + 1 + chars[0].start),
258
+ end: sourceCode.getLocFromIndex(node.range[0] + 1 + chars.at(-1).end)
259
+ };
260
+ }
261
+
262
+ /**
263
+ * Finds the report loc(s) for a range of matches.
264
+ * @param {Character[][]} matches Characters that should trigger a report.
265
+ * @param {Node} node The node to report.
266
+ * @returns {Object | null} Node loc(s) for context.report.
267
+ */
268
+ function getNodeReportLocations(matches, node) {
269
+ const locs = [];
270
+
271
+ for (const chars of matches) {
272
+ const loc = generateReportLocation(chars, node);
273
+
274
+ // If a report can't match to a range, don't report any others
275
+ if (!loc) {
276
+ return [node.loc];
277
+ }
278
+
279
+ locs.push(loc);
280
+ }
281
+
282
+ return locs;
283
+ }
284
+
184
285
  /**
185
286
  * Verify a given regular expression.
186
287
  * @param {Node} node The node to report.
@@ -208,21 +309,24 @@ module.exports = {
208
309
  return;
209
310
  }
210
311
 
211
- const foundKinds = new Set();
312
+ const foundKindMatches = new Map();
212
313
 
213
314
  visitRegExpAST(patternNode, {
214
315
  onCharacterClassEnter(ccNode) {
215
316
  for (const chars of iterateCharacterSequence(ccNode.elements)) {
216
317
  for (const kind of kinds) {
217
- if (hasCharacterSequence[kind](chars)) {
218
- foundKinds.add(kind);
318
+ if (foundKindMatches.has(kind)) {
319
+ foundKindMatches.get(kind).push(...findCharacterSequences[kind](chars));
320
+ } else {
321
+ foundKindMatches.set(kind, [...findCharacterSequences[kind](chars)]);
219
322
  }
323
+
220
324
  }
221
325
  }
222
326
  }
223
327
  });
224
328
 
225
- for (const kind of foundKinds) {
329
+ for (const [kind, matches] of foundKindMatches) {
226
330
  let suggest;
227
331
 
228
332
  if (kind === "surrogatePairWithoutUFlag") {
@@ -232,11 +336,16 @@ module.exports = {
232
336
  }];
233
337
  }
234
338
 
235
- context.report({
236
- node,
237
- messageId: kind,
238
- suggest
239
- });
339
+ const locs = getNodeReportLocations(matches, node);
340
+
341
+ for (const loc of locs) {
342
+ context.report({
343
+ node,
344
+ loc,
345
+ messageId: kind,
346
+ suggest
347
+ });
348
+ }
240
349
  }
241
350
  }
242
351
 
@@ -267,7 +376,7 @@ module.exports = {
267
376
  const flags = getStringIfConstant(flagsNode, scope);
268
377
 
269
378
  if (typeof pattern === "string") {
270
- verify(refNode, pattern, flags || "", fixer => {
379
+ verify(patternNode, pattern, flags || "", fixer => {
271
380
 
272
381
  if (!isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, pattern)) {
273
382
  return null;
@@ -70,7 +70,7 @@ module.exports = {
70
70
  const sourceCode = context.sourceCode;
71
71
 
72
72
  // Swallow the final newline, as some editors add it automatically and we don't want it to cause an issue
73
- const allLines = sourceCode.lines[sourceCode.lines.length - 1] === "" ? sourceCode.lines.slice(0, -1) : sourceCode.lines;
73
+ const allLines = sourceCode.lines.at(-1) === "" ? sourceCode.lines.slice(0, -1) : sourceCode.lines;
74
74
  const templateLiteralLines = new Set();
75
75
 
76
76
  //--------------------------------------------------------------------------
@@ -97,7 +97,7 @@ module.exports = {
97
97
  * @private
98
98
  */
99
99
  function isRestricted(name) {
100
- return Object.prototype.hasOwnProperty.call(restrictedGlobalMessages, name);
100
+ return Object.hasOwn(restrictedGlobalMessages, name);
101
101
  }
102
102
 
103
103
  return {