eslint 9.21.0 → 9.23.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 (425) hide show
  1. package/README.md +52 -48
  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 +828 -808
  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 +541 -457
  21. package/lib/config/config-loader.js +648 -618
  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 +65 -68
  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 +12 -0
  29. package/lib/eslint/eslint-helpers.js +709 -679
  30. package/lib/eslint/eslint.js +944 -886
  31. package/lib/eslint/index.js +2 -2
  32. package/lib/eslint/legacy-eslint.js +576 -532
  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 +1129 -1054
  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 +2402 -2044
  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 +35 -35
  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 +375 -361
  71. package/lib/rule-tester/index.js +1 -1
  72. package/lib/rule-tester/rule-tester.js +1307 -1045
  73. package/lib/rules/accessor-pairs.js +297 -262
  74. package/lib/rules/array-bracket-newline.js +249 -237
  75. package/lib/rules/array-bracket-spacing.js +262 -223
  76. package/lib/rules/array-callback-return.js +401 -355
  77. package/lib/rules/array-element-newline.js +357 -312
  78. package/lib/rules/arrow-body-style.js +399 -280
  79. package/lib/rules/arrow-parens.js +205 -172
  80. package/lib/rules/arrow-spacing.js +168 -162
  81. package/lib/rules/block-scoped-var.js +124 -122
  82. package/lib/rules/block-spacing.js +185 -175
  83. package/lib/rules/brace-style.js +261 -198
  84. package/lib/rules/callback-return.js +202 -189
  85. package/lib/rules/camelcase.js +402 -391
  86. package/lib/rules/capitalized-comments.js +252 -231
  87. package/lib/rules/class-methods-use-this.js +179 -171
  88. package/lib/rules/comma-dangle.js +378 -345
  89. package/lib/rules/comma-spacing.js +192 -194
  90. package/lib/rules/comma-style.js +374 -315
  91. package/lib/rules/complexity.js +172 -168
  92. package/lib/rules/computed-property-spacing.js +235 -210
  93. package/lib/rules/consistent-return.js +180 -169
  94. package/lib/rules/consistent-this.js +166 -146
  95. package/lib/rules/constructor-super.js +411 -403
  96. package/lib/rules/curly.js +406 -331
  97. package/lib/rules/default-case-last.js +37 -30
  98. package/lib/rules/default-case.js +88 -84
  99. package/lib/rules/default-param-last.js +68 -53
  100. package/lib/rules/dot-location.js +121 -109
  101. package/lib/rules/dot-notation.js +191 -155
  102. package/lib/rules/eol-last.js +121 -119
  103. package/lib/rules/eqeqeq.js +167 -154
  104. package/lib/rules/for-direction.js +145 -120
  105. package/lib/rules/func-call-spacing.js +260 -230
  106. package/lib/rules/func-name-matching.js +292 -208
  107. package/lib/rules/func-names.js +164 -163
  108. package/lib/rules/func-style.js +158 -126
  109. package/lib/rules/function-call-argument-newline.js +151 -128
  110. package/lib/rules/function-paren-newline.js +348 -290
  111. package/lib/rules/generator-star-spacing.js +228 -209
  112. package/lib/rules/getter-return.js +207 -171
  113. package/lib/rules/global-require.js +84 -73
  114. package/lib/rules/grouped-accessor-pairs.js +169 -149
  115. package/lib/rules/guard-for-in.js +71 -62
  116. package/lib/rules/handle-callback-err.js +107 -102
  117. package/lib/rules/id-blacklist.js +181 -198
  118. package/lib/rules/id-denylist.js +167 -186
  119. package/lib/rules/id-length.js +196 -170
  120. package/lib/rules/id-match.js +343 -288
  121. package/lib/rules/implicit-arrow-linebreak.js +101 -78
  122. package/lib/rules/indent-legacy.js +1343 -1117
  123. package/lib/rules/indent.js +2271 -1758
  124. package/lib/rules/index.js +317 -292
  125. package/lib/rules/init-declarations.js +115 -106
  126. package/lib/rules/jsx-quotes.js +93 -81
  127. package/lib/rules/key-spacing.js +749 -632
  128. package/lib/rules/keyword-spacing.js +647 -604
  129. package/lib/rules/line-comment-position.js +141 -127
  130. package/lib/rules/linebreak-style.js +106 -105
  131. package/lib/rules/lines-around-comment.js +539 -447
  132. package/lib/rules/lines-around-directive.js +232 -202
  133. package/lib/rules/lines-between-class-members.js +304 -233
  134. package/lib/rules/logical-assignment-operators.js +581 -398
  135. package/lib/rules/max-classes-per-file.js +68 -67
  136. package/lib/rules/max-depth.js +145 -142
  137. package/lib/rules/max-len.js +472 -433
  138. package/lib/rules/max-lines-per-function.js +200 -175
  139. package/lib/rules/max-lines.js +157 -161
  140. package/lib/rules/max-nested-callbacks.js +101 -103
  141. package/lib/rules/max-params.js +77 -75
  142. package/lib/rules/max-statements-per-line.js +204 -197
  143. package/lib/rules/max-statements.js +167 -163
  144. package/lib/rules/multiline-comment-style.js +636 -478
  145. package/lib/rules/multiline-ternary.js +240 -175
  146. package/lib/rules/new-cap.js +232 -212
  147. package/lib/rules/new-parens.js +87 -78
  148. package/lib/rules/newline-after-var.js +286 -249
  149. package/lib/rules/newline-before-return.js +228 -221
  150. package/lib/rules/newline-per-chained-call.js +141 -126
  151. package/lib/rules/no-alert.js +89 -78
  152. package/lib/rules/no-array-constructor.js +121 -112
  153. package/lib/rules/no-async-promise-executor.js +29 -23
  154. package/lib/rules/no-await-in-loop.js +68 -70
  155. package/lib/rules/no-bitwise.js +123 -99
  156. package/lib/rules/no-buffer-constructor.js +54 -46
  157. package/lib/rules/no-caller.js +38 -32
  158. package/lib/rules/no-case-declarations.js +60 -56
  159. package/lib/rules/no-catch-shadow.js +75 -72
  160. package/lib/rules/no-class-assign.js +50 -47
  161. package/lib/rules/no-compare-neg-zero.js +61 -47
  162. package/lib/rules/no-cond-assign.js +147 -131
  163. package/lib/rules/no-confusing-arrow.js +97 -80
  164. package/lib/rules/no-console.js +201 -190
  165. package/lib/rules/no-const-assign.js +46 -40
  166. package/lib/rules/no-constant-binary-expression.js +499 -404
  167. package/lib/rules/no-constant-condition.js +157 -142
  168. package/lib/rules/no-constructor-return.js +48 -48
  169. package/lib/rules/no-continue.js +24 -26
  170. package/lib/rules/no-control-regex.js +124 -120
  171. package/lib/rules/no-debugger.js +27 -29
  172. package/lib/rules/no-delete-var.js +28 -28
  173. package/lib/rules/no-div-regex.js +46 -40
  174. package/lib/rules/no-dupe-args.js +67 -68
  175. package/lib/rules/no-dupe-class-members.js +92 -88
  176. package/lib/rules/no-dupe-else-if.js +99 -76
  177. package/lib/rules/no-dupe-keys.js +132 -109
  178. package/lib/rules/no-duplicate-case.js +49 -42
  179. package/lib/rules/no-duplicate-imports.js +178 -175
  180. package/lib/rules/no-else-return.js +429 -384
  181. package/lib/rules/no-empty-character-class.js +56 -49
  182. package/lib/rules/no-empty-function.js +126 -127
  183. package/lib/rules/no-empty-pattern.js +62 -57
  184. package/lib/rules/no-empty-static-block.js +36 -34
  185. package/lib/rules/no-empty.js +97 -85
  186. package/lib/rules/no-eq-null.js +36 -31
  187. package/lib/rules/no-eval.js +255 -249
  188. package/lib/rules/no-ex-assign.js +41 -38
  189. package/lib/rules/no-extend-native.js +160 -158
  190. package/lib/rules/no-extra-bind.js +200 -189
  191. package/lib/rules/no-extra-boolean-cast.js +397 -347
  192. package/lib/rules/no-extra-label.js +149 -130
  193. package/lib/rules/no-extra-parens.js +1653 -1324
  194. package/lib/rules/no-extra-semi.js +145 -143
  195. package/lib/rules/no-fallthrough.js +198 -156
  196. package/lib/rules/no-floating-decimal.js +73 -65
  197. package/lib/rules/no-func-assign.js +53 -54
  198. package/lib/rules/no-global-assign.js +77 -72
  199. package/lib/rules/no-implicit-coercion.js +348 -292
  200. package/lib/rules/no-implicit-globals.js +157 -134
  201. package/lib/rules/no-implied-eval.js +139 -111
  202. package/lib/rules/no-import-assign.js +144 -158
  203. package/lib/rules/no-inline-comments.js +100 -94
  204. package/lib/rules/no-inner-declarations.js +114 -100
  205. package/lib/rules/no-invalid-regexp.js +221 -189
  206. package/lib/rules/no-invalid-this.js +122 -116
  207. package/lib/rules/no-irregular-whitespace.js +265 -251
  208. package/lib/rules/no-iterator.js +28 -32
  209. package/lib/rules/no-label-var.js +58 -61
  210. package/lib/rules/no-labels.js +137 -132
  211. package/lib/rules/no-lone-blocks.js +126 -122
  212. package/lib/rules/no-lonely-if.js +107 -76
  213. package/lib/rules/no-loop-func.js +233 -212
  214. package/lib/rules/no-loss-of-precision.js +215 -200
  215. package/lib/rules/no-magic-numbers.js +245 -217
  216. package/lib/rules/no-misleading-character-class.js +498 -445
  217. package/lib/rules/no-mixed-operators.js +187 -181
  218. package/lib/rules/no-mixed-requires.js +252 -239
  219. package/lib/rules/no-mixed-spaces-and-tabs.js +133 -120
  220. package/lib/rules/no-multi-assign.js +45 -43
  221. package/lib/rules/no-multi-spaces.js +162 -142
  222. package/lib/rules/no-multi-str.js +41 -40
  223. package/lib/rules/no-multiple-empty-lines.js +195 -157
  224. package/lib/rules/no-native-reassign.js +89 -84
  225. package/lib/rules/no-negated-condition.js +78 -74
  226. package/lib/rules/no-negated-in-lhs.js +44 -42
  227. package/lib/rules/no-nested-ternary.js +32 -31
  228. package/lib/rules/no-new-func.js +70 -61
  229. package/lib/rules/no-new-native-nonconstructor.js +42 -38
  230. package/lib/rules/no-new-object.js +47 -47
  231. package/lib/rules/no-new-require.js +47 -46
  232. package/lib/rules/no-new-symbol.js +51 -49
  233. package/lib/rules/no-new-wrappers.js +42 -40
  234. package/lib/rules/no-new.js +27 -28
  235. package/lib/rules/no-nonoctal-decimal-escape.js +140 -120
  236. package/lib/rules/no-obj-calls.js +65 -52
  237. package/lib/rules/no-object-constructor.js +103 -96
  238. package/lib/rules/no-octal-escape.js +39 -42
  239. package/lib/rules/no-octal.js +31 -31
  240. package/lib/rules/no-param-reassign.js +234 -216
  241. package/lib/rules/no-path-concat.js +65 -66
  242. package/lib/rules/no-plusplus.js +59 -60
  243. package/lib/rules/no-process-env.js +48 -47
  244. package/lib/rules/no-process-exit.js +53 -49
  245. package/lib/rules/no-promise-executor-return.js +213 -181
  246. package/lib/rules/no-proto.js +25 -28
  247. package/lib/rules/no-prototype-builtins.js +145 -123
  248. package/lib/rules/no-redeclare.js +153 -151
  249. package/lib/rules/no-regex-spaces.js +182 -160
  250. package/lib/rules/no-restricted-exports.js +207 -184
  251. package/lib/rules/no-restricted-globals.js +110 -111
  252. package/lib/rules/no-restricted-imports.js +656 -536
  253. package/lib/rules/no-restricted-modules.js +221 -201
  254. package/lib/rules/no-restricted-properties.js +180 -152
  255. package/lib/rules/no-restricted-syntax.js +55 -51
  256. package/lib/rules/no-return-assign.js +54 -49
  257. package/lib/rules/no-return-await.js +147 -123
  258. package/lib/rules/no-script-url.js +51 -44
  259. package/lib/rules/no-self-assign.js +147 -145
  260. package/lib/rules/no-self-compare.js +62 -45
  261. package/lib/rules/no-sequences.js +134 -115
  262. package/lib/rules/no-setter-return.js +184 -151
  263. package/lib/rules/no-shadow-restricted-names.js +60 -45
  264. package/lib/rules/no-shadow.js +341 -315
  265. package/lib/rules/no-spaced-func.js +81 -76
  266. package/lib/rules/no-sparse-arrays.js +53 -58
  267. package/lib/rules/no-sync.js +60 -59
  268. package/lib/rules/no-tabs.js +82 -71
  269. package/lib/rules/no-template-curly-in-string.js +32 -31
  270. package/lib/rules/no-ternary.js +24 -28
  271. package/lib/rules/no-this-before-super.js +320 -318
  272. package/lib/rules/no-throw-literal.js +30 -35
  273. package/lib/rules/no-trailing-spaces.js +198 -190
  274. package/lib/rules/no-undef-init.js +75 -60
  275. package/lib/rules/no-undef.js +50 -47
  276. package/lib/rules/no-undefined.js +72 -74
  277. package/lib/rules/no-underscore-dangle.js +369 -326
  278. package/lib/rules/no-unexpected-multiline.js +111 -101
  279. package/lib/rules/no-unmodified-loop-condition.js +253 -253
  280. package/lib/rules/no-unneeded-ternary.js +211 -146
  281. package/lib/rules/no-unreachable-loop.js +144 -141
  282. package/lib/rules/no-unreachable.js +254 -247
  283. package/lib/rules/no-unsafe-finally.js +92 -84
  284. package/lib/rules/no-unsafe-negation.js +104 -82
  285. package/lib/rules/no-unsafe-optional-chaining.js +191 -177
  286. package/lib/rules/no-unused-expressions.js +177 -161
  287. package/lib/rules/no-unused-labels.js +138 -123
  288. package/lib/rules/no-unused-private-class-members.js +205 -181
  289. package/lib/rules/no-unused-vars.js +1668 -1448
  290. package/lib/rules/no-use-before-define.js +228 -230
  291. package/lib/rules/no-useless-assignment.js +589 -510
  292. package/lib/rules/no-useless-backreference.js +211 -192
  293. package/lib/rules/no-useless-call.js +57 -52
  294. package/lib/rules/no-useless-catch.js +39 -39
  295. package/lib/rules/no-useless-computed-key.js +143 -114
  296. package/lib/rules/no-useless-concat.js +64 -59
  297. package/lib/rules/no-useless-constructor.js +157 -110
  298. package/lib/rules/no-useless-escape.js +341 -290
  299. package/lib/rules/no-useless-rename.js +182 -155
  300. package/lib/rules/no-useless-return.js +343 -311
  301. package/lib/rules/no-var.js +232 -211
  302. package/lib/rules/no-void.js +49 -47
  303. package/lib/rules/no-warning-comments.js +190 -185
  304. package/lib/rules/no-whitespace-before-property.js +130 -114
  305. package/lib/rules/no-with.js +23 -25
  306. package/lib/rules/nonblock-statement-body-position.js +148 -129
  307. package/lib/rules/object-curly-newline.js +305 -264
  308. package/lib/rules/object-curly-spacing.js +359 -313
  309. package/lib/rules/object-property-newline.js +136 -105
  310. package/lib/rules/object-shorthand.js +606 -501
  311. package/lib/rules/one-var-declaration-per-line.js +103 -99
  312. package/lib/rules/one-var.js +652 -536
  313. package/lib/rules/operator-assignment.js +218 -160
  314. package/lib/rules/operator-linebreak.js +294 -250
  315. package/lib/rules/padded-blocks.js +345 -307
  316. package/lib/rules/padding-line-between-statements.js +442 -438
  317. package/lib/rules/prefer-arrow-callback.js +361 -312
  318. package/lib/rules/prefer-const.js +417 -376
  319. package/lib/rules/prefer-destructuring.js +300 -278
  320. package/lib/rules/prefer-exponentiation-operator.js +175 -132
  321. package/lib/rules/prefer-named-capture-group.js +152 -139
  322. package/lib/rules/prefer-numeric-literals.js +120 -112
  323. package/lib/rules/prefer-object-has-own.js +115 -81
  324. package/lib/rules/prefer-object-spread.js +212 -192
  325. package/lib/rules/prefer-promise-reject-errors.js +139 -121
  326. package/lib/rules/prefer-reflect.js +126 -106
  327. package/lib/rules/prefer-regex-literals.js +577 -465
  328. package/lib/rules/prefer-rest-params.js +78 -79
  329. package/lib/rules/prefer-spread.js +46 -43
  330. package/lib/rules/prefer-template.js +265 -194
  331. package/lib/rules/quote-props.js +372 -306
  332. package/lib/rules/quotes.js +373 -325
  333. package/lib/rules/radix.js +151 -135
  334. package/lib/rules/require-atomic-updates.js +315 -284
  335. package/lib/rules/require-await.js +143 -115
  336. package/lib/rules/require-unicode-regexp.js +281 -176
  337. package/lib/rules/require-yield.js +52 -53
  338. package/lib/rules/rest-spread-spacing.js +127 -115
  339. package/lib/rules/semi-spacing.js +280 -249
  340. package/lib/rules/semi-style.js +175 -133
  341. package/lib/rules/semi.js +455 -435
  342. package/lib/rules/sort-imports.js +305 -232
  343. package/lib/rules/sort-keys.js +218 -187
  344. package/lib/rules/sort-vars.js +126 -92
  345. package/lib/rules/space-before-blocks.js +198 -188
  346. package/lib/rules/space-before-function-paren.js +185 -165
  347. package/lib/rules/space-in-parens.js +358 -287
  348. package/lib/rules/space-infix-ops.js +236 -200
  349. package/lib/rules/space-unary-ops.js +355 -297
  350. package/lib/rules/spaced-comment.js +362 -318
  351. package/lib/rules/strict.js +264 -229
  352. package/lib/rules/switch-colon-spacing.js +129 -121
  353. package/lib/rules/symbol-description.js +44 -47
  354. package/lib/rules/template-curly-spacing.js +147 -141
  355. package/lib/rules/template-tag-spacing.js +97 -87
  356. package/lib/rules/unicode-bom.js +53 -55
  357. package/lib/rules/use-isnan.js +236 -205
  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 +152 -110
  370. package/lib/rules/vars-on-top.js +151 -144
  371. package/lib/rules/wrap-iife.js +203 -190
  372. package/lib/rules/wrap-regex.js +69 -57
  373. package/lib/rules/yield-star-spacing.js +144 -133
  374. package/lib/rules/yoda.js +282 -271
  375. package/lib/services/parser-service.js +35 -35
  376. package/lib/services/processor-service.js +66 -73
  377. package/lib/shared/ajv.js +14 -14
  378. package/lib/shared/assert.js +3 -4
  379. package/lib/shared/ast-utils.js +7 -6
  380. package/lib/shared/deep-merge-arrays.js +24 -22
  381. package/lib/shared/directives.js +3 -2
  382. package/lib/shared/flags.js +46 -17
  383. package/lib/shared/logging.js +24 -25
  384. package/lib/shared/option-utils.js +43 -36
  385. package/lib/shared/runtime-info.js +136 -127
  386. package/lib/shared/serialization.js +27 -27
  387. package/lib/shared/severity.js +22 -22
  388. package/lib/shared/stats.js +5 -5
  389. package/lib/shared/string-utils.js +16 -16
  390. package/lib/shared/text-table.js +28 -27
  391. package/lib/shared/traverser.js +153 -146
  392. package/lib/types/config-api.d.ts +8 -0
  393. package/lib/types/index.d.ts +2010 -1559
  394. package/lib/types/rules.d.ts +5312 -0
  395. package/lib/types/use-at-your-own-risk.d.ts +32 -30
  396. package/lib/unsupported-api.js +5 -5
  397. package/messages/all-files-ignored.js +3 -3
  398. package/messages/all-matched-files-ignored.js +3 -3
  399. package/messages/config-file-missing.js +2 -2
  400. package/messages/config-plugin-missing.js +3 -3
  401. package/messages/config-serialize-function.js +9 -7
  402. package/messages/eslintrc-incompat.js +13 -15
  403. package/messages/eslintrc-plugins.js +3 -4
  404. package/messages/extend-config-missing.js +3 -3
  405. package/messages/failed-to-read-json.js +3 -3
  406. package/messages/file-not-found.js +3 -3
  407. package/messages/invalid-rule-options.js +2 -2
  408. package/messages/invalid-rule-severity.js +2 -2
  409. package/messages/no-config-found.js +3 -3
  410. package/messages/plugin-conflict.js +8 -8
  411. package/messages/plugin-invalid.js +3 -3
  412. package/messages/plugin-missing.js +3 -3
  413. package/messages/print-config-with-directory-path.js +2 -2
  414. package/messages/shared.js +6 -1
  415. package/messages/whitespace-found.js +3 -3
  416. package/package.json +22 -20
  417. package/lib/types/rules/best-practices.d.ts +0 -1143
  418. package/lib/types/rules/deprecated.d.ts +0 -252
  419. package/lib/types/rules/ecmascript-6.d.ts +0 -647
  420. package/lib/types/rules/index.d.ts +0 -50
  421. package/lib/types/rules/node-commonjs.d.ts +0 -171
  422. package/lib/types/rules/possible-errors.d.ts +0 -685
  423. package/lib/types/rules/strict-mode.d.ts +0 -38
  424. package/lib/types/rules/stylistic-issues.d.ts +0 -2043
  425. package/lib/types/rules/variables.d.ts +0 -234
@@ -41,1452 +41,1672 @@ const astUtils = require("./utils/ast-utils");
41
41
 
42
42
  /** @type {import('../shared/types').Rule} */
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 varible 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 varible 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
  };