eslint 8.57.1 → 9.39.1

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 (458) hide show
  1. package/README.md +165 -115
  2. package/bin/eslint.js +112 -89
  3. package/conf/default-cli-options.js +22 -22
  4. package/conf/ecma-version.js +16 -0
  5. package/conf/globals.js +109 -94
  6. package/conf/replacements.json +24 -20
  7. package/conf/rule-type-list.json +89 -26
  8. package/lib/api.js +16 -20
  9. package/lib/cli-engine/cli-engine.js +841 -810
  10. package/lib/cli-engine/file-enumerator.js +384 -390
  11. package/lib/cli-engine/formatters/formatters-meta.json +17 -45
  12. package/lib/cli-engine/formatters/html.js +110 -102
  13. package/lib/cli-engine/formatters/json-with-metadata.js +5 -5
  14. package/lib/cli-engine/formatters/json.js +2 -2
  15. package/lib/cli-engine/formatters/stylish.js +97 -76
  16. package/lib/cli-engine/hash.js +1 -1
  17. package/lib/cli-engine/index.js +1 -1
  18. package/lib/cli-engine/lint-result-cache.js +165 -148
  19. package/lib/cli-engine/load-rules.js +17 -17
  20. package/lib/cli.js +481 -399
  21. package/lib/config/config-loader.js +816 -0
  22. package/lib/config/config.js +674 -0
  23. package/lib/config/default-config.js +57 -46
  24. package/lib/config/flat-config-array.js +170 -333
  25. package/lib/config/flat-config-schema.js +389 -389
  26. package/lib/config-api.js +12 -0
  27. package/lib/eslint/eslint-helpers.js +1196 -663
  28. package/lib/eslint/eslint.js +1262 -607
  29. package/lib/eslint/index.js +3 -3
  30. package/lib/eslint/legacy-eslint.js +786 -0
  31. package/lib/eslint/worker.js +173 -0
  32. package/lib/languages/js/index.js +336 -0
  33. package/lib/languages/js/source-code/index.js +7 -0
  34. package/lib/languages/js/source-code/source-code.js +1364 -0
  35. package/lib/languages/js/source-code/token-store/backward-token-comment-cursor.js +61 -0
  36. package/lib/languages/js/source-code/token-store/backward-token-cursor.js +57 -0
  37. package/lib/{source-code → languages/js/source-code}/token-store/cursor.js +36 -36
  38. package/lib/languages/js/source-code/token-store/cursors.js +120 -0
  39. package/lib/{source-code → languages/js/source-code}/token-store/decorative-cursor.js +17 -18
  40. package/lib/{source-code → languages/js/source-code}/token-store/filter-cursor.js +19 -20
  41. package/lib/languages/js/source-code/token-store/forward-token-comment-cursor.js +65 -0
  42. package/lib/languages/js/source-code/token-store/forward-token-cursor.js +62 -0
  43. package/lib/languages/js/source-code/token-store/index.js +721 -0
  44. package/lib/{source-code → languages/js/source-code}/token-store/limit-cursor.js +17 -18
  45. package/lib/languages/js/source-code/token-store/padded-token-cursor.js +45 -0
  46. package/lib/{source-code → languages/js/source-code}/token-store/skip-cursor.js +19 -20
  47. package/lib/languages/js/source-code/token-store/utils.js +110 -0
  48. package/lib/languages/js/validate-language-options.js +196 -0
  49. package/lib/linter/apply-disable-directives.js +490 -371
  50. package/lib/linter/code-path-analysis/code-path-analyzer.js +650 -674
  51. package/lib/linter/code-path-analysis/code-path-segment.js +215 -216
  52. package/lib/linter/code-path-analysis/code-path-state.js +2118 -2096
  53. package/lib/linter/code-path-analysis/code-path.js +307 -317
  54. package/lib/linter/code-path-analysis/debug-helpers.js +183 -163
  55. package/lib/linter/code-path-analysis/fork-context.js +297 -272
  56. package/lib/linter/code-path-analysis/id-generator.js +22 -23
  57. package/lib/linter/esquery.js +332 -0
  58. package/lib/linter/file-context.js +144 -0
  59. package/lib/linter/file-report.js +608 -0
  60. package/lib/linter/index.js +3 -5
  61. package/lib/linter/interpolate.js +38 -16
  62. package/lib/linter/linter.js +2328 -1785
  63. package/lib/linter/rule-fixer.js +136 -107
  64. package/lib/linter/rules.js +37 -46
  65. package/lib/linter/source-code-fixer.js +96 -94
  66. package/lib/linter/source-code-traverser.js +333 -0
  67. package/lib/linter/source-code-visitor.js +81 -0
  68. package/lib/linter/timing.js +145 -97
  69. package/lib/linter/vfile.js +115 -0
  70. package/lib/options.js +464 -326
  71. package/lib/rule-tester/index.js +3 -1
  72. package/lib/rule-tester/rule-tester.js +1371 -998
  73. package/lib/rules/accessor-pairs.js +333 -259
  74. package/lib/rules/array-bracket-newline.js +250 -220
  75. package/lib/rules/array-bracket-spacing.js +286 -229
  76. package/lib/rules/array-callback-return.js +401 -354
  77. package/lib/rules/array-element-newline.js +358 -295
  78. package/lib/rules/arrow-body-style.js +400 -278
  79. package/lib/rules/arrow-parens.js +206 -155
  80. package/lib/rules/arrow-spacing.js +169 -145
  81. package/lib/rules/block-scoped-var.js +125 -123
  82. package/lib/rules/block-spacing.js +186 -158
  83. package/lib/rules/brace-style.js +262 -181
  84. package/lib/rules/callback-return.js +203 -174
  85. package/lib/rules/camelcase.js +403 -380
  86. package/lib/rules/capitalized-comments.js +253 -228
  87. package/lib/rules/class-methods-use-this.js +231 -168
  88. package/lib/rules/comma-dangle.js +379 -328
  89. package/lib/rules/comma-spacing.js +193 -177
  90. package/lib/rules/comma-style.js +375 -298
  91. package/lib/rules/complexity.js +180 -144
  92. package/lib/rules/computed-property-spacing.js +236 -193
  93. package/lib/rules/consistent-return.js +181 -170
  94. package/lib/rules/consistent-this.js +167 -141
  95. package/lib/rules/constructor-super.js +418 -411
  96. package/lib/rules/curly.js +407 -468
  97. package/lib/rules/default-case-last.js +39 -32
  98. package/lib/rules/default-case.js +89 -83
  99. package/lib/rules/default-param-last.js +69 -53
  100. package/lib/rules/dot-location.js +122 -92
  101. package/lib/rules/dot-notation.js +193 -153
  102. package/lib/rules/eol-last.js +122 -102
  103. package/lib/rules/eqeqeq.js +191 -155
  104. package/lib/rules/for-direction.js +150 -122
  105. package/lib/rules/func-call-spacing.js +261 -213
  106. package/lib/rules/func-name-matching.js +294 -209
  107. package/lib/rules/func-names.js +165 -164
  108. package/lib/rules/func-style.js +209 -86
  109. package/lib/rules/function-call-argument-newline.js +152 -111
  110. package/lib/rules/function-paren-newline.js +349 -273
  111. package/lib/rules/generator-star-spacing.js +229 -192
  112. package/lib/rules/getter-return.js +208 -170
  113. package/lib/rules/global-require.js +85 -58
  114. package/lib/rules/grouped-accessor-pairs.js +201 -148
  115. package/lib/rules/guard-for-in.js +72 -63
  116. package/lib/rules/handle-callback-err.js +108 -87
  117. package/lib/rules/id-blacklist.js +182 -187
  118. package/lib/rules/id-denylist.js +174 -179
  119. package/lib/rules/id-length.js +197 -157
  120. package/lib/rules/id-match.js +350 -286
  121. package/lib/rules/implicit-arrow-linebreak.js +102 -61
  122. package/lib/rules/indent-legacy.js +1345 -1102
  123. package/lib/rules/indent.js +2272 -1741
  124. package/lib/rules/index.js +320 -294
  125. package/lib/rules/init-declarations.js +139 -106
  126. package/lib/rules/jsx-quotes.js +94 -64
  127. package/lib/rules/key-spacing.js +750 -615
  128. package/lib/rules/keyword-spacing.js +648 -587
  129. package/lib/rules/line-comment-position.js +143 -108
  130. package/lib/rules/linebreak-style.js +115 -88
  131. package/lib/rules/lines-around-comment.js +540 -430
  132. package/lib/rules/lines-around-directive.js +233 -185
  133. package/lib/rules/lines-between-class-members.js +305 -216
  134. package/lib/rules/logical-assignment-operators.js +582 -398
  135. package/lib/rules/max-classes-per-file.js +69 -68
  136. package/lib/rules/max-depth.js +146 -143
  137. package/lib/rules/max-len.js +473 -416
  138. package/lib/rules/max-lines-per-function.js +201 -176
  139. package/lib/rules/max-lines.js +158 -162
  140. package/lib/rules/max-nested-callbacks.js +102 -104
  141. package/lib/rules/max-params.js +102 -75
  142. package/lib/rules/max-statements-per-line.js +205 -180
  143. package/lib/rules/max-statements.js +168 -164
  144. package/lib/rules/multiline-comment-style.js +638 -460
  145. package/lib/rules/multiline-ternary.js +241 -158
  146. package/lib/rules/new-cap.js +233 -232
  147. package/lib/rules/new-parens.js +88 -61
  148. package/lib/rules/newline-after-var.js +287 -233
  149. package/lib/rules/newline-before-return.js +229 -204
  150. package/lib/rules/newline-per-chained-call.js +142 -109
  151. package/lib/rules/no-alert.js +90 -79
  152. package/lib/rules/no-array-constructor.js +175 -113
  153. package/lib/rules/no-async-promise-executor.js +30 -24
  154. package/lib/rules/no-await-in-loop.js +79 -70
  155. package/lib/rules/no-bitwise.js +113 -87
  156. package/lib/rules/no-buffer-constructor.js +61 -37
  157. package/lib/rules/no-caller.js +39 -33
  158. package/lib/rules/no-case-declarations.js +61 -45
  159. package/lib/rules/no-catch-shadow.js +76 -62
  160. package/lib/rules/no-class-assign.js +51 -48
  161. package/lib/rules/no-compare-neg-zero.js +62 -48
  162. package/lib/rules/no-cond-assign.js +148 -132
  163. package/lib/rules/no-confusing-arrow.js +98 -63
  164. package/lib/rules/no-console.js +202 -188
  165. package/lib/rules/no-const-assign.js +58 -41
  166. package/lib/rules/no-constant-binary-expression.js +501 -407
  167. package/lib/rules/no-constant-condition.js +158 -131
  168. package/lib/rules/no-constructor-return.js +49 -49
  169. package/lib/rules/no-continue.js +25 -26
  170. package/lib/rules/no-control-regex.js +125 -121
  171. package/lib/rules/no-debugger.js +28 -30
  172. package/lib/rules/no-delete-var.js +29 -29
  173. package/lib/rules/no-div-regex.js +47 -40
  174. package/lib/rules/no-dupe-args.js +79 -69
  175. package/lib/rules/no-dupe-class-members.js +102 -89
  176. package/lib/rules/no-dupe-else-if.js +100 -77
  177. package/lib/rules/no-dupe-keys.js +133 -110
  178. package/lib/rules/no-duplicate-case.js +50 -43
  179. package/lib/rules/no-duplicate-imports.js +266 -188
  180. package/lib/rules/no-else-return.js +430 -385
  181. package/lib/rules/no-empty-character-class.js +57 -50
  182. package/lib/rules/no-empty-function.js +197 -128
  183. package/lib/rules/no-empty-pattern.js +63 -56
  184. package/lib/rules/no-empty-static-block.js +61 -35
  185. package/lib/rules/no-empty.js +135 -85
  186. package/lib/rules/no-eq-null.js +37 -32
  187. package/lib/rules/no-eval.js +258 -249
  188. package/lib/rules/no-ex-assign.js +42 -39
  189. package/lib/rules/no-extend-native.js +161 -160
  190. package/lib/rules/no-extra-bind.js +201 -190
  191. package/lib/rules/no-extra-boolean-cast.js +398 -295
  192. package/lib/rules/no-extra-label.js +150 -130
  193. package/lib/rules/no-extra-parens.js +1654 -1307
  194. package/lib/rules/no-extra-semi.js +146 -126
  195. package/lib/rules/no-fallthrough.js +200 -136
  196. package/lib/rules/no-floating-decimal.js +74 -48
  197. package/lib/rules/no-func-assign.js +54 -55
  198. package/lib/rules/no-global-assign.js +78 -72
  199. package/lib/rules/no-implicit-coercion.js +350 -262
  200. package/lib/rules/no-implicit-globals.js +174 -133
  201. package/lib/rules/no-implied-eval.js +150 -112
  202. package/lib/rules/no-import-assign.js +145 -159
  203. package/lib/rules/no-inline-comments.js +101 -96
  204. package/lib/rules/no-inner-declarations.js +115 -78
  205. package/lib/rules/no-invalid-regexp.js +223 -174
  206. package/lib/rules/no-invalid-this.js +145 -117
  207. package/lib/rules/no-irregular-whitespace.js +266 -250
  208. package/lib/rules/no-iterator.js +29 -33
  209. package/lib/rules/no-label-var.js +59 -61
  210. package/lib/rules/no-labels.js +138 -131
  211. package/lib/rules/no-lone-blocks.js +127 -123
  212. package/lib/rules/no-lonely-if.js +105 -67
  213. package/lib/rules/no-loop-func.js +245 -184
  214. package/lib/rules/no-loss-of-precision.js +236 -201
  215. package/lib/rules/no-magic-numbers.js +339 -217
  216. package/lib/rules/no-misleading-character-class.js +548 -253
  217. package/lib/rules/no-mixed-operators.js +188 -164
  218. package/lib/rules/no-mixed-requires.js +253 -224
  219. package/lib/rules/no-mixed-spaces-and-tabs.js +135 -103
  220. package/lib/rules/no-multi-assign.js +46 -47
  221. package/lib/rules/no-multi-spaces.js +163 -125
  222. package/lib/rules/no-multi-str.js +42 -40
  223. package/lib/rules/no-multiple-empty-lines.js +196 -140
  224. package/lib/rules/no-native-reassign.js +90 -74
  225. package/lib/rules/no-negated-condition.js +79 -74
  226. package/lib/rules/no-negated-in-lhs.js +45 -32
  227. package/lib/rules/no-nested-ternary.js +33 -31
  228. package/lib/rules/no-new-func.js +71 -62
  229. package/lib/rules/no-new-native-nonconstructor.js +43 -39
  230. package/lib/rules/no-new-object.js +48 -39
  231. package/lib/rules/no-new-require.js +48 -31
  232. package/lib/rules/no-new-symbol.js +61 -43
  233. package/lib/rules/no-new-wrappers.js +43 -41
  234. package/lib/rules/no-new.js +28 -29
  235. package/lib/rules/no-nonoctal-decimal-escape.js +149 -121
  236. package/lib/rules/no-obj-calls.js +66 -53
  237. package/lib/rules/no-object-constructor.js +104 -97
  238. package/lib/rules/no-octal-escape.js +40 -43
  239. package/lib/rules/no-octal.js +29 -32
  240. package/lib/rules/no-param-reassign.js +236 -218
  241. package/lib/rules/no-path-concat.js +66 -51
  242. package/lib/rules/no-plusplus.js +60 -63
  243. package/lib/rules/no-process-env.js +49 -32
  244. package/lib/rules/no-process-exit.js +48 -28
  245. package/lib/rules/no-promise-executor-return.js +205 -204
  246. package/lib/rules/no-proto.js +26 -29
  247. package/lib/rules/no-prototype-builtins.js +146 -124
  248. package/lib/rules/no-redeclare.js +154 -155
  249. package/lib/rules/no-regex-spaces.js +183 -161
  250. package/lib/rules/no-restricted-exports.js +208 -174
  251. package/lib/rules/no-restricted-globals.js +254 -112
  252. package/lib/rules/no-restricted-imports.js +824 -384
  253. package/lib/rules/no-restricted-modules.js +222 -186
  254. package/lib/rules/no-restricted-properties.js +218 -153
  255. package/lib/rules/no-restricted-syntax.js +56 -52
  256. package/lib/rules/no-return-assign.js +56 -49
  257. package/lib/rules/no-return-await.js +147 -120
  258. package/lib/rules/no-script-url.js +53 -46
  259. package/lib/rules/no-self-assign.js +148 -145
  260. package/lib/rules/no-self-compare.js +63 -46
  261. package/lib/rules/no-sequences.js +135 -115
  262. package/lib/rules/no-setter-return.js +176 -178
  263. package/lib/rules/no-shadow-restricted-names.js +84 -36
  264. package/lib/rules/no-shadow.js +598 -310
  265. package/lib/rules/no-spaced-func.js +82 -60
  266. package/lib/rules/no-sparse-arrays.js +46 -28
  267. package/lib/rules/no-sync.js +61 -44
  268. package/lib/rules/no-tabs.js +83 -54
  269. package/lib/rules/no-template-curly-in-string.js +33 -32
  270. package/lib/rules/no-ternary.js +25 -28
  271. package/lib/rules/no-this-before-super.js +332 -298
  272. package/lib/rules/no-throw-literal.js +31 -36
  273. package/lib/rules/no-trailing-spaces.js +208 -174
  274. package/lib/rules/no-unassigned-vars.js +80 -0
  275. package/lib/rules/no-undef-init.js +86 -60
  276. package/lib/rules/no-undef.js +52 -47
  277. package/lib/rules/no-undefined.js +73 -74
  278. package/lib/rules/no-underscore-dangle.js +370 -322
  279. package/lib/rules/no-unexpected-multiline.js +112 -102
  280. package/lib/rules/no-unmodified-loop-condition.js +254 -254
  281. package/lib/rules/no-unneeded-ternary.js +212 -146
  282. package/lib/rules/no-unreachable-loop.js +145 -140
  283. package/lib/rules/no-unreachable.js +255 -248
  284. package/lib/rules/no-unsafe-finally.js +93 -85
  285. package/lib/rules/no-unsafe-negation.js +105 -81
  286. package/lib/rules/no-unsafe-optional-chaining.js +193 -177
  287. package/lib/rules/no-unused-expressions.js +199 -158
  288. package/lib/rules/no-unused-labels.js +139 -124
  289. package/lib/rules/no-unused-private-class-members.js +206 -182
  290. package/lib/rules/no-unused-vars.js +1708 -687
  291. package/lib/rules/no-use-before-define.js +327 -229
  292. package/lib/rules/no-useless-assignment.js +654 -0
  293. package/lib/rules/no-useless-backreference.js +212 -143
  294. package/lib/rules/no-useless-call.js +58 -53
  295. package/lib/rules/no-useless-catch.js +40 -40
  296. package/lib/rules/no-useless-computed-key.js +144 -108
  297. package/lib/rules/no-useless-concat.js +65 -59
  298. package/lib/rules/no-useless-constructor.js +160 -97
  299. package/lib/rules/no-useless-escape.js +364 -291
  300. package/lib/rules/no-useless-rename.js +183 -153
  301. package/lib/rules/no-useless-return.js +344 -307
  302. package/lib/rules/no-var.js +245 -212
  303. package/lib/rules/no-void.js +51 -46
  304. package/lib/rules/no-warning-comments.js +191 -183
  305. package/lib/rules/no-whitespace-before-property.js +131 -97
  306. package/lib/rules/no-with.js +24 -26
  307. package/lib/rules/nonblock-statement-body-position.js +149 -112
  308. package/lib/rules/object-curly-newline.js +306 -247
  309. package/lib/rules/object-curly-spacing.js +360 -296
  310. package/lib/rules/object-property-newline.js +137 -88
  311. package/lib/rules/object-shorthand.js +632 -500
  312. package/lib/rules/one-var-declaration-per-line.js +104 -82
  313. package/lib/rules/one-var.js +686 -536
  314. package/lib/rules/operator-assignment.js +219 -158
  315. package/lib/rules/operator-linebreak.js +295 -233
  316. package/lib/rules/padded-blocks.js +346 -290
  317. package/lib/rules/padding-line-between-statements.js +443 -421
  318. package/lib/rules/prefer-arrow-callback.js +371 -315
  319. package/lib/rules/prefer-const.js +418 -373
  320. package/lib/rules/prefer-destructuring.js +309 -278
  321. package/lib/rules/prefer-exponentiation-operator.js +176 -132
  322. package/lib/rules/prefer-named-capture-group.js +160 -141
  323. package/lib/rules/prefer-numeric-literals.js +121 -112
  324. package/lib/rules/prefer-object-has-own.js +116 -82
  325. package/lib/rules/prefer-object-spread.js +214 -193
  326. package/lib/rules/prefer-promise-reject-errors.js +140 -118
  327. package/lib/rules/prefer-reflect.js +126 -103
  328. package/lib/rules/prefer-regex-literals.js +561 -463
  329. package/lib/rules/prefer-rest-params.js +79 -80
  330. package/lib/rules/prefer-spread.js +47 -43
  331. package/lib/rules/prefer-template.js +266 -194
  332. package/lib/rules/preserve-caught-error.js +535 -0
  333. package/lib/rules/quote-props.js +373 -289
  334. package/lib/rules/quotes.js +374 -308
  335. package/lib/rules/radix.js +152 -134
  336. package/lib/rules/require-atomic-updates.js +316 -282
  337. package/lib/rules/require-await.js +153 -82
  338. package/lib/rules/require-unicode-regexp.js +296 -108
  339. package/lib/rules/require-yield.js +53 -54
  340. package/lib/rules/rest-spread-spacing.js +128 -98
  341. package/lib/rules/semi-spacing.js +281 -232
  342. package/lib/rules/semi-style.js +176 -116
  343. package/lib/rules/semi.js +456 -418
  344. package/lib/rules/sort-imports.js +307 -229
  345. package/lib/rules/sort-keys.js +219 -181
  346. package/lib/rules/sort-vars.js +127 -91
  347. package/lib/rules/space-before-blocks.js +199 -171
  348. package/lib/rules/space-before-function-paren.js +186 -148
  349. package/lib/rules/space-in-parens.js +359 -270
  350. package/lib/rules/space-infix-ops.js +237 -183
  351. package/lib/rules/space-unary-ops.js +356 -280
  352. package/lib/rules/spaced-comment.js +363 -301
  353. package/lib/rules/strict.js +266 -229
  354. package/lib/rules/switch-colon-spacing.js +130 -104
  355. package/lib/rules/symbol-description.js +45 -48
  356. package/lib/rules/template-curly-spacing.js +148 -124
  357. package/lib/rules/template-tag-spacing.js +98 -70
  358. package/lib/rules/unicode-bom.js +54 -54
  359. package/lib/rules/use-isnan.js +237 -110
  360. package/lib/rules/utils/ast-utils.js +2139 -1688
  361. package/lib/rules/utils/char-source.js +247 -0
  362. package/lib/rules/utils/fix-tracker.js +99 -88
  363. package/lib/rules/utils/keywords.js +59 -59
  364. package/lib/rules/utils/lazy-loading-rule-map.js +81 -78
  365. package/lib/rules/utils/regular-expressions.js +35 -19
  366. package/lib/rules/utils/unicode/index.js +9 -4
  367. package/lib/rules/utils/unicode/is-combining-character.js +1 -1
  368. package/lib/rules/utils/unicode/is-emoji-modifier.js +1 -1
  369. package/lib/rules/utils/unicode/is-regional-indicator-symbol.js +1 -1
  370. package/lib/rules/utils/unicode/is-surrogate-pair.js +1 -1
  371. package/lib/rules/valid-typeof.js +153 -109
  372. package/lib/rules/vars-on-top.js +152 -144
  373. package/lib/rules/wrap-iife.js +204 -173
  374. package/lib/rules/wrap-regex.js +77 -47
  375. package/lib/rules/yield-star-spacing.js +145 -116
  376. package/lib/rules/yoda.js +283 -274
  377. package/lib/services/parser-service.js +65 -0
  378. package/lib/services/processor-service.js +101 -0
  379. package/lib/services/suppressions-service.js +302 -0
  380. package/lib/services/warning-service.js +98 -0
  381. package/lib/shared/ajv.js +14 -14
  382. package/lib/shared/assert.js +21 -0
  383. package/lib/shared/ast-utils.js +7 -6
  384. package/lib/shared/deep-merge-arrays.js +62 -0
  385. package/lib/shared/directives.js +3 -2
  386. package/lib/shared/flags.js +108 -0
  387. package/lib/shared/logging.js +24 -16
  388. package/lib/shared/naming.js +109 -0
  389. package/lib/shared/option-utils.js +63 -0
  390. package/lib/shared/relative-module-resolver.js +18 -40
  391. package/lib/shared/runtime-info.js +138 -128
  392. package/lib/shared/serialization.js +78 -0
  393. package/lib/shared/severity.js +22 -22
  394. package/lib/shared/stats.js +30 -0
  395. package/lib/shared/string-utils.js +19 -21
  396. package/lib/shared/text-table.js +68 -0
  397. package/lib/shared/translate-cli-options.js +281 -0
  398. package/lib/shared/traverser.js +153 -146
  399. package/lib/types/config-api.d.ts +12 -0
  400. package/lib/types/index.d.ts +1473 -0
  401. package/lib/types/rules.d.ts +5589 -0
  402. package/lib/types/universal.d.ts +6 -0
  403. package/lib/types/use-at-your-own-risk.d.ts +87 -0
  404. package/lib/universal.js +10 -0
  405. package/lib/unsupported-api.js +8 -9
  406. package/messages/all-files-ignored.js +3 -3
  407. package/messages/all-matched-files-ignored.js +21 -0
  408. package/messages/config-file-missing.js +16 -0
  409. package/messages/config-plugin-missing.js +14 -0
  410. package/messages/config-serialize-function.js +30 -0
  411. package/messages/eslintrc-incompat.js +35 -16
  412. package/messages/eslintrc-plugins.js +8 -5
  413. package/messages/extend-config-missing.js +3 -3
  414. package/messages/failed-to-read-json.js +3 -3
  415. package/messages/file-not-found.js +3 -3
  416. package/messages/invalid-rule-options.js +2 -2
  417. package/messages/invalid-rule-severity.js +2 -2
  418. package/messages/no-config-found.js +4 -4
  419. package/messages/plugin-conflict.js +9 -9
  420. package/messages/plugin-invalid.js +4 -4
  421. package/messages/plugin-missing.js +4 -4
  422. package/messages/print-config-with-directory-path.js +2 -2
  423. package/messages/shared.js +6 -1
  424. package/messages/whitespace-found.js +3 -3
  425. package/package.json +105 -60
  426. package/conf/config-schema.js +0 -93
  427. package/lib/cli-engine/formatters/checkstyle.js +0 -60
  428. package/lib/cli-engine/formatters/compact.js +0 -60
  429. package/lib/cli-engine/formatters/jslint-xml.js +0 -41
  430. package/lib/cli-engine/formatters/junit.js +0 -82
  431. package/lib/cli-engine/formatters/tap.js +0 -95
  432. package/lib/cli-engine/formatters/unix.js +0 -58
  433. package/lib/cli-engine/formatters/visualstudio.js +0 -63
  434. package/lib/cli-engine/xml-escape.js +0 -34
  435. package/lib/config/flat-config-helpers.js +0 -111
  436. package/lib/config/rule-validator.js +0 -158
  437. package/lib/eslint/flat-eslint.js +0 -1159
  438. package/lib/linter/config-comment-parser.js +0 -185
  439. package/lib/linter/node-event-generator.js +0 -354
  440. package/lib/linter/report-translator.js +0 -369
  441. package/lib/linter/safe-emitter.js +0 -52
  442. package/lib/rule-tester/flat-rule-tester.js +0 -1131
  443. package/lib/rules/require-jsdoc.js +0 -122
  444. package/lib/rules/utils/patterns/letters.js +0 -36
  445. package/lib/rules/valid-jsdoc.js +0 -516
  446. package/lib/shared/config-validator.js +0 -347
  447. package/lib/shared/deprecation-warnings.js +0 -58
  448. package/lib/shared/types.js +0 -216
  449. package/lib/source-code/index.js +0 -5
  450. package/lib/source-code/source-code.js +0 -1055
  451. package/lib/source-code/token-store/backward-token-comment-cursor.js +0 -57
  452. package/lib/source-code/token-store/backward-token-cursor.js +0 -58
  453. package/lib/source-code/token-store/cursors.js +0 -90
  454. package/lib/source-code/token-store/forward-token-comment-cursor.js +0 -57
  455. package/lib/source-code/token-store/forward-token-cursor.js +0 -63
  456. package/lib/source-code/token-store/index.js +0 -627
  457. package/lib/source-code/token-store/padded-token-cursor.js +0 -38
  458. package/lib/source-code/token-store/utils.js +0 -107
