eslint 9.22.0 → 9.24.0

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