@@ -15,6 +15,11 @@ const astUtils = require("./utils/ast-utils");
15
15
  // Typedefs
16
16
  //------------------------------------------------------------------------------
17
17
 
18
+ /**
19
+ * A simple name for the types of variables that this rule supports
20
+ * @typedef {'array-destructure'|'catch-clause'|'parameter'|'variable'} VariableType
21
+ */
22
+
18
23
  /**
19
24
  * Bag of data used for formatting the `unusedVar` lint message.
20
25
  * @typedef {Object} UnusedVarMessageData
@@ -23,696 +28,1712 @@ const astUtils = require("./utils/ast-utils");
23
28
  * @property {string} additional Any additional info to be appended at the end.
24
29
  */
25
30
 
31
+ /**
32
+ * Bag of data used for formatting the `usedIgnoredVar` lint message.
33
+ * @typedef {Object} UsedIgnoredVarMessageData
34
+ * @property {string} varName The name of the unused var.
35
+ * @property {string} additional Any additional info to be appended at the end.
36
+ */
37
+
26
38
  //------------------------------------------------------------------------------
27
39
  // Rule Definition
28
40
  //------------------------------------------------------------------------------
29
41
 
30
- /** @type {import('../shared/types').Rule} */
42
+ /** @type {import('../types').Rule.RuleModule} */
31
43
  module.exports = {
32
- meta: {
33
- type: "problem",
34
-
35
- docs: {
36
- description: "Disallow unused variables",
37
- recommended: true,
38
- url: "https://eslint.org/docs/latest/rules/no-unused-vars"
39
- },
40
-
41
- schema: [
42
- {
43
- oneOf: [
44
- {
45
- enum: ["all", "local"]
46
- },
47
- {
48
- type: "object",
49
- properties: {
50
- vars: {
51
- enum: ["all", "local"]
52
- },
53
- varsIgnorePattern: {
54
- type: "string"
55
- },
56
- args: {
57
- enum: ["all", "after-used", "none"]
58
- },
59
- ignoreRestSiblings: {
60
- type: "boolean"
61
- },
62
- argsIgnorePattern: {
63
- type: "string"
64
- },
65
- caughtErrors: {
66
- enum: ["all", "none"]
67
- },
68
- caughtErrorsIgnorePattern: {
69
- type: "string"
70
- },
71
- destructuredArrayIgnorePattern: {
72
- type: "string"
73
- }
74
- },
75
- additionalProperties: false
76
- }
77
- ]
78
- }
79
- ],
80
-
81
- messages: {
82
- unusedVar: "'{{varName}}' is {{action}} but never used{{additional}}."
83
- }
84
- },
85
-
86
- create(context) {
87
- const sourceCode = context.sourceCode;
88
-
89
- const REST_PROPERTY_TYPE = /^(?:RestElement|(?:Experimental)?RestProperty)$/u;
90
-
91
- const config = {
92
- vars: "all",
93
- args: "after-used",
94
- ignoreRestSiblings: false,
95
- caughtErrors: "none"
96
- };
97
-
98
- const firstOption = context.options[0];
99
-
100
- if (firstOption) {
101
- if (typeof firstOption === "string") {
102
- config.vars = firstOption;
103
- } else {
104
- config.vars = firstOption.vars || config.vars;
105
- config.args = firstOption.args || config.args;
106
- config.ignoreRestSiblings = firstOption.ignoreRestSiblings || config.ignoreRestSiblings;
107
- config.caughtErrors = firstOption.caughtErrors || config.caughtErrors;
108
-
109
- if (firstOption.varsIgnorePattern) {
110
- config.varsIgnorePattern = new RegExp(firstOption.varsIgnorePattern, "u");
111
- }
112
-
113
- if (firstOption.argsIgnorePattern) {
114
- config.argsIgnorePattern = new RegExp(firstOption.argsIgnorePattern, "u");
115
- }
116
-
117
- if (firstOption.caughtErrorsIgnorePattern) {
118
- config.caughtErrorsIgnorePattern = new RegExp(firstOption.caughtErrorsIgnorePattern, "u");
119
- }
120
-
121
- if (firstOption.destructuredArrayIgnorePattern) {
122
- config.destructuredArrayIgnorePattern = new RegExp(firstOption.destructuredArrayIgnorePattern, "u");
123
- }
124
- }
125
- }
126
-
127
- /**
128
- * Generates the message data about the variable being defined and unused,
129
- * including the ignore pattern if configured.
130
- * @param {Variable} unusedVar eslint-scope variable object.
131
- * @returns {UnusedVarMessageData} The message data to be used with this unused variable.
132
- */
133
- function getDefinedMessageData(unusedVar) {
134
- const defType = unusedVar.defs && unusedVar.defs[0] && unusedVar.defs[0].type;
135
- let type;
136
- let pattern;
137
-
138
- if (defType === "CatchClause" && config.caughtErrorsIgnorePattern) {
139
- type = "args";
140
- pattern = config.caughtErrorsIgnorePattern.toString();
141
- } else if (defType === "Parameter" && config.argsIgnorePattern) {
142
- type = "args";
143
- pattern = config.argsIgnorePattern.toString();
144
- } else if (defType !== "Parameter" && config.varsIgnorePattern) {
145
- type = "vars";
146
- pattern = config.varsIgnorePattern.toString();
147
- }
148
-
149
- const additional = type ? `. Allowed unused ${type} must match ${pattern}` : "";
150
-
151
- return {
152
- varName: unusedVar.name,
153
- action: "defined",
154
- additional
155
- };
156
- }
157
-
158
- /**
159
- * Generate the warning message about the variable being
160
- * assigned and unused, including the ignore pattern if configured.
161
- * @param {Variable} unusedVar eslint-scope variable object.
162
- * @returns {UnusedVarMessageData} The message data to be used with this unused variable.
163
- */
164
- function getAssignedMessageData(unusedVar) {
165
- const def = unusedVar.defs[0];
166
- let additional = "";
167
-
168
- if (config.destructuredArrayIgnorePattern && def && def.name.parent.type === "ArrayPattern") {
169
- additional = `. Allowed unused elements of array destructuring patterns must match ${config.destructuredArrayIgnorePattern.toString()}`;
170
- } else if (config.varsIgnorePattern) {
171
- additional = `. Allowed unused vars must match ${config.varsIgnorePattern.toString()}`;
172
- }
173
-
174
- return {
175
- varName: unusedVar.name,
176
- action: "assigned a value",
177
- additional
178
- };
179
- }
180
-
181
- //--------------------------------------------------------------------------
182
- // Helpers
183
- //--------------------------------------------------------------------------
184
-
185
- const STATEMENT_TYPE = /(?:Statement|Declaration)$/u;
186
-
187
- /**
188
- * Determines if a given variable is being exported from a module.
189
- * @param {Variable} variable eslint-scope variable object.
190
- * @returns {boolean} True if the variable is exported, false if not.
191
- * @private
192
- */
193
- function isExported(variable) {
194
-
195
- const definition = variable.defs[0];
196
-
197
- if (definition) {
198
-
199
- let node = definition.node;
200
-
201
- if (node.type === "VariableDeclarator") {
202
- node = node.parent;
203
- } else if (definition.type === "Parameter") {
204
- return false;
205
- }
206
-
207
- return node.parent.type.indexOf("Export") === 0;
208
- }
209
- return false;
210
-
211
- }
212
-
213
- /**
214
- * Checks whether a node is a sibling of the rest property or not.
215
- * @param {ASTNode} node a node to check
216
- * @returns {boolean} True if the node is a sibling of the rest property, otherwise false.
217
- */
218
- function hasRestSibling(node) {
219
- return node.type === "Property" &&
220
- node.parent.type === "ObjectPattern" &&
221
- REST_PROPERTY_TYPE.test(node.parent.properties[node.parent.properties.length - 1].type);
222
- }
223
-
224
- /**
225
- * Determines if a variable has a sibling rest property
226
- * @param {Variable} variable eslint-scope variable object.
227
- * @returns {boolean} True if the variable is exported, false if not.
228
- * @private
229
- */
230
- function hasRestSpreadSibling(variable) {
231
- if (config.ignoreRestSiblings) {
232
- const hasRestSiblingDefinition = variable.defs.some(def => hasRestSibling(def.name.parent));
233
- const hasRestSiblingReference = variable.references.some(ref => hasRestSibling(ref.identifier.parent));
234
-
235
- return hasRestSiblingDefinition || hasRestSiblingReference;
236
- }
237
-
238
- return false;
239
- }
240
-
241
- /**
242
- * Determines if a reference is a read operation.
243
- * @param {Reference} ref An eslint-scope Reference
244
- * @returns {boolean} whether the given reference represents a read operation
245
- * @private
246
- */
247
- function isReadRef(ref) {
248
- return ref.isRead();
249
- }
250
-
251
- /**
252
- * Determine if an identifier is referencing an enclosing function name.
253
- * @param {Reference} ref The reference to check.
254
- * @param {ASTNode[]} nodes The candidate function nodes.
255
- * @returns {boolean} True if it's a self-reference, false if not.
256
- * @private
257
- */
258
- function isSelfReference(ref, nodes) {
259
- let scope = ref.from;
260
-
261
- while (scope) {
262
- if (nodes.includes(scope.block)) {
263
- return true;
264
- }
265
-
266
- scope = scope.upper;
267
- }
268
-
269
- return false;
270
- }
271
-
272
- /**
273
- * Gets a list of function definitions for a specified variable.
274
- * @param {Variable} variable eslint-scope variable object.
275
- * @returns {ASTNode[]} Function nodes.
276
- * @private
277
- */
278
- function getFunctionDefinitions(variable) {
279
- const functionDefinitions = [];
280
-
281
- variable.defs.forEach(def => {
282
- const { type, node } = def;
283
-
284
- // FunctionDeclarations
285
- if (type === "FunctionName") {
286
- functionDefinitions.push(node);
287
- }
288
-
289
- // FunctionExpressions
290
- if (type === "Variable" && node.init &&
291
- (node.init.type === "FunctionExpression" || node.init.type === "ArrowFunctionExpression")) {
292
- functionDefinitions.push(node.init);
293
- }
294
- });
295
- return functionDefinitions;
296
- }
297
-
298
- /**
299
- * Checks the position of given nodes.
300
- * @param {ASTNode} inner A node which is expected as inside.
301
- * @param {ASTNode} outer A node which is expected as outside.
302
- * @returns {boolean} `true` if the `inner` node exists in the `outer` node.
303
- * @private
304
- */
305
- function isInside(inner, outer) {
306
- return (
307
- inner.range[0] >= outer.range[0] &&
308
- inner.range[1] <= outer.range[1]
309
- );
310
- }
311
-
312
- /**
313
- * Checks whether a given node is unused expression or not.
314
- * @param {ASTNode} node The node itself
315
- * @returns {boolean} The node is an unused expression.
316
- * @private
317
- */
318
- function isUnusedExpression(node) {
319
- const parent = node.parent;
320
-
321
- if (parent.type === "ExpressionStatement") {
322
- return true;
323
- }
324
-
325
- if (parent.type === "SequenceExpression") {
326
- const isLastExpression = parent.expressions[parent.expressions.length - 1] === node;
327
-
328
- if (!isLastExpression) {
329
- return true;
330
- }
331
- return isUnusedExpression(parent);
332
- }
333
-
334
- return false;
335
- }
336
-
337
- /**
338
- * If a given reference is left-hand side of an assignment, this gets
339
- * the right-hand side node of the assignment.
340
- *
341
- * In the following cases, this returns null.
342
- *
343
- * - The reference is not the LHS of an assignment expression.
344
- * - The reference is inside of a loop.
345
- * - The reference is inside of a function scope which is different from
346
- * the declaration.
347
- * @param {eslint-scope.Reference} ref A reference to check.
348
- * @param {ASTNode} prevRhsNode The previous RHS node.
349
- * @returns {ASTNode|null} The RHS node or null.
350
- * @private
351
- */
352
- function getRhsNode(ref, prevRhsNode) {
353
- const id = ref.identifier;
354
- const parent = id.parent;
355
- const refScope = ref.from.variableScope;
356
- const varScope = ref.resolved.scope.variableScope;
357
- const canBeUsedLater = refScope !== varScope || astUtils.isInLoop(id);
358
-
359
- /*
360
- * Inherits the previous node if this reference is in the node.
361
- * This is for `a = a + a`-like code.
362
- */
363
- if (prevRhsNode && isInside(id, prevRhsNode)) {
364
- return prevRhsNode;
365
- }
366
-
367
- if (parent.type === "AssignmentExpression" &&
368
- isUnusedExpression(parent) &&
369
- id === parent.left &&
370
- !canBeUsedLater
371
- ) {
372
- return parent.right;
373
- }
374
- return null;
375
- }
376
-
377
- /**
378
- * Checks whether a given function node is stored to somewhere or not.
379
- * If the function node is stored, the function can be used later.
380
- * @param {ASTNode} funcNode A function node to check.
381
- * @param {ASTNode} rhsNode The RHS node of the previous assignment.
382
- * @returns {boolean} `true` if under the following conditions:
383
- * - the funcNode is assigned to a variable.
384
- * - the funcNode is bound as an argument of a function call.
385
- * - the function is bound to a property and the object satisfies above conditions.
386
- * @private
387
- */
388
- function isStorableFunction(funcNode, rhsNode) {
389
- let node = funcNode;
390
- let parent = funcNode.parent;
391
-
392
- while (parent && isInside(parent, rhsNode)) {
393
- switch (parent.type) {
394
- case "SequenceExpression":
395
- if (parent.expressions[parent.expressions.length - 1] !== node) {
396
- return false;
397
- }
398
- break;
399
-
400
- case "CallExpression":
401
- case "NewExpression":
402
- return parent.callee !== node;
403
-
404
- case "AssignmentExpression":
405
- case "TaggedTemplateExpression":
406
- case "YieldExpression":
407
- return true;
408
-
409
- default:
410
- if (STATEMENT_TYPE.test(parent.type)) {
411
-
412
- /*
413
- * If it encountered statements, this is a complex pattern.
414
- * Since analyzing complex patterns is hard, this returns `true` to avoid false positive.
415
- */
416
- return true;
417
- }
418
- }
419
-
420
- node = parent;
421
- parent = parent.parent;
422
- }
423
-
424
- return false;
425
- }
426
-
427
- /**
428
- * Checks whether a given Identifier node exists inside of a function node which can be used later.
429
- *
430
- * "can be used later" means:
431
- * - the function is assigned to a variable.
432
- * - the function is bound to a property and the object can be used later.
433
- * - the function is bound as an argument of a function call.
434
- *
435
- * If a reference exists in a function which can be used later, the reference is read when the function is called.
436
- * @param {ASTNode} id An Identifier node to check.
437
- * @param {ASTNode} rhsNode The RHS node of the previous assignment.
438
- * @returns {boolean} `true` if the `id` node exists inside of a function node which can be used later.
439
- * @private
440
- */
441
- function isInsideOfStorableFunction(id, rhsNode) {
442
- const funcNode = astUtils.getUpperFunction(id);
443
-
444
- return (
445
- funcNode &&
446
- isInside(funcNode, rhsNode) &&
447
- isStorableFunction(funcNode, rhsNode)
448
- );
449
- }
450
-
451
- /**
452
- * Checks whether a given reference is a read to update itself or not.
453
- * @param {eslint-scope.Reference} ref A reference to check.
454
- * @param {ASTNode} rhsNode The RHS node of the previous assignment.
455
- * @returns {boolean} The reference is a read to update itself.
456
- * @private
457
- */
458
- function isReadForItself(ref, rhsNode) {
459
- const id = ref.identifier;
460
- const parent = id.parent;
461
-
462
- return ref.isRead() && (
463
-
464
- // self update. e.g. `a += 1`, `a++`
465
- (
466
- (
467
- parent.type === "AssignmentExpression" &&
468
- parent.left === id &&
469
- isUnusedExpression(parent) &&
470
- !astUtils.isLogicalAssignmentOperator(parent.operator)
471
- ) ||
472
- (
473
- parent.type === "UpdateExpression" &&
474
- isUnusedExpression(parent)
475
- )
476
- ) ||
477
-
478
- // in RHS of an assignment for itself. e.g. `a = a + 1`
479
- (
480
- rhsNode &&
481
- isInside(id, rhsNode) &&
482
- !isInsideOfStorableFunction(id, rhsNode)
483
- )
484
- );
485
- }
486
-
487
- /**
488
- * Determine if an identifier is used either in for-in or for-of loops.
489
- * @param {Reference} ref The reference to check.
490
- * @returns {boolean} whether reference is used in the for-in loops
491
- * @private
492
- */
493
- function isForInOfRef(ref) {
494
- let target = ref.identifier.parent;
495
-
496
-
497
- // "for (var ...) { return; }"
498
- if (target.type === "VariableDeclarator") {
499
- target = target.parent.parent;
500
- }
501
-
502
- if (target.type !== "ForInStatement" && target.type !== "ForOfStatement") {
503
- return false;
504
- }
505
-
506
- // "for (...) { return; }"
507
- if (target.body.type === "BlockStatement") {
508
- target = target.body.body[0];
509
-
510
- // "for (...) return;"
511
- } else {
512
- target = target.body;
513
- }
514
-
515
- // For empty loop body
516
- if (!target) {
517
- return false;
518
- }
519
-
520
- return target.type === "ReturnStatement";
521
- }
522
-
523
- /**
524
- * Determines if the variable is used.
525
- * @param {Variable} variable The variable to check.
526
- * @returns {boolean} True if the variable is used
527
- * @private
528
- */
529
- function isUsedVariable(variable) {
530
- const functionNodes = getFunctionDefinitions(variable),
531
- isFunctionDefinition = functionNodes.length > 0;
532
- let rhsNode = null;
533
-
534
- return variable.references.some(ref => {
535
- if (isForInOfRef(ref)) {
536
- return true;
537
- }
538
-
539
- const forItself = isReadForItself(ref, rhsNode);
540
-
541
- rhsNode = getRhsNode(ref, rhsNode);
542
-
543
- return (
544
- isReadRef(ref) &&
545
- !forItself &&
546
- !(isFunctionDefinition && isSelfReference(ref, functionNodes))
547
- );
548
- });
549
- }
550
-
551
- /**
552
- * Checks whether the given variable is after the last used parameter.
553
- * @param {eslint-scope.Variable} variable The variable to check.
554
- * @returns {boolean} `true` if the variable is defined after the last
555
- * used parameter.
556
- */
557
- function isAfterLastUsedArg(variable) {
558
- const def = variable.defs[0];
559
- const params = sourceCode.getDeclaredVariables(def.node);
560
- const posteriorParams = params.slice(params.indexOf(variable) + 1);
561
-
562
- // If any used parameters occur after this parameter, do not report.
563
- return !posteriorParams.some(v => v.references.length > 0 || v.eslintUsed);
564
- }
565
-
566
- /**
567
- * Gets an array of variables without read references.
568
- * @param {Scope} scope an eslint-scope Scope object.
569
- * @param {Variable[]} unusedVars an array that saving result.
570
- * @returns {Variable[]} unused variables of the scope and descendant scopes.
571
- * @private
572
- */
573
- function collectUnusedVariables(scope, unusedVars) {
574
- const variables = scope.variables;
575
- const childScopes = scope.childScopes;
576
- let i, l;
577
-
578
- if (scope.type !== "global" || config.vars === "all") {
579
- for (i = 0, l = variables.length; i < l; ++i) {
580
- const variable = variables[i];
581
-
582
- // skip a variable of class itself name in the class scope
583
- if (scope.type === "class" && scope.block.id === variable.identifiers[0]) {
584
- continue;
585
- }
586
-
587
- // skip function expression names and variables marked with markVariableAsUsed()
588
- if (scope.functionExpressionScope || variable.eslintUsed) {
589
- continue;
590
- }
591
-
592
- // skip implicit "arguments" variable
593
- if (scope.type === "function" && variable.name === "arguments" && variable.identifiers.length === 0) {
594
- continue;
595
- }
596
-
597
- // explicit global variables don't have definitions.
598
- const def = variable.defs[0];
599
-
600
- if (def) {
601
- const type = def.type;
602
- const refUsedInArrayPatterns = variable.references.some(ref => ref.identifier.parent.type === "ArrayPattern");
603
-
604
- // skip elements of array destructuring patterns
605
- if (
606
- (
607
- def.name.parent.type === "ArrayPattern" ||
608
- refUsedInArrayPatterns
609
- ) &&
610
- config.destructuredArrayIgnorePattern &&
611
- config.destructuredArrayIgnorePattern.test(def.name.name)
612
- ) {
613
- continue;
614
- }
615
-
616
- // skip catch variables
617
- if (type === "CatchClause") {
618
- if (config.caughtErrors === "none") {
619
- continue;
620
- }
621
-
622
- // skip ignored parameters
623
- if (config.caughtErrorsIgnorePattern && config.caughtErrorsIgnorePattern.test(def.name.name)) {
624
- continue;
625
- }
626
- }
627
-
628
- if (type === "Parameter") {
629
-
630
- // skip any setter argument
631
- if ((def.node.parent.type === "Property" || def.node.parent.type === "MethodDefinition") && def.node.parent.kind === "set") {
632
- continue;
633
- }
634
-
635
- // if "args" option is "none", skip any parameter
636
- if (config.args === "none") {
637
- continue;
638
- }
639
-
640
- // skip ignored parameters
641
- if (config.argsIgnorePattern && config.argsIgnorePattern.test(def.name.name)) {
642
- continue;
643
- }
644
-
645
- // if "args" option is "after-used", skip used variables
646
- if (config.args === "after-used" && astUtils.isFunction(def.name.parent) && !isAfterLastUsedArg(variable)) {
647
- continue;
648
- }
649
- } else {
650
-
651
- // skip ignored variables
652
- if (config.varsIgnorePattern && config.varsIgnorePattern.test(def.name.name)) {
653
- continue;
654
- }
655
- }
656
- }
657
-
658
- if (!isUsedVariable(variable) && !isExported(variable) && !hasRestSpreadSibling(variable)) {
659
- unusedVars.push(variable);
660
- }
661
- }
662
- }
663
-
664
- for (i = 0, l = childScopes.length; i < l; ++i) {
665
- collectUnusedVariables(childScopes[i], unusedVars);
666
- }
667
-
668
- return unusedVars;
669
- }
670
-
671
- //--------------------------------------------------------------------------
672
- // Public
673
- //--------------------------------------------------------------------------
674
-
675
- return {
676
- "Program:exit"(programNode) {
677
- const unusedVars = collectUnusedVariables(sourceCode.getScope(programNode), []);
678
-
679
- for (let i = 0, l = unusedVars.length; i < l; ++i) {
680
- const unusedVar = unusedVars[i];
681
-
682
- // Report the first declaration.
683
- if (unusedVar.defs.length > 0) {
684
-
685
- // report last write reference, https://github.com/eslint/eslint/issues/14324
686
- const writeReferences = unusedVar.references.filter(ref => ref.isWrite() && ref.from.variableScope === unusedVar.scope.variableScope);
687
-
688
- let referenceToReport;
689
-
690
- if (writeReferences.length > 0) {
691
- referenceToReport = writeReferences[writeReferences.length - 1];
692
- }
693
-
694
- context.report({
695
- node: referenceToReport ? referenceToReport.identifier : unusedVar.identifiers[0],
696
- messageId: "unusedVar",
697
- data: unusedVar.references.some(ref => ref.isWrite())
698
- ? getAssignedMessageData(unusedVar)
699
- : getDefinedMessageData(unusedVar)
700
- });
701
-
702
- // If there are no regular declaration, report the first `/*globals*/` comment directive.
703
- } else if (unusedVar.eslintExplicitGlobalComments) {
704
- const directiveComment = unusedVar.eslintExplicitGlobalComments[0];
705
-
706
- context.report({
707
- node: programNode,
708
- loc: astUtils.getNameLocationInGlobalDirectiveComment(sourceCode, directiveComment, unusedVar.name),
709
- messageId: "unusedVar",
710
- data: getDefinedMessageData(unusedVar)
711
- });
712
- }
713
- }
714
- }
715
- };
716
-
717
- }
44
+ meta: {
45
+ type: "problem",
46
+
47
+ docs: {
48
+ description: "Disallow unused variables",
49
+ recommended: true,
50
+ url: "https://eslint.org/docs/latest/rules/no-unused-vars",
51
+ },
52
+
53
+ hasSuggestions: true,
54
+
55
+ schema: [
56
+ {
57
+ oneOf: [
58
+ {
59
+ enum: ["all", "local"],
60
+ },
61
+ {
62
+ type: "object",
63
+ properties: {
64
+ vars: {
65
+ enum: ["all", "local"],
66
+ },
67
+ varsIgnorePattern: {
68
+ type: "string",
69
+ },
70
+ args: {
71
+ enum: ["all", "after-used", "none"],
72
+ },
73
+ ignoreRestSiblings: {
74
+ type: "boolean",
75
+ },
76
+ argsIgnorePattern: {
77
+ type: "string",
78
+ },
79
+ caughtErrors: {
80
+ enum: ["all", "none"],
81
+ },
82
+ caughtErrorsIgnorePattern: {
83
+ type: "string",
84
+ },
85
+ destructuredArrayIgnorePattern: {
86
+ type: "string",
87
+ },
88
+ ignoreClassWithStaticInitBlock: {
89
+ type: "boolean",
90
+ },
91
+ ignoreUsingDeclarations: {
92
+ type: "boolean",
93
+ },
94
+ reportUsedIgnorePattern: {
95
+ type: "boolean",
96
+ },
97
+ },
98
+ additionalProperties: false,
99
+ },
100
+ ],
101
+ },
102
+ ],
103
+
104
+ messages: {
105
+ unusedVar:
106
+ "'{{varName}}' is {{action}} but never used{{additional}}.",
107
+ usedIgnoredVar:
108
+ "'{{varName}}' is marked as ignored but is used{{additional}}.",
109
+ removeVar: "Remove unused variable '{{varName}}'.",
110
+ },
111
+ },
112
+
113
+ create(context) {
114
+ const sourceCode = context.sourceCode;
115
+
116
+ const REST_PROPERTY_TYPE =
117
+ /^(?:RestElement|(?:Experimental)?RestProperty)$/u;
118
+
119
+ const config = {
120
+ vars: "all",
121
+ args: "after-used",
122
+ ignoreRestSiblings: false,
123
+ caughtErrors: "all",
124
+ ignoreClassWithStaticInitBlock: false,
125
+ ignoreUsingDeclarations: false,
126
+ reportUsedIgnorePattern: false,
127
+ };
128
+
129
+ const firstOption = context.options[0];
130
+
131
+ if (firstOption) {
132
+ if (typeof firstOption === "string") {
133
+ config.vars = firstOption;
134
+ } else {
135
+ config.vars = firstOption.vars || config.vars;
136
+ config.args = firstOption.args || config.args;
137
+ config.ignoreRestSiblings =
138
+ firstOption.ignoreRestSiblings || config.ignoreRestSiblings;
139
+ config.caughtErrors =
140
+ firstOption.caughtErrors || config.caughtErrors;
141
+ config.ignoreClassWithStaticInitBlock =
142
+ firstOption.ignoreClassWithStaticInitBlock ||
143
+ config.ignoreClassWithStaticInitBlock;
144
+ config.ignoreUsingDeclarations =
145
+ firstOption.ignoreUsingDeclarations ||
146
+ config.ignoreUsingDeclarations;
147
+ config.reportUsedIgnorePattern =
148
+ firstOption.reportUsedIgnorePattern ||
149
+ config.reportUsedIgnorePattern;
150
+
151
+ if (firstOption.varsIgnorePattern) {
152
+ config.varsIgnorePattern = new RegExp(
153
+ firstOption.varsIgnorePattern,
154
+ "u",
155
+ );
156
+ }
157
+
158
+ if (firstOption.argsIgnorePattern) {
159
+ config.argsIgnorePattern = new RegExp(
160
+ firstOption.argsIgnorePattern,
161
+ "u",
162
+ );
163
+ }
164
+
165
+ if (firstOption.caughtErrorsIgnorePattern) {
166
+ config.caughtErrorsIgnorePattern = new RegExp(
167
+ firstOption.caughtErrorsIgnorePattern,
168
+ "u",
169
+ );
170
+ }
171
+
172
+ if (firstOption.destructuredArrayIgnorePattern) {
173
+ config.destructuredArrayIgnorePattern = new RegExp(
174
+ firstOption.destructuredArrayIgnorePattern,
175
+ "u",
176
+ );
177
+ }
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Determines what variable type a def is.
183
+ * @param {Object} def the declaration to check
184
+ * @returns {VariableType} a simple name for the types of variables that this rule supports
185
+ */
186
+ function defToVariableType(def) {
187
+ /*
188
+ * This `destructuredArrayIgnorePattern` error report works differently from the catch
189
+ * clause and parameter error reports. _Both_ the `varsIgnorePattern` and the
190
+ * `destructuredArrayIgnorePattern` will be checked for array destructuring. However,
191
+ * for the purposes of the report, the currently defined behavior is to only inform the
192
+ * user of the `destructuredArrayIgnorePattern` if it's present (regardless of the fact
193
+ * that the `varsIgnorePattern` would also apply). If it's not present, the user will be
194
+ * informed of the `varsIgnorePattern`, assuming that's present.
195
+ */
196
+ if (
197
+ config.destructuredArrayIgnorePattern &&
198
+ def.name.parent.type === "ArrayPattern"
199
+ ) {
200
+ return "array-destructure";
201
+ }
202
+
203
+ switch (def.type) {
204
+ case "CatchClause":
205
+ return "catch-clause";
206
+ case "Parameter":
207
+ return "parameter";
208
+
209
+ default:
210
+ return "variable";
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Gets a given variable's description and configured ignore pattern
216
+ * based on the provided variableType
217
+ * @param {VariableType} variableType a simple name for the types of variables that this rule supports
218
+ * @throws {Error} (Unreachable)
219
+ * @returns {[string | undefined, string | undefined]} the given variable's description and
220
+ * ignore pattern
221
+ */
222
+ function getVariableDescription(variableType) {
223
+ let pattern;
224
+ let variableDescription;
225
+
226
+ switch (variableType) {
227
+ case "array-destructure":
228
+ pattern = config.destructuredArrayIgnorePattern;
229
+ variableDescription = "elements of array destructuring";
230
+ break;
231
+
232
+ case "catch-clause":
233
+ pattern = config.caughtErrorsIgnorePattern;
234
+ variableDescription = "caught errors";
235
+ break;
236
+
237
+ case "parameter":
238
+ pattern = config.argsIgnorePattern;
239
+ variableDescription = "args";
240
+ break;
241
+
242
+ case "variable":
243
+ pattern = config.varsIgnorePattern;
244
+ variableDescription = "vars";
245
+ break;
246
+
247
+ default:
248
+ throw new Error(
249
+ `Unexpected variable type: ${variableType}`,
250
+ );
251
+ }
252
+
253
+ if (pattern) {
254
+ pattern = pattern.toString();
255
+ }
256
+
257
+ return [variableDescription, pattern];
258
+ }
259
+
260
+ /**
261
+ * Generates the message data about the variable being defined and unused,
262
+ * including the ignore pattern if configured.
263
+ * @param {Variable} unusedVar eslint-scope variable object.
264
+ * @returns {UnusedVarMessageData} The message data to be used with this unused variable.
265
+ */
266
+ function getDefinedMessageData(unusedVar) {
267
+ const def = unusedVar.defs && unusedVar.defs[0];
268
+ let additionalMessageData = "";
269
+
270
+ if (def) {
271
+ const [variableDescription, pattern] = getVariableDescription(
272
+ defToVariableType(def),
273
+ );
274
+
275
+ if (pattern && variableDescription) {
276
+ additionalMessageData = `. Allowed unused ${variableDescription} must match ${pattern}`;
277
+ }
278
+ }
279
+
280
+ return {
281
+ varName: unusedVar.name,
282
+ action: "defined",
283
+ additional: additionalMessageData,
284
+ };
285
+ }
286
+
287
+ /**
288
+ * Generate the warning message about the variable being
289
+ * assigned and unused, including the ignore pattern if configured.
290
+ * @param {Variable} unusedVar eslint-scope variable object.
291
+ * @returns {UnusedVarMessageData} The message data to be used with this unused variable.
292
+ */
293
+ function getAssignedMessageData(unusedVar) {
294
+ const def = unusedVar.defs && unusedVar.defs[0];
295
+ let additionalMessageData = "";
296
+
297
+ if (def) {
298
+ const [variableDescription, pattern] = getVariableDescription(
299
+ defToVariableType(def),
300
+ );
301
+
302
+ if (pattern && variableDescription) {
303
+ additionalMessageData = `. Allowed unused ${variableDescription} must match ${pattern}`;
304
+ }
305
+ }
306
+
307
+ return {
308
+ varName: unusedVar.name,
309
+ action: "assigned a value",
310
+ additional: additionalMessageData,
311
+ };
312
+ }
313
+
314
+ /**
315
+ * Generate the warning message about a variable being used even though
316
+ * it is marked as being ignored.
317
+ * @param {Variable} variable eslint-scope variable object
318
+ * @param {VariableType} variableType a simple name for the types of variables that this rule supports
319
+ * @returns {UsedIgnoredVarMessageData} The message data to be used with
320
+ * this used ignored variable.
321
+ */
322
+ function getUsedIgnoredMessageData(variable, variableType) {
323
+ const [variableDescription, pattern] =
324
+ getVariableDescription(variableType);
325
+
326
+ let additionalMessageData = "";
327
+
328
+ if (pattern && variableDescription) {
329
+ additionalMessageData = `. Used ${variableDescription} must not match ${pattern}`;
330
+ }
331
+
332
+ return {
333
+ varName: variable.name,
334
+ additional: additionalMessageData,
335
+ };
336
+ }
337
+
338
+ //--------------------------------------------------------------------------
339
+ // Helpers
340
+ //--------------------------------------------------------------------------
341
+
342
+ const STATEMENT_TYPE = /(?:Statement|Declaration)$/u;
343
+
344
+ /**
345
+ * Determines if a given variable is being exported from a module.
346
+ * @param {Variable} variable eslint-scope variable object.
347
+ * @returns {boolean} True if the variable is exported, false if not.
348
+ * @private
349
+ */
350
+ function isExported(variable) {
351
+ const definition = variable.defs[0];
352
+
353
+ if (definition) {
354
+ let node = definition.node;
355
+
356
+ if (node.type === "VariableDeclarator") {
357
+ node = node.parent;
358
+ } else if (definition.type === "Parameter") {
359
+ return false;
360
+ }
361
+
362
+ return node.parent.type.indexOf("Export") === 0;
363
+ }
364
+ return false;
365
+ }
366
+
367
+ /**
368
+ * Determines if a given variable uses the explicit resource management protocol.
369
+ * @param {Variable} variable eslint-scope variable object.
370
+ * @returns {boolean} True if the variable is declared with "using" or "await using"
371
+ * @private
372
+ */
373
+ function usesExplicitResourceManagement(variable) {
374
+ const [definition] = variable.defs;
375
+
376
+ return (
377
+ definition?.type === "Variable" &&
378
+ (definition.parent.kind === "using" ||
379
+ definition.parent.kind === "await using")
380
+ );
381
+ }
382
+
383
+ /**
384
+ * Checks whether a node is a sibling of the rest property or not.
385
+ * @param {ASTNode} node a node to check
386
+ * @returns {boolean} True if the node is a sibling of the rest property, otherwise false.
387
+ */
388
+ function hasRestSibling(node) {
389
+ return (
390
+ node.type === "Property" &&
391
+ node.parent.type === "ObjectPattern" &&
392
+ REST_PROPERTY_TYPE.test(node.parent.properties.at(-1).type)
393
+ );
394
+ }
395
+
396
+ /**
397
+ * Determines if a variable has a sibling rest property
398
+ * @param {Variable} variable eslint-scope variable object.
399
+ * @returns {boolean} True if the variable has a sibling rest property, false if not.
400
+ * @private
401
+ */
402
+ function hasRestSpreadSibling(variable) {
403
+ if (config.ignoreRestSiblings) {
404
+ const hasRestSiblingDefinition = variable.defs.some(def =>
405
+ hasRestSibling(def.name.parent),
406
+ );
407
+ const hasRestSiblingReference = variable.references.some(ref =>
408
+ hasRestSibling(ref.identifier.parent),
409
+ );
410
+
411
+ return hasRestSiblingDefinition || hasRestSiblingReference;
412
+ }
413
+
414
+ return false;
415
+ }
416
+
417
+ /**
418
+ * Determines if a reference is a read operation.
419
+ * @param {Reference} ref An eslint-scope Reference
420
+ * @returns {boolean} whether the given reference represents a read operation
421
+ * @private
422
+ */
423
+ function isReadRef(ref) {
424
+ return ref.isRead();
425
+ }
426
+
427
+ /**
428
+ * Determine if an identifier is referencing an enclosing function name.
429
+ * @param {Reference} ref The reference to check.
430
+ * @param {ASTNode[]} nodes The candidate function nodes.
431
+ * @returns {boolean} True if it's a self-reference, false if not.
432
+ * @private
433
+ */
434
+ function isSelfReference(ref, nodes) {
435
+ let scope = ref.from;
436
+
437
+ while (scope) {
438
+ if (nodes.includes(scope.block)) {
439
+ return true;
440
+ }
441
+
442
+ scope = scope.upper;
443
+ }
444
+
445
+ return false;
446
+ }
447
+
448
+ /**
449
+ * Gets a list of function definitions for a specified variable.
450
+ * @param {Variable} variable eslint-scope variable object.
451
+ * @returns {ASTNode[]} Function nodes.
452
+ * @private
453
+ */
454
+ function getFunctionDefinitions(variable) {
455
+ const functionDefinitions = [];
456
+
457
+ variable.defs.forEach(def => {
458
+ const { type, node } = def;
459
+
460
+ // FunctionDeclarations
461
+ if (type === "FunctionName") {
462
+ functionDefinitions.push(node);
463
+ }
464
+
465
+ // FunctionExpressions
466
+ if (
467
+ type === "Variable" &&
468
+ node.init &&
469
+ (node.init.type === "FunctionExpression" ||
470
+ node.init.type === "ArrowFunctionExpression")
471
+ ) {
472
+ functionDefinitions.push(node.init);
473
+ }
474
+ });
475
+ return functionDefinitions;
476
+ }
477
+
478
+ /**
479
+ * Checks the position of given nodes.
480
+ * @param {ASTNode} inner A node which is expected as inside.
481
+ * @param {ASTNode} outer A node which is expected as outside.
482
+ * @returns {boolean} `true` if the `inner` node exists in the `outer` node.
483
+ * @private
484
+ */
485
+ function isInside(inner, outer) {
486
+ return (
487
+ inner.range[0] >= outer.range[0] &&
488
+ inner.range[1] <= outer.range[1]
489
+ );
490
+ }
491
+
492
+ /**
493
+ * Checks whether a given node is unused expression or not.
494
+ * @param {ASTNode} node The node itself
495
+ * @returns {boolean} The node is an unused expression.
496
+ * @private
497
+ */
498
+ function isUnusedExpression(node) {
499
+ const parent = node.parent;
500
+
501
+ if (parent.type === "ExpressionStatement") {
502
+ return true;
503
+ }
504
+
505
+ if (parent.type === "SequenceExpression") {
506
+ const isLastExpression = parent.expressions.at(-1) === node;
507
+
508
+ if (!isLastExpression) {
509
+ return true;
510
+ }
511
+ return isUnusedExpression(parent);
512
+ }
513
+
514
+ return false;
515
+ }
516
+
517
+ /**
518
+ * If a given reference is left-hand side of an assignment, this gets
519
+ * the right-hand side node of the assignment.
520
+ *
521
+ * In the following cases, this returns null.
522
+ *
523
+ * - The reference is not the LHS of an assignment expression.
524
+ * - The reference is inside of a loop.
525
+ * - The reference is inside of a function scope which is different from
526
+ * the declaration.
527
+ * @param {eslint-scope.Reference} ref A reference to check.
528
+ * @param {ASTNode} prevRhsNode The previous RHS node.
529
+ * @returns {ASTNode|null} The RHS node or null.
530
+ * @private
531
+ */
532
+ function getRhsNode(ref, prevRhsNode) {
533
+ const id = ref.identifier;
534
+ const parent = id.parent;
535
+ const refScope = ref.from.variableScope;
536
+ const varScope = ref.resolved.scope.variableScope;
537
+ const canBeUsedLater =
538
+ refScope !== varScope || astUtils.isInLoop(id);
539
+
540
+ /*
541
+ * Inherits the previous node if this reference is in the node.
542
+ * This is for `a = a + a`-like code.
543
+ */
544
+ if (prevRhsNode && isInside(id, prevRhsNode)) {
545
+ return prevRhsNode;
546
+ }
547
+
548
+ if (
549
+ parent.type === "AssignmentExpression" &&
550
+ isUnusedExpression(parent) &&
551
+ id === parent.left &&
552
+ !canBeUsedLater
553
+ ) {
554
+ return parent.right;
555
+ }
556
+ return null;
557
+ }
558
+
559
+ /**
560
+ * Checks whether a given function node is stored to somewhere or not.
561
+ * If the function node is stored, the function can be used later.
562
+ * @param {ASTNode} funcNode A function node to check.
563
+ * @param {ASTNode} rhsNode The RHS node of the previous assignment.
564
+ * @returns {boolean} `true` if under the following conditions:
565
+ * - the funcNode is assigned to a variable.
566
+ * - the funcNode is bound as an argument of a function call.
567
+ * - the function is bound to a property and the object satisfies above conditions.
568
+ * @private
569
+ */
570
+ function isStorableFunction(funcNode, rhsNode) {
571
+ let node = funcNode;
572
+ let parent = funcNode.parent;
573
+
574
+ while (parent && isInside(parent, rhsNode)) {
575
+ switch (parent.type) {
576
+ case "SequenceExpression":
577
+ if (parent.expressions.at(-1) !== node) {
578
+ return false;
579
+ }
580
+ break;
581
+
582
+ case "CallExpression":
583
+ case "NewExpression":
584
+ return parent.callee !== node;
585
+
586
+ case "AssignmentExpression":
587
+ case "TaggedTemplateExpression":
588
+ case "YieldExpression":
589
+ return true;
590
+
591
+ default:
592
+ if (STATEMENT_TYPE.test(parent.type)) {
593
+ /*
594
+ * If it encountered statements, this is a complex pattern.
595
+ * Since analyzing complex patterns is hard, this returns `true` to avoid false positive.
596
+ */
597
+ return true;
598
+ }
599
+ }
600
+
601
+ node = parent;
602
+ parent = parent.parent;
603
+ }
604
+
605
+ return false;
606
+ }
607
+
608
+ /**
609
+ * Checks whether a given Identifier node exists inside of a function node which can be used later.
610
+ *
611
+ * "can be used later" means:
612
+ * - the function is assigned to a variable.
613
+ * - the function is bound to a property and the object can be used later.
614
+ * - the function is bound as an argument of a function call.
615
+ *
616
+ * If a reference exists in a function which can be used later, the reference is read when the function is called.
617
+ * @param {ASTNode} id An Identifier node to check.
618
+ * @param {ASTNode} rhsNode The RHS node of the previous assignment.
619
+ * @returns {boolean} `true` if the `id` node exists inside of a function node which can be used later.
620
+ * @private
621
+ */
622
+ function isInsideOfStorableFunction(id, rhsNode) {
623
+ const funcNode = astUtils.getUpperFunction(id);
624
+
625
+ return (
626
+ funcNode &&
627
+ isInside(funcNode, rhsNode) &&
628
+ isStorableFunction(funcNode, rhsNode)
629
+ );
630
+ }
631
+
632
+ /**
633
+ * Checks whether a given reference is a read to update itself or not.
634
+ * @param {eslint-scope.Reference} ref A reference to check.
635
+ * @param {ASTNode} rhsNode The RHS node of the previous assignment.
636
+ * @returns {boolean} The reference is a read to update itself.
637
+ * @private
638
+ */
639
+ function isReadForItself(ref, rhsNode) {
640
+ const id = ref.identifier;
641
+ const parent = id.parent;
642
+
643
+ return (
644
+ ref.isRead() &&
645
+ // self update. e.g. `a += 1`, `a++`
646
+ ((parent.type === "AssignmentExpression" &&
647
+ parent.left === id &&
648
+ isUnusedExpression(parent) &&
649
+ !astUtils.isLogicalAssignmentOperator(parent.operator)) ||
650
+ (parent.type === "UpdateExpression" &&
651
+ isUnusedExpression(parent)) ||
652
+ // in RHS of an assignment for itself. e.g. `a = a + 1`
653
+ (rhsNode &&
654
+ isInside(id, rhsNode) &&
655
+ !isInsideOfStorableFunction(id, rhsNode)))
656
+ );
657
+ }
658
+
659
+ /**
660
+ * Determine if an identifier is used either in for-in or for-of loops.
661
+ * @param {Reference} ref The reference to check.
662
+ * @returns {boolean} whether reference is used in the for-in loops
663
+ * @private
664
+ */
665
+ function isForInOfRef(ref) {
666
+ let target = ref.identifier.parent;
667
+
668
+ // "for (var ...) { return; }"
669
+ if (target.type === "VariableDeclarator") {
670
+ target = target.parent.parent;
671
+ }
672
+
673
+ if (
674
+ target.type !== "ForInStatement" &&
675
+ target.type !== "ForOfStatement"
676
+ ) {
677
+ return false;
678
+ }
679
+
680
+ // "for (...) { return; }"
681
+ if (target.body.type === "BlockStatement") {
682
+ target = target.body.body[0];
683
+
684
+ // "for (...) return;"
685
+ } else {
686
+ target = target.body;
687
+ }
688
+
689
+ // For empty loop body
690
+ if (!target) {
691
+ return false;
692
+ }
693
+
694
+ return target.type === "ReturnStatement";
695
+ }
696
+
697
+ /**
698
+ * Determines if the variable is used.
699
+ * @param {Variable} variable The variable to check.
700
+ * @returns {boolean} True if the variable is used
701
+ * @private
702
+ */
703
+ function isUsedVariable(variable) {
704
+ if (variable.eslintUsed) {
705
+ return true;
706
+ }
707
+
708
+ const functionNodes = getFunctionDefinitions(variable);
709
+ const isFunctionDefinition = functionNodes.length > 0;
710
+
711
+ let rhsNode = null;
712
+
713
+ return variable.references.some(ref => {
714
+ if (isForInOfRef(ref)) {
715
+ return true;
716
+ }
717
+
718
+ const forItself = isReadForItself(ref, rhsNode);
719
+
720
+ rhsNode = getRhsNode(ref, rhsNode);
721
+
722
+ return (
723
+ isReadRef(ref) &&
724
+ !forItself &&
725
+ !(
726
+ isFunctionDefinition &&
727
+ isSelfReference(ref, functionNodes)
728
+ )
729
+ );
730
+ });
731
+ }
732
+
733
+ /**
734
+ * Checks whether the given variable is after the last used parameter.
735
+ * @param {eslint-scope.Variable} variable The variable to check.
736
+ * @returns {boolean} `true` if the variable is defined after the last
737
+ * used parameter.
738
+ */
739
+ function isAfterLastUsedArg(variable) {
740
+ const def = variable.defs[0];
741
+ const params = sourceCode.getDeclaredVariables(def.node);
742
+ const posteriorParams = params.slice(params.indexOf(variable) + 1);
743
+
744
+ // If any used parameters occur after this parameter, do not report.
745
+ return !posteriorParams.some(
746
+ v => v.references.length > 0 || v.eslintUsed,
747
+ );
748
+ }
749
+
750
+ /**
751
+ * Gets an array of variables without read references.
752
+ * @param {Scope} scope an eslint-scope Scope object.
753
+ * @param {Variable[]} unusedVars an array that saving result.
754
+ * @returns {Variable[]} unused variables of the scope and descendant scopes.
755
+ * @private
756
+ */
757
+ function collectUnusedVariables(scope, unusedVars) {
758
+ const variables = scope.variables;
759
+ const childScopes = scope.childScopes;
760
+ let i, l;
761
+
762
+ if (scope.type !== "global" || config.vars === "all") {
763
+ for (i = 0, l = variables.length; i < l; ++i) {
764
+ const variable = variables[i];
765
+
766
+ // skip a variable of class itself name in the class scope
767
+ if (
768
+ scope.type === "class" &&
769
+ scope.block.id === variable.identifiers[0]
770
+ ) {
771
+ continue;
772
+ }
773
+
774
+ // skip function expression names
775
+ if (scope.functionExpressionScope) {
776
+ continue;
777
+ }
778
+
779
+ // skip variables marked with markVariableAsUsed()
780
+ if (
781
+ !config.reportUsedIgnorePattern &&
782
+ variable.eslintUsed
783
+ ) {
784
+ continue;
785
+ }
786
+
787
+ // skip implicit "arguments" variable
788
+ if (
789
+ scope.type === "function" &&
790
+ variable.name === "arguments" &&
791
+ variable.identifiers.length === 0
792
+ ) {
793
+ continue;
794
+ }
795
+
796
+ // explicit global variables don't have definitions.
797
+ const def = variable.defs[0];
798
+
799
+ if (def) {
800
+ const type = def.type;
801
+ const refUsedInArrayPatterns = variable.references.some(
802
+ ref =>
803
+ ref.identifier.parent.type === "ArrayPattern",
804
+ );
805
+
806
+ // skip elements of array destructuring patterns
807
+ if (
808
+ (def.name.parent.type === "ArrayPattern" ||
809
+ refUsedInArrayPatterns) &&
810
+ config.destructuredArrayIgnorePattern &&
811
+ config.destructuredArrayIgnorePattern.test(
812
+ def.name.name,
813
+ )
814
+ ) {
815
+ if (
816
+ config.reportUsedIgnorePattern &&
817
+ isUsedVariable(variable)
818
+ ) {
819
+ context.report({
820
+ node: def.name,
821
+ messageId: "usedIgnoredVar",
822
+ data: getUsedIgnoredMessageData(
823
+ variable,
824
+ "array-destructure",
825
+ ),
826
+ });
827
+ }
828
+
829
+ continue;
830
+ }
831
+
832
+ if (type === "ClassName") {
833
+ const hasStaticBlock = def.node.body.body.some(
834
+ node => node.type === "StaticBlock",
835
+ );
836
+
837
+ if (
838
+ config.ignoreClassWithStaticInitBlock &&
839
+ hasStaticBlock
840
+ ) {
841
+ continue;
842
+ }
843
+ }
844
+
845
+ // skip catch variables
846
+ if (type === "CatchClause") {
847
+ if (config.caughtErrors === "none") {
848
+ continue;
849
+ }
850
+
851
+ // skip ignored parameters
852
+ if (
853
+ config.caughtErrorsIgnorePattern &&
854
+ config.caughtErrorsIgnorePattern.test(
855
+ def.name.name,
856
+ )
857
+ ) {
858
+ if (
859
+ config.reportUsedIgnorePattern &&
860
+ isUsedVariable(variable)
861
+ ) {
862
+ context.report({
863
+ node: def.name,
864
+ messageId: "usedIgnoredVar",
865
+ data: getUsedIgnoredMessageData(
866
+ variable,
867
+ "catch-clause",
868
+ ),
869
+ });
870
+ }
871
+
872
+ continue;
873
+ }
874
+ } else if (type === "Parameter") {
875
+ // skip any setter argument
876
+ if (
877
+ (def.node.parent.type === "Property" ||
878
+ def.node.parent.type ===
879
+ "MethodDefinition") &&
880
+ def.node.parent.kind === "set"
881
+ ) {
882
+ continue;
883
+ }
884
+
885
+ // if "args" option is "none", skip any parameter
886
+ if (config.args === "none") {
887
+ continue;
888
+ }
889
+
890
+ // skip ignored parameters
891
+ if (
892
+ config.argsIgnorePattern &&
893
+ config.argsIgnorePattern.test(def.name.name)
894
+ ) {
895
+ if (
896
+ config.reportUsedIgnorePattern &&
897
+ isUsedVariable(variable)
898
+ ) {
899
+ context.report({
900
+ node: def.name,
901
+ messageId: "usedIgnoredVar",
902
+ data: getUsedIgnoredMessageData(
903
+ variable,
904
+ "parameter",
905
+ ),
906
+ });
907
+ }
908
+
909
+ continue;
910
+ }
911
+
912
+ // if "args" option is "after-used", skip used variables
913
+ if (
914
+ config.args === "after-used" &&
915
+ astUtils.isFunction(def.name.parent) &&
916
+ !isAfterLastUsedArg(variable)
917
+ ) {
918
+ continue;
919
+ }
920
+ } else {
921
+ // skip ignored variables
922
+ if (
923
+ config.varsIgnorePattern &&
924
+ config.varsIgnorePattern.test(def.name.name)
925
+ ) {
926
+ if (
927
+ config.reportUsedIgnorePattern &&
928
+ isUsedVariable(variable)
929
+ ) {
930
+ context.report({
931
+ node: def.name,
932
+ messageId: "usedIgnoredVar",
933
+ data: getUsedIgnoredMessageData(
934
+ variable,
935
+ "variable",
936
+ ),
937
+ });
938
+ }
939
+
940
+ continue;
941
+ }
942
+ }
943
+ }
944
+
945
+ if (
946
+ !isUsedVariable(variable) &&
947
+ !isExported(variable) &&
948
+ !(
949
+ config.ignoreUsingDeclarations &&
950
+ usesExplicitResourceManagement(variable)
951
+ ) &&
952
+ !hasRestSpreadSibling(variable)
953
+ ) {
954
+ unusedVars.push(variable);
955
+ }
956
+ }
957
+ }
958
+
959
+ for (i = 0, l = childScopes.length; i < l; ++i) {
960
+ collectUnusedVariables(childScopes[i], unusedVars);
961
+ }
962
+
963
+ return unusedVars;
964
+ }
965
+
966
+ /**
967
+ * fixes unused variables
968
+ * @param {Object} fixer fixer object
969
+ * @param {Object} unusedVar unused variable to fix
970
+ * @returns {Object} fixer object
971
+ */
972
+ function handleFixes(fixer, unusedVar) {
973
+ const id = unusedVar.identifiers[0];
974
+ const parent = id.parent;
975
+ const parentType = parent.type;
976
+ const tokenBefore = sourceCode.getTokenBefore(id);
977
+ const tokenAfter = sourceCode.getTokenAfter(id);
978
+ const isFunction = astUtils.isFunction;
979
+ const isLoop = astUtils.isLoop;
980
+ const allWriteReferences = unusedVar.references.filter(ref =>
981
+ ref.isWrite(),
982
+ );
983
+
984
+ /**
985
+ * get range from token before of a given node
986
+ * @param {ASTNode} node node of identifier
987
+ * @param {number} skips number of token to skip
988
+ * @returns {number} start range of token before the identifier
989
+ */
990
+ function getPreviousTokenStart(node, skips) {
991
+ return sourceCode.getTokenBefore(node, skips).range[0];
992
+ }
993
+
994
+ /**
995
+ * get range to token after of a given node
996
+ * @param {ASTNode} node node of identifier
997
+ * @param {number} skips number of token to skip
998
+ * @returns {number} end range of token after the identifier
999
+ */
1000
+ function getNextTokenEnd(node, skips) {
1001
+ return sourceCode.getTokenAfter(node, skips).range[1];
1002
+ }
1003
+
1004
+ /**
1005
+ * get the value of token before of a given node
1006
+ * @param {ASTNode} node node of identifier
1007
+ * @returns {string} value of token before the identifier
1008
+ */
1009
+ function getTokenBeforeValue(node) {
1010
+ return sourceCode.getTokenBefore(node).value;
1011
+ }
1012
+
1013
+ /**
1014
+ * get the value of token after of a given node
1015
+ * @param {ASTNode} node node of identifier
1016
+ * @returns {string} value of token after the identifier
1017
+ */
1018
+ function getTokenAfterValue(node) {
1019
+ return sourceCode.getTokenAfter(node).value;
1020
+ }
1021
+
1022
+ /**
1023
+ * Check if an array has a single element with null as other element.
1024
+ * @param {ASTNode} node ArrayPattern node
1025
+ * @returns {boolean} true if array has single element with other null elements
1026
+ */
1027
+ function hasSingleElement(node) {
1028
+ return node.elements.filter(e => e !== null).length === 1;
1029
+ }
1030
+
1031
+ /**
1032
+ * check whether import specifier has an import of particular type
1033
+ * @param {ASTNode} node ImportDeclaration node
1034
+ * @param {string} type type of import to check
1035
+ * @returns {boolean} true if import specifier has import of specified type
1036
+ */
1037
+ function hasImportOfCertainType(node, type) {
1038
+ return node.specifiers.some(e => e.type === type);
1039
+ }
1040
+
1041
+ /**
1042
+ * Check whether declaration is safe to remove or not
1043
+ * @param {ASTNode} nextToken next token of unused variable
1044
+ * @param {ASTNode} prevToken previous token of unused variable
1045
+ * @returns {boolean} true if declaration is not safe to remove
1046
+ */
1047
+ function isDeclarationNotSafeToRemove(nextToken, prevToken) {
1048
+ return (
1049
+ nextToken.type === "String" ||
1050
+ (prevToken &&
1051
+ !astUtils.isSemicolonToken(prevToken) &&
1052
+ !astUtils.isOpeningBraceToken(prevToken))
1053
+ );
1054
+ }
1055
+
1056
+ /**
1057
+ * give fixes for unused variables in function parameters
1058
+ * @param {ASTNode} node node to check
1059
+ * @returns {Object} fixer object
1060
+ */
1061
+ function fixFunctionParameters(node) {
1062
+ const parentNode = node.parent;
1063
+
1064
+ if (isFunction(parentNode)) {
1065
+ // remove unused function parameter if there is only a single parameter
1066
+ if (parentNode.params.length === 1) {
1067
+ return fixer.removeRange(node.range);
1068
+ }
1069
+
1070
+ // remove first unused function parameter when there are multiple parameters
1071
+ if (
1072
+ getTokenBeforeValue(node) === "(" &&
1073
+ getTokenAfterValue(node) === ","
1074
+ ) {
1075
+ return fixer.removeRange([
1076
+ node.range[0],
1077
+ getNextTokenEnd(node),
1078
+ ]);
1079
+ }
1080
+
1081
+ // remove unused function parameters except first one when there are multiple parameters
1082
+ return fixer.removeRange([
1083
+ getPreviousTokenStart(node),
1084
+ node.range[1],
1085
+ ]);
1086
+ }
1087
+
1088
+ return null;
1089
+ }
1090
+
1091
+ /**
1092
+ * fix unused variable declarations and function parameters
1093
+ * @param {ASTNode} node parent node to identifier
1094
+ * @returns {Object} fixer object
1095
+ */
1096
+ function fixVariables(node) {
1097
+ const parentNode = node.parent;
1098
+
1099
+ // remove unused declared variables such as var a = b; or var a = b, c;
1100
+ if (parentNode.type === "VariableDeclarator") {
1101
+ // skip variable in for (const [ foo ] of bar);
1102
+ if (isLoop(parentNode.parent.parent)) {
1103
+ return null;
1104
+ }
1105
+
1106
+ /*
1107
+ * remove unused declared variable with single declaration such as 'var a = b;'
1108
+ * remove complete declaration when there is an unused variable in 'const { a } = foo;', same for arrays.
1109
+ */
1110
+ if (parentNode.parent.declarations.length === 1) {
1111
+ // if next token is a string it could become a directive if node is removed -> no suggestion.
1112
+ const nextToken = sourceCode.getTokenAfter(
1113
+ parentNode.parent,
1114
+ );
1115
+
1116
+ // if previous token exists and is not ";" or "{" not sure about ASI rules -> no suggestion.
1117
+ const prevToken = sourceCode.getTokenBefore(
1118
+ parentNode.parent,
1119
+ );
1120
+
1121
+ if (
1122
+ nextToken &&
1123
+ isDeclarationNotSafeToRemove(nextToken, prevToken)
1124
+ ) {
1125
+ return null;
1126
+ }
1127
+
1128
+ return fixer.removeRange(parentNode.parent.range);
1129
+ }
1130
+
1131
+ /*
1132
+ * remove unused declared variable with multiple declaration except first one such as 'var a = b, c = d;'
1133
+ * fix 'let bar = "hello", { a } = foo;' to 'let bar = "hello";' if 'a' is unused, same for arrays.
1134
+ */
1135
+ if (getTokenBeforeValue(parentNode) === ",") {
1136
+ return fixer.removeRange([
1137
+ getPreviousTokenStart(parentNode),
1138
+ parentNode.range[1],
1139
+ ]);
1140
+ }
1141
+
1142
+ /*
1143
+ * remove first unused declared variable when there are multiple declarations
1144
+ * fix 'let { a } = foo, bar = "hello";' to 'let bar = "hello";' if 'a' is unused, same for arrays.
1145
+ */
1146
+ return fixer.removeRange([
1147
+ parentNode.range[0],
1148
+ getNextTokenEnd(parentNode),
1149
+ ]);
1150
+ }
1151
+
1152
+ // fixes [{a: {k}}], [{a: [k]}]
1153
+ if (getTokenBeforeValue(node) === ":") {
1154
+ if (parentNode.parent.type === "ObjectPattern") {
1155
+ // eslint-disable-next-line no-use-before-define -- due to interdependency of functions
1156
+ return fixObjectWithValueSeparator(node);
1157
+ }
1158
+ }
1159
+
1160
+ // fix unused function parameters
1161
+ return fixFunctionParameters(node);
1162
+ }
1163
+
1164
+ /**
1165
+ * fix nested object like { a: { b } }
1166
+ * @param {ASTNode} node parent node to check
1167
+ * @returns {Object} fixer object
1168
+ */
1169
+ function fixNestedObjectVariable(node) {
1170
+ const parentNode = node.parent;
1171
+
1172
+ // fix for { a: { b: { c: { d } } } }
1173
+ if (
1174
+ parentNode.parent.parent.parent.type === "ObjectPattern" &&
1175
+ parentNode.parent.properties.length === 1
1176
+ ) {
1177
+ return fixNestedObjectVariable(parentNode.parent);
1178
+ }
1179
+
1180
+ // fix for { a: { b } }
1181
+ if (parentNode.parent.type === "ObjectPattern") {
1182
+ // fix for unused variables in destructured object with single property in variable declaration and function parameter
1183
+ if (parentNode.parent.properties.length === 1) {
1184
+ return fixVariables(parentNode.parent);
1185
+ }
1186
+
1187
+ // fix for first unused property when there are multiple properties such as '{ a: { b }, c }'
1188
+ if (getTokenBeforeValue(parentNode) === "{") {
1189
+ return fixer.removeRange([
1190
+ parentNode.range[0],
1191
+ getNextTokenEnd(parentNode),
1192
+ ]);
1193
+ }
1194
+
1195
+ // fix for unused property except first one when there are multiple properties such as '{ k, a: { b } }'
1196
+ return fixer.removeRange([
1197
+ getPreviousTokenStart(parentNode),
1198
+ parentNode.range[1],
1199
+ ]);
1200
+ }
1201
+
1202
+ return null;
1203
+ }
1204
+
1205
+ /**
1206
+ * fix unused variables in array and nested array
1207
+ * @param {ASTNode} node parent node to check
1208
+ * @returns {Object} fixer object
1209
+ */
1210
+ function fixNestedArrayVariable(node) {
1211
+ const parentNode = node.parent;
1212
+
1213
+ // fix for nested arrays [[ a ]]
1214
+ if (
1215
+ parentNode.parent.type === "ArrayPattern" &&
1216
+ hasSingleElement(parentNode)
1217
+ ) {
1218
+ return fixNestedArrayVariable(parentNode);
1219
+ }
1220
+
1221
+ if (hasSingleElement(parentNode)) {
1222
+ // fixes { a: [{ b }] } or { a: [[ b ]] }
1223
+ if (getTokenBeforeValue(parentNode) === ":") {
1224
+ return fixVariables(parentNode);
1225
+ }
1226
+
1227
+ // fixes [a, ...[[ b ]]] or [a, ...[{ b }]]
1228
+ if (parentNode.parent.type === "RestElement") {
1229
+ // eslint-disable-next-line no-use-before-define -- due to interdependency of functions
1230
+ return fixRestInPattern(parentNode.parent);
1231
+ }
1232
+
1233
+ // fix unused variables in destructured array in variable declaration or function parameter
1234
+ return fixVariables(parentNode);
1235
+ }
1236
+
1237
+ // remove last unused array element
1238
+ if (
1239
+ getTokenBeforeValue(node) === "," &&
1240
+ getTokenAfterValue(node) === "]"
1241
+ ) {
1242
+ return fixer.removeRange([
1243
+ getPreviousTokenStart(node),
1244
+ node.range[1],
1245
+ ]);
1246
+ }
1247
+
1248
+ // remove unused array element
1249
+ return fixer.removeRange(node.range);
1250
+ }
1251
+
1252
+ /**
1253
+ * fix cases like {a: {k}} or {a: [k]}
1254
+ * @param {ASTNode} node parent node to check
1255
+ * @returns {Object} fixer object
1256
+ */
1257
+ function fixObjectWithValueSeparator(node) {
1258
+ const parentNode = node.parent.parent;
1259
+
1260
+ // fix cases like [{a : { b }}] or [{a : [ b ]}]
1261
+ if (
1262
+ parentNode.parent.type === "ArrayPattern" &&
1263
+ parentNode.properties.length === 1
1264
+ ) {
1265
+ return fixNestedArrayVariable(parentNode);
1266
+ }
1267
+
1268
+ // fix cases like {a: {k}} or {a: [k]}
1269
+ return fixNestedObjectVariable(node);
1270
+ }
1271
+
1272
+ /**
1273
+ * fix ...[[a]] or ...[{a}] like patterns
1274
+ * @param {ASTNode} node parent node to check
1275
+ * @returns {Object} fixer object
1276
+ */
1277
+ function fixRestInPattern(node) {
1278
+ const parentNode = node.parent;
1279
+
1280
+ // fix ...[[a]] or ...[{a}] in function parameters
1281
+ if (isFunction(parentNode)) {
1282
+ if (parentNode.params.length === 1) {
1283
+ return fixer.removeRange(node.range);
1284
+ }
1285
+
1286
+ return fixer.removeRange([
1287
+ getPreviousTokenStart(node),
1288
+ node.range[1],
1289
+ ]);
1290
+ }
1291
+
1292
+ // fix rest in nested array pattern like [[a, ...[b]]]
1293
+ if (parentNode.type === "ArrayPattern") {
1294
+ // fix [[...[b]]]
1295
+ if (hasSingleElement(parentNode)) {
1296
+ if (parentNode.parent.type === "ArrayPattern") {
1297
+ return fixNestedArrayVariable(parentNode);
1298
+ }
1299
+
1300
+ // fix 'const [...[b]] = foo; and function foo([...[b]]) {}
1301
+ return fixVariables(parentNode);
1302
+ }
1303
+
1304
+ // fix [[a, ...[b]]]
1305
+ return fixer.removeRange([
1306
+ getPreviousTokenStart(node),
1307
+ node.range[1],
1308
+ ]);
1309
+ }
1310
+
1311
+ return null;
1312
+ }
1313
+
1314
+ // skip fix when variable has references that would be left behind
1315
+ if (
1316
+ allWriteReferences.some(
1317
+ ref => ref.identifier.range[0] !== id.range[0],
1318
+ )
1319
+ ) {
1320
+ return null;
1321
+ }
1322
+
1323
+ // remove declared variables such as var a; or var a, b;
1324
+ if (parentType === "VariableDeclarator") {
1325
+ if (parent.parent.declarations.length === 1) {
1326
+ // prevent fix of variable in forOf and forIn loops.
1327
+ if (
1328
+ isLoop(parent.parent.parent) &&
1329
+ parent.parent.parent.body !== parent.parent
1330
+ ) {
1331
+ return null;
1332
+ }
1333
+
1334
+ // removes only variable not semicolon in 'if (foo()) var bar;' or in 'loops' or in 'with' statement.
1335
+ if (
1336
+ parent.parent.parent.type === "IfStatement" ||
1337
+ isLoop(parent.parent.parent) ||
1338
+ (parent.parent.parent.type === "WithStatement" &&
1339
+ parent.parent.parent.body === parent.parent)
1340
+ ) {
1341
+ return fixer.replaceText(parent.parent, ";");
1342
+ }
1343
+
1344
+ // if next token is a string it could become a directive if node is removed -> no suggestion.
1345
+ const nextToken = sourceCode.getTokenAfter(parent.parent);
1346
+
1347
+ // if previous token exists and is not ";" or "{" not sure about ASI rules -> no suggestion.
1348
+ const prevToken = sourceCode.getTokenBefore(parent.parent);
1349
+
1350
+ if (
1351
+ nextToken &&
1352
+ isDeclarationNotSafeToRemove(nextToken, prevToken)
1353
+ ) {
1354
+ return null;
1355
+ }
1356
+
1357
+ // remove unused declared variable with single declaration like 'var a = b;'
1358
+ return fixer.removeRange(parent.parent.range);
1359
+ }
1360
+
1361
+ // remove unused declared variable with multiple declaration except first one like 'var a = b, c = d;'
1362
+ if (tokenBefore.value === ",") {
1363
+ return fixer.removeRange([
1364
+ tokenBefore.range[0],
1365
+ parent.range[1],
1366
+ ]);
1367
+ }
1368
+
1369
+ // remove first unused declared variable when there are multiple declarations
1370
+ return fixer.removeRange([
1371
+ parent.range[0],
1372
+ getNextTokenEnd(parent),
1373
+ ]);
1374
+ }
1375
+
1376
+ // remove variables in object patterns
1377
+ if (parent.parent.type === "ObjectPattern") {
1378
+ if (parent.parent.properties.length === 1) {
1379
+ // fix [a, ...{b}]
1380
+ if (parent.parent.parent.type === "RestElement") {
1381
+ return fixRestInPattern(parent.parent.parent);
1382
+ }
1383
+
1384
+ // fix [{ a }]
1385
+ if (parent.parent.parent.type === "ArrayPattern") {
1386
+ return fixNestedArrayVariable(parent.parent);
1387
+ }
1388
+
1389
+ /*
1390
+ * var {a} = foo;
1391
+ * function a({a}) {}
1392
+ * fix const { a: { b } } = foo;
1393
+ */
1394
+ return fixVariables(parent.parent);
1395
+ }
1396
+
1397
+ // fix const { a:b } = foo;
1398
+ if (tokenBefore.value === ":") {
1399
+ // remove first unused variable in const { a:b } = foo;
1400
+ if (
1401
+ getTokenBeforeValue(parent) === "{" &&
1402
+ getTokenAfterValue(parent) === ","
1403
+ ) {
1404
+ return fixer.removeRange([
1405
+ parent.range[0],
1406
+ getNextTokenEnd(parent),
1407
+ ]);
1408
+ }
1409
+
1410
+ // remove unused variables in const { a: b, c: d } = foo; except first one
1411
+ return fixer.removeRange([
1412
+ getPreviousTokenStart(parent),
1413
+ id.range[1],
1414
+ ]);
1415
+ }
1416
+ }
1417
+
1418
+ // remove unused variables inside an array
1419
+ if (parentType === "ArrayPattern") {
1420
+ if (hasSingleElement(parent)) {
1421
+ // fix [a, ...[b]]
1422
+ if (parent.parent.type === "RestElement") {
1423
+ return fixRestInPattern(parent.parent);
1424
+ }
1425
+
1426
+ // fix [ [a] ]
1427
+ if (parent.parent.type === "ArrayPattern") {
1428
+ return fixNestedArrayVariable(parent);
1429
+ }
1430
+
1431
+ /*
1432
+ * fix var [a] = foo;
1433
+ * fix function foo([a]) {}
1434
+ * fix const { a: [b] } = foo;
1435
+ */
1436
+ return fixVariables(parent);
1437
+ }
1438
+
1439
+ // if "a" is unused in [a, b ,c] fixes to [, b, c]
1440
+ if (tokenBefore.value === "," && tokenAfter.value === ",") {
1441
+ return fixer.removeRange(id.range);
1442
+ }
1443
+ }
1444
+
1445
+ // remove unused rest elements
1446
+ if (parentType === "RestElement") {
1447
+ // fix [a, ...rest]
1448
+ if (parent.parent.type === "ArrayPattern") {
1449
+ if (hasSingleElement(parent.parent)) {
1450
+ // fix [[...rest]] when there is only rest element
1451
+ if (parent.parent.parent.type === "ArrayPattern") {
1452
+ return fixNestedArrayVariable(parent.parent);
1453
+ }
1454
+
1455
+ // fix 'const [...rest] = foo;' and 'function foo([...rest]) {}'
1456
+ return fixVariables(parent.parent);
1457
+ }
1458
+
1459
+ // fix [a, ...rest]
1460
+ return fixer.removeRange([
1461
+ getPreviousTokenStart(id, 1),
1462
+ id.range[1],
1463
+ ]);
1464
+ }
1465
+
1466
+ // fix { a, ...rest}
1467
+ if (parent.parent.type === "ObjectPattern") {
1468
+ // fix 'const {...rest} = foo;' and 'function foo({...rest}) {}'
1469
+ if (parent.parent.properties.length === 1) {
1470
+ return fixVariables(parent.parent);
1471
+ }
1472
+
1473
+ // fix { a, ...rest} when there are multiple properties
1474
+ return fixer.removeRange([
1475
+ getPreviousTokenStart(id, 1),
1476
+ id.range[1],
1477
+ ]);
1478
+ }
1479
+
1480
+ // fix function foo(...rest) {}
1481
+ if (isFunction(parent.parent)) {
1482
+ // remove unused rest in function parameter if there is only single parameter
1483
+ if (parent.parent.params.length === 1) {
1484
+ return fixer.removeRange(parent.range);
1485
+ }
1486
+
1487
+ // remove unused rest in function parameter if there multiple parameter
1488
+ return fixer.removeRange([
1489
+ getPreviousTokenStart(parent),
1490
+ parent.range[1],
1491
+ ]);
1492
+ }
1493
+ }
1494
+
1495
+ if (parentType === "AssignmentPattern") {
1496
+ // fix [a = aDefault]
1497
+ if (parent.parent.type === "ArrayPattern") {
1498
+ return fixNestedArrayVariable(parent);
1499
+ }
1500
+
1501
+ // fix {a = aDefault}
1502
+ if (parent.parent.parent.type === "ObjectPattern") {
1503
+ if (parent.parent.parent.properties.length === 1) {
1504
+ // fixes [{a = aDefault}]
1505
+ if (
1506
+ parent.parent.parent.parent.type === "ArrayPattern"
1507
+ ) {
1508
+ return fixNestedArrayVariable(parent.parent.parent);
1509
+ }
1510
+
1511
+ // fix 'const {a = aDefault} = foo;' and 'function foo({a = aDefault}) {}'
1512
+ return fixVariables(parent.parent.parent);
1513
+ }
1514
+
1515
+ // fix unused 'a' in {a = aDefault} if it is the first property
1516
+ if (
1517
+ getTokenBeforeValue(parent.parent) === "{" &&
1518
+ getTokenAfterValue(parent.parent) === ","
1519
+ ) {
1520
+ return fixer.removeRange([
1521
+ parent.parent.range[0],
1522
+ getNextTokenEnd(parent.parent),
1523
+ ]);
1524
+ }
1525
+
1526
+ // fix unused 'b' in {a, b = aDefault} if it is not the first property
1527
+ return fixer.removeRange([
1528
+ getPreviousTokenStart(parent.parent),
1529
+ parent.parent.range[1],
1530
+ ]);
1531
+ }
1532
+
1533
+ // fix unused assignment patterns in function parameters
1534
+ if (isFunction(parent.parent)) {
1535
+ return fixFunctionParameters(parent);
1536
+ }
1537
+ }
1538
+
1539
+ // remove unused functions
1540
+ if (parentType === "FunctionDeclaration" && parent.id === id) {
1541
+ return fixer.removeRange(parent.range);
1542
+ }
1543
+
1544
+ // remove unused default import
1545
+ if (parentType === "ImportDefaultSpecifier") {
1546
+ // remove unused default import when there are not other imports
1547
+ if (
1548
+ !hasImportOfCertainType(parent.parent, "ImportSpecifier") &&
1549
+ !hasImportOfCertainType(
1550
+ parent.parent,
1551
+ "ImportNamespaceSpecifier",
1552
+ )
1553
+ ) {
1554
+ return fixer.removeRange([
1555
+ parent.range[0],
1556
+ parent.parent.source.range[0],
1557
+ ]);
1558
+ }
1559
+
1560
+ // remove unused default import when there are other imports also
1561
+ return fixer.removeRange([id.range[0], tokenAfter.range[1]]);
1562
+ }
1563
+
1564
+ if (parentType === "ImportSpecifier") {
1565
+ // remove unused imports when there is a single import
1566
+ if (
1567
+ parent.parent.specifiers.filter(
1568
+ e => e.type === "ImportSpecifier",
1569
+ ).length === 1
1570
+ ) {
1571
+ // remove unused import when there is no default import
1572
+ if (
1573
+ !hasImportOfCertainType(
1574
+ parent.parent,
1575
+ "ImportDefaultSpecifier",
1576
+ )
1577
+ ) {
1578
+ return fixer.removeRange(parent.parent.range);
1579
+ }
1580
+
1581
+ // fixes "import foo from 'module';" to "import 'module';"
1582
+ return fixer.removeRange([
1583
+ getPreviousTokenStart(parent, 1),
1584
+ tokenAfter.range[1],
1585
+ ]);
1586
+ }
1587
+
1588
+ if (getTokenBeforeValue(parent) === "{") {
1589
+ return fixer.removeRange([
1590
+ parent.range[0],
1591
+ getNextTokenEnd(parent),
1592
+ ]);
1593
+ }
1594
+
1595
+ return fixer.removeRange([
1596
+ getPreviousTokenStart(parent),
1597
+ parent.range[1],
1598
+ ]);
1599
+ }
1600
+
1601
+ if (parentType === "ImportNamespaceSpecifier") {
1602
+ if (
1603
+ hasImportOfCertainType(
1604
+ parent.parent,
1605
+ "ImportDefaultSpecifier",
1606
+ )
1607
+ ) {
1608
+ return fixer.removeRange([
1609
+ getPreviousTokenStart(parent),
1610
+ parent.range[1],
1611
+ ]);
1612
+ }
1613
+
1614
+ // fixes "import * as foo from 'module';" to "import 'module';"
1615
+ return fixer.removeRange([
1616
+ parent.range[0],
1617
+ parent.parent.source.range[0],
1618
+ ]);
1619
+ }
1620
+
1621
+ // skip error in catch(error) variable
1622
+ if (parentType === "CatchClause") {
1623
+ return null;
1624
+ }
1625
+
1626
+ // remove unused declared classes
1627
+ if (parentType === "ClassDeclaration") {
1628
+ return fixer.removeRange(parent.range);
1629
+ }
1630
+
1631
+ // remove unused variable that is in a sequence [a,b] fixes to [a]
1632
+ if (tokenBefore?.value === ",") {
1633
+ return fixer.removeRange([tokenBefore.range[0], id.range[1]]);
1634
+ }
1635
+
1636
+ // remove unused variable that is in a sequence inside function arguments and object pattern
1637
+ if (tokenAfter.value === ",") {
1638
+ // fix function foo(a, b) {}
1639
+ if (tokenBefore.value === "(") {
1640
+ return fixer.removeRange([
1641
+ id.range[0],
1642
+ tokenAfter.range[1],
1643
+ ]);
1644
+ }
1645
+
1646
+ // fix const {a, b} = foo;
1647
+ if (tokenBefore.value === "{") {
1648
+ return fixer.removeRange([
1649
+ id.range[0],
1650
+ tokenAfter.range[1],
1651
+ ]);
1652
+ }
1653
+ }
1654
+
1655
+ if (
1656
+ parentType === "ArrowFunctionExpression" &&
1657
+ parent.params.length === 1 &&
1658
+ tokenAfter?.value !== ")"
1659
+ ) {
1660
+ return fixer.replaceText(id, "()");
1661
+ }
1662
+
1663
+ return fixer.removeRange(id.range);
1664
+ }
1665
+
1666
+ //--------------------------------------------------------------------------
1667
+ // Public
1668
+ //--------------------------------------------------------------------------
1669
+
1670
+ return {
1671
+ "Program:exit"(programNode) {
1672
+ const unusedVars = collectUnusedVariables(
1673
+ sourceCode.getScope(programNode),
1674
+ [],
1675
+ );
1676
+
1677
+ for (let i = 0, l = unusedVars.length; i < l; ++i) {
1678
+ const unusedVar = unusedVars[i];
1679
+
1680
+ // Report the first declaration.
1681
+ if (unusedVar.defs.length > 0) {
1682
+ // report last write reference, https://github.com/eslint/eslint/issues/14324
1683
+ const writeReferences = unusedVar.references.filter(
1684
+ ref =>
1685
+ ref.isWrite() &&
1686
+ ref.from.variableScope ===
1687
+ unusedVar.scope.variableScope,
1688
+ );
1689
+
1690
+ let referenceToReport;
1691
+
1692
+ if (writeReferences.length > 0) {
1693
+ referenceToReport = writeReferences.at(-1);
1694
+ }
1695
+
1696
+ context.report({
1697
+ node: referenceToReport
1698
+ ? referenceToReport.identifier
1699
+ : unusedVar.identifiers[0],
1700
+ messageId: "unusedVar",
1701
+ data: unusedVar.references.some(ref =>
1702
+ ref.isWrite(),
1703
+ )
1704
+ ? getAssignedMessageData(unusedVar)
1705
+ : getDefinedMessageData(unusedVar),
1706
+ suggest: [
1707
+ {
1708
+ messageId: "removeVar",
1709
+ data: {
1710
+ varName: unusedVar.name,
1711
+ },
1712
+ fix(fixer) {
1713
+ return handleFixes(fixer, unusedVar);
1714
+ },
1715
+ },
1716
+ ],
1717
+ });
1718
+
1719
+ // If there are no regular declaration, report the first `/*globals*/` comment directive.
1720
+ } else if (unusedVar.eslintExplicitGlobalComments) {
1721
+ const directiveComment =
1722
+ unusedVar.eslintExplicitGlobalComments[0];
1723
+
1724
+ context.report({
1725
+ node: programNode,
1726
+ loc: astUtils.getNameLocationInGlobalDirectiveComment(
1727
+ sourceCode,
1728
+ directiveComment,
1729
+ unusedVar.name,
1730
+ ),
1731
+ messageId: "unusedVar",
1732
+ data: getDefinedMessageData(unusedVar),
1733
+ });
1734
+ }
1735
+ }
1736
+ },
1737
+ };
1738
+ },
718
1739
  };