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.
- package/README.md +52 -48
- package/bin/eslint.js +92 -90
- package/conf/default-cli-options.js +22 -22
- package/conf/ecma-version.js +1 -1
- package/conf/globals.js +97 -98
- package/conf/replacements.json +24 -20
- package/conf/rule-type-list.json +88 -92
- package/lib/api.js +12 -12
- package/lib/cli-engine/cli-engine.js +828 -808
- package/lib/cli-engine/file-enumerator.js +381 -387
- package/lib/cli-engine/formatters/formatters-meta.json +16 -16
- package/lib/cli-engine/formatters/html.js +107 -99
- package/lib/cli-engine/formatters/json-with-metadata.js +5 -5
- package/lib/cli-engine/formatters/json.js +2 -2
- package/lib/cli-engine/formatters/stylish.js +96 -75
- package/lib/cli-engine/hash.js +1 -1
- package/lib/cli-engine/index.js +1 -1
- package/lib/cli-engine/lint-result-cache.js +144 -145
- package/lib/cli-engine/load-rules.js +16 -16
- package/lib/cli.js +541 -457
- package/lib/config/config-loader.js +648 -618
- package/lib/config/config.js +247 -221
- package/lib/config/default-config.js +54 -45
- package/lib/config/flat-config-array.js +167 -172
- package/lib/config/flat-config-helpers.js +65 -68
- package/lib/config/flat-config-schema.js +375 -368
- package/lib/config/rule-validator.js +139 -144
- package/lib/config-api.js +12 -0
- package/lib/eslint/eslint-helpers.js +709 -679
- package/lib/eslint/eslint.js +944 -886
- package/lib/eslint/index.js +2 -2
- package/lib/eslint/legacy-eslint.js +576 -532
- package/lib/languages/js/index.js +263 -264
- package/lib/languages/js/source-code/index.js +1 -1
- package/lib/languages/js/source-code/source-code.js +1129 -1054
- package/lib/languages/js/source-code/token-store/backward-token-comment-cursor.js +39 -35
- package/lib/languages/js/source-code/token-store/backward-token-cursor.js +35 -36
- package/lib/languages/js/source-code/token-store/cursor.js +36 -36
- package/lib/languages/js/source-code/token-store/cursors.js +80 -52
- package/lib/languages/js/source-code/token-store/decorative-cursor.js +17 -18
- package/lib/languages/js/source-code/token-store/filter-cursor.js +19 -20
- package/lib/languages/js/source-code/token-store/forward-token-comment-cursor.js +40 -32
- package/lib/languages/js/source-code/token-store/forward-token-cursor.js +40 -41
- package/lib/languages/js/source-code/token-store/index.js +592 -498
- package/lib/languages/js/source-code/token-store/limit-cursor.js +17 -18
- package/lib/languages/js/source-code/token-store/padded-token-cursor.js +23 -16
- package/lib/languages/js/source-code/token-store/skip-cursor.js +19 -20
- package/lib/languages/js/source-code/token-store/utils.js +63 -60
- package/lib/languages/js/validate-language-options.js +104 -89
- package/lib/linter/apply-disable-directives.js +467 -383
- package/lib/linter/code-path-analysis/code-path-analyzer.js +650 -672
- package/lib/linter/code-path-analysis/code-path-segment.js +215 -216
- package/lib/linter/code-path-analysis/code-path-state.js +2118 -2096
- package/lib/linter/code-path-analysis/code-path.js +307 -319
- package/lib/linter/code-path-analysis/debug-helpers.js +183 -163
- package/lib/linter/code-path-analysis/fork-context.js +296 -271
- package/lib/linter/code-path-analysis/id-generator.js +22 -23
- package/lib/linter/file-context.js +119 -120
- package/lib/linter/index.js +3 -3
- package/lib/linter/interpolate.js +16 -16
- package/lib/linter/linter.js +2402 -2044
- package/lib/linter/node-event-generator.js +284 -225
- package/lib/linter/report-translator.js +256 -219
- package/lib/linter/rule-fixer.js +122 -124
- package/lib/linter/rules.js +35 -35
- package/lib/linter/safe-emitter.js +18 -18
- package/lib/linter/source-code-fixer.js +94 -92
- package/lib/linter/timing.js +104 -101
- package/lib/linter/vfile.js +70 -73
- package/lib/options.js +375 -361
- package/lib/rule-tester/index.js +1 -1
- package/lib/rule-tester/rule-tester.js +1307 -1045
- package/lib/rules/accessor-pairs.js +297 -262
- package/lib/rules/array-bracket-newline.js +249 -237
- package/lib/rules/array-bracket-spacing.js +262 -223
- package/lib/rules/array-callback-return.js +401 -355
- package/lib/rules/array-element-newline.js +357 -312
- package/lib/rules/arrow-body-style.js +399 -280
- package/lib/rules/arrow-parens.js +205 -172
- package/lib/rules/arrow-spacing.js +168 -162
- package/lib/rules/block-scoped-var.js +124 -122
- package/lib/rules/block-spacing.js +185 -175
- package/lib/rules/brace-style.js +261 -198
- package/lib/rules/callback-return.js +202 -189
- package/lib/rules/camelcase.js +402 -391
- package/lib/rules/capitalized-comments.js +252 -231
- package/lib/rules/class-methods-use-this.js +179 -171
- package/lib/rules/comma-dangle.js +378 -345
- package/lib/rules/comma-spacing.js +192 -194
- package/lib/rules/comma-style.js +374 -315
- package/lib/rules/complexity.js +172 -168
- package/lib/rules/computed-property-spacing.js +235 -210
- package/lib/rules/consistent-return.js +180 -169
- package/lib/rules/consistent-this.js +166 -146
- package/lib/rules/constructor-super.js +411 -403
- package/lib/rules/curly.js +406 -331
- package/lib/rules/default-case-last.js +37 -30
- package/lib/rules/default-case.js +88 -84
- package/lib/rules/default-param-last.js +68 -53
- package/lib/rules/dot-location.js +121 -109
- package/lib/rules/dot-notation.js +191 -155
- package/lib/rules/eol-last.js +121 -119
- package/lib/rules/eqeqeq.js +167 -154
- package/lib/rules/for-direction.js +145 -120
- package/lib/rules/func-call-spacing.js +260 -230
- package/lib/rules/func-name-matching.js +292 -208
- package/lib/rules/func-names.js +164 -163
- package/lib/rules/func-style.js +158 -126
- package/lib/rules/function-call-argument-newline.js +151 -128
- package/lib/rules/function-paren-newline.js +348 -290
- package/lib/rules/generator-star-spacing.js +228 -209
- package/lib/rules/getter-return.js +207 -171
- package/lib/rules/global-require.js +84 -73
- package/lib/rules/grouped-accessor-pairs.js +169 -149
- package/lib/rules/guard-for-in.js +71 -62
- package/lib/rules/handle-callback-err.js +107 -102
- package/lib/rules/id-blacklist.js +181 -198
- package/lib/rules/id-denylist.js +167 -186
- package/lib/rules/id-length.js +196 -170
- package/lib/rules/id-match.js +343 -288
- package/lib/rules/implicit-arrow-linebreak.js +101 -78
- package/lib/rules/indent-legacy.js +1343 -1117
- package/lib/rules/indent.js +2271 -1758
- package/lib/rules/index.js +317 -292
- package/lib/rules/init-declarations.js +115 -106
- package/lib/rules/jsx-quotes.js +93 -81
- package/lib/rules/key-spacing.js +749 -632
- package/lib/rules/keyword-spacing.js +647 -604
- package/lib/rules/line-comment-position.js +141 -127
- package/lib/rules/linebreak-style.js +106 -105
- package/lib/rules/lines-around-comment.js +539 -447
- package/lib/rules/lines-around-directive.js +232 -202
- package/lib/rules/lines-between-class-members.js +304 -233
- package/lib/rules/logical-assignment-operators.js +581 -398
- package/lib/rules/max-classes-per-file.js +68 -67
- package/lib/rules/max-depth.js +145 -142
- package/lib/rules/max-len.js +472 -433
- package/lib/rules/max-lines-per-function.js +200 -175
- package/lib/rules/max-lines.js +157 -161
- package/lib/rules/max-nested-callbacks.js +101 -103
- package/lib/rules/max-params.js +77 -75
- package/lib/rules/max-statements-per-line.js +204 -197
- package/lib/rules/max-statements.js +167 -163
- package/lib/rules/multiline-comment-style.js +636 -478
- package/lib/rules/multiline-ternary.js +240 -175
- package/lib/rules/new-cap.js +232 -212
- package/lib/rules/new-parens.js +87 -78
- package/lib/rules/newline-after-var.js +286 -249
- package/lib/rules/newline-before-return.js +228 -221
- package/lib/rules/newline-per-chained-call.js +141 -126
- package/lib/rules/no-alert.js +89 -78
- package/lib/rules/no-array-constructor.js +121 -112
- package/lib/rules/no-async-promise-executor.js +29 -23
- package/lib/rules/no-await-in-loop.js +68 -70
- package/lib/rules/no-bitwise.js +123 -99
- package/lib/rules/no-buffer-constructor.js +54 -46
- package/lib/rules/no-caller.js +38 -32
- package/lib/rules/no-case-declarations.js +60 -56
- package/lib/rules/no-catch-shadow.js +75 -72
- package/lib/rules/no-class-assign.js +50 -47
- package/lib/rules/no-compare-neg-zero.js +61 -47
- package/lib/rules/no-cond-assign.js +147 -131
- package/lib/rules/no-confusing-arrow.js +97 -80
- package/lib/rules/no-console.js +201 -190
- package/lib/rules/no-const-assign.js +46 -40
- package/lib/rules/no-constant-binary-expression.js +499 -404
- package/lib/rules/no-constant-condition.js +157 -142
- package/lib/rules/no-constructor-return.js +48 -48
- package/lib/rules/no-continue.js +24 -26
- package/lib/rules/no-control-regex.js +124 -120
- package/lib/rules/no-debugger.js +27 -29
- package/lib/rules/no-delete-var.js +28 -28
- package/lib/rules/no-div-regex.js +46 -40
- package/lib/rules/no-dupe-args.js +67 -68
- package/lib/rules/no-dupe-class-members.js +92 -88
- package/lib/rules/no-dupe-else-if.js +99 -76
- package/lib/rules/no-dupe-keys.js +132 -109
- package/lib/rules/no-duplicate-case.js +49 -42
- package/lib/rules/no-duplicate-imports.js +178 -175
- package/lib/rules/no-else-return.js +429 -384
- package/lib/rules/no-empty-character-class.js +56 -49
- package/lib/rules/no-empty-function.js +126 -127
- package/lib/rules/no-empty-pattern.js +62 -57
- package/lib/rules/no-empty-static-block.js +36 -34
- package/lib/rules/no-empty.js +97 -85
- package/lib/rules/no-eq-null.js +36 -31
- package/lib/rules/no-eval.js +255 -249
- package/lib/rules/no-ex-assign.js +41 -38
- package/lib/rules/no-extend-native.js +160 -158
- package/lib/rules/no-extra-bind.js +200 -189
- package/lib/rules/no-extra-boolean-cast.js +397 -347
- package/lib/rules/no-extra-label.js +149 -130
- package/lib/rules/no-extra-parens.js +1653 -1324
- package/lib/rules/no-extra-semi.js +145 -143
- package/lib/rules/no-fallthrough.js +198 -156
- package/lib/rules/no-floating-decimal.js +73 -65
- package/lib/rules/no-func-assign.js +53 -54
- package/lib/rules/no-global-assign.js +77 -72
- package/lib/rules/no-implicit-coercion.js +348 -292
- package/lib/rules/no-implicit-globals.js +157 -134
- package/lib/rules/no-implied-eval.js +139 -111
- package/lib/rules/no-import-assign.js +144 -158
- package/lib/rules/no-inline-comments.js +100 -94
- package/lib/rules/no-inner-declarations.js +114 -100
- package/lib/rules/no-invalid-regexp.js +221 -189
- package/lib/rules/no-invalid-this.js +122 -116
- package/lib/rules/no-irregular-whitespace.js +265 -251
- package/lib/rules/no-iterator.js +28 -32
- package/lib/rules/no-label-var.js +58 -61
- package/lib/rules/no-labels.js +137 -132
- package/lib/rules/no-lone-blocks.js +126 -122
- package/lib/rules/no-lonely-if.js +107 -76
- package/lib/rules/no-loop-func.js +233 -212
- package/lib/rules/no-loss-of-precision.js +215 -200
- package/lib/rules/no-magic-numbers.js +245 -217
- package/lib/rules/no-misleading-character-class.js +498 -445
- package/lib/rules/no-mixed-operators.js +187 -181
- package/lib/rules/no-mixed-requires.js +252 -239
- package/lib/rules/no-mixed-spaces-and-tabs.js +133 -120
- package/lib/rules/no-multi-assign.js +45 -43
- package/lib/rules/no-multi-spaces.js +162 -142
- package/lib/rules/no-multi-str.js +41 -40
- package/lib/rules/no-multiple-empty-lines.js +195 -157
- package/lib/rules/no-native-reassign.js +89 -84
- package/lib/rules/no-negated-condition.js +78 -74
- package/lib/rules/no-negated-in-lhs.js +44 -42
- package/lib/rules/no-nested-ternary.js +32 -31
- package/lib/rules/no-new-func.js +70 -61
- package/lib/rules/no-new-native-nonconstructor.js +42 -38
- package/lib/rules/no-new-object.js +47 -47
- package/lib/rules/no-new-require.js +47 -46
- package/lib/rules/no-new-symbol.js +51 -49
- package/lib/rules/no-new-wrappers.js +42 -40
- package/lib/rules/no-new.js +27 -28
- package/lib/rules/no-nonoctal-decimal-escape.js +140 -120
- package/lib/rules/no-obj-calls.js +65 -52
- package/lib/rules/no-object-constructor.js +103 -96
- package/lib/rules/no-octal-escape.js +39 -42
- package/lib/rules/no-octal.js +31 -31
- package/lib/rules/no-param-reassign.js +234 -216
- package/lib/rules/no-path-concat.js +65 -66
- package/lib/rules/no-plusplus.js +59 -60
- package/lib/rules/no-process-env.js +48 -47
- package/lib/rules/no-process-exit.js +53 -49
- package/lib/rules/no-promise-executor-return.js +213 -181
- package/lib/rules/no-proto.js +25 -28
- package/lib/rules/no-prototype-builtins.js +145 -123
- package/lib/rules/no-redeclare.js +153 -151
- package/lib/rules/no-regex-spaces.js +182 -160
- package/lib/rules/no-restricted-exports.js +207 -184
- package/lib/rules/no-restricted-globals.js +110 -111
- package/lib/rules/no-restricted-imports.js +656 -536
- package/lib/rules/no-restricted-modules.js +221 -201
- package/lib/rules/no-restricted-properties.js +180 -152
- package/lib/rules/no-restricted-syntax.js +55 -51
- package/lib/rules/no-return-assign.js +54 -49
- package/lib/rules/no-return-await.js +147 -123
- package/lib/rules/no-script-url.js +51 -44
- package/lib/rules/no-self-assign.js +147 -145
- package/lib/rules/no-self-compare.js +62 -45
- package/lib/rules/no-sequences.js +134 -115
- package/lib/rules/no-setter-return.js +184 -151
- package/lib/rules/no-shadow-restricted-names.js +60 -45
- package/lib/rules/no-shadow.js +341 -315
- package/lib/rules/no-spaced-func.js +81 -76
- package/lib/rules/no-sparse-arrays.js +53 -58
- package/lib/rules/no-sync.js +60 -59
- package/lib/rules/no-tabs.js +82 -71
- package/lib/rules/no-template-curly-in-string.js +32 -31
- package/lib/rules/no-ternary.js +24 -28
- package/lib/rules/no-this-before-super.js +320 -318
- package/lib/rules/no-throw-literal.js +30 -35
- package/lib/rules/no-trailing-spaces.js +198 -190
- package/lib/rules/no-undef-init.js +75 -60
- package/lib/rules/no-undef.js +50 -47
- package/lib/rules/no-undefined.js +72 -74
- package/lib/rules/no-underscore-dangle.js +369 -326
- package/lib/rules/no-unexpected-multiline.js +111 -101
- package/lib/rules/no-unmodified-loop-condition.js +253 -253
- package/lib/rules/no-unneeded-ternary.js +211 -146
- package/lib/rules/no-unreachable-loop.js +144 -141
- package/lib/rules/no-unreachable.js +254 -247
- package/lib/rules/no-unsafe-finally.js +92 -84
- package/lib/rules/no-unsafe-negation.js +104 -82
- package/lib/rules/no-unsafe-optional-chaining.js +191 -177
- package/lib/rules/no-unused-expressions.js +177 -161
- package/lib/rules/no-unused-labels.js +138 -123
- package/lib/rules/no-unused-private-class-members.js +205 -181
- package/lib/rules/no-unused-vars.js +1668 -1448
- package/lib/rules/no-use-before-define.js +228 -230
- package/lib/rules/no-useless-assignment.js +589 -510
- package/lib/rules/no-useless-backreference.js +211 -192
- package/lib/rules/no-useless-call.js +57 -52
- package/lib/rules/no-useless-catch.js +39 -39
- package/lib/rules/no-useless-computed-key.js +143 -114
- package/lib/rules/no-useless-concat.js +64 -59
- package/lib/rules/no-useless-constructor.js +157 -110
- package/lib/rules/no-useless-escape.js +341 -290
- package/lib/rules/no-useless-rename.js +182 -155
- package/lib/rules/no-useless-return.js +343 -311
- package/lib/rules/no-var.js +232 -211
- package/lib/rules/no-void.js +49 -47
- package/lib/rules/no-warning-comments.js +190 -185
- package/lib/rules/no-whitespace-before-property.js +130 -114
- package/lib/rules/no-with.js +23 -25
- package/lib/rules/nonblock-statement-body-position.js +148 -129
- package/lib/rules/object-curly-newline.js +305 -264
- package/lib/rules/object-curly-spacing.js +359 -313
- package/lib/rules/object-property-newline.js +136 -105
- package/lib/rules/object-shorthand.js +606 -501
- package/lib/rules/one-var-declaration-per-line.js +103 -99
- package/lib/rules/one-var.js +652 -536
- package/lib/rules/operator-assignment.js +218 -160
- package/lib/rules/operator-linebreak.js +294 -250
- package/lib/rules/padded-blocks.js +345 -307
- package/lib/rules/padding-line-between-statements.js +442 -438
- package/lib/rules/prefer-arrow-callback.js +361 -312
- package/lib/rules/prefer-const.js +417 -376
- package/lib/rules/prefer-destructuring.js +300 -278
- package/lib/rules/prefer-exponentiation-operator.js +175 -132
- package/lib/rules/prefer-named-capture-group.js +152 -139
- package/lib/rules/prefer-numeric-literals.js +120 -112
- package/lib/rules/prefer-object-has-own.js +115 -81
- package/lib/rules/prefer-object-spread.js +212 -192
- package/lib/rules/prefer-promise-reject-errors.js +139 -121
- package/lib/rules/prefer-reflect.js +126 -106
- package/lib/rules/prefer-regex-literals.js +577 -465
- package/lib/rules/prefer-rest-params.js +78 -79
- package/lib/rules/prefer-spread.js +46 -43
- package/lib/rules/prefer-template.js +265 -194
- package/lib/rules/quote-props.js +372 -306
- package/lib/rules/quotes.js +373 -325
- package/lib/rules/radix.js +151 -135
- package/lib/rules/require-atomic-updates.js +315 -284
- package/lib/rules/require-await.js +143 -115
- package/lib/rules/require-unicode-regexp.js +281 -176
- package/lib/rules/require-yield.js +52 -53
- package/lib/rules/rest-spread-spacing.js +127 -115
- package/lib/rules/semi-spacing.js +280 -249
- package/lib/rules/semi-style.js +175 -133
- package/lib/rules/semi.js +455 -435
- package/lib/rules/sort-imports.js +305 -232
- package/lib/rules/sort-keys.js +218 -187
- package/lib/rules/sort-vars.js +126 -92
- package/lib/rules/space-before-blocks.js +198 -188
- package/lib/rules/space-before-function-paren.js +185 -165
- package/lib/rules/space-in-parens.js +358 -287
- package/lib/rules/space-infix-ops.js +236 -200
- package/lib/rules/space-unary-ops.js +355 -297
- package/lib/rules/spaced-comment.js +362 -318
- package/lib/rules/strict.js +264 -229
- package/lib/rules/switch-colon-spacing.js +129 -121
- package/lib/rules/symbol-description.js +44 -47
- package/lib/rules/template-curly-spacing.js +147 -141
- package/lib/rules/template-tag-spacing.js +97 -87
- package/lib/rules/unicode-bom.js +53 -55
- package/lib/rules/use-isnan.js +236 -205
- package/lib/rules/utils/ast-utils.js +2039 -1860
- package/lib/rules/utils/char-source.js +162 -155
- package/lib/rules/utils/fix-tracker.js +83 -80
- package/lib/rules/utils/keywords.js +59 -59
- package/lib/rules/utils/lazy-loading-rule-map.js +79 -76
- package/lib/rules/utils/regular-expressions.js +32 -24
- package/lib/rules/utils/unicode/index.js +4 -4
- package/lib/rules/utils/unicode/is-combining-character.js +1 -1
- package/lib/rules/utils/unicode/is-emoji-modifier.js +1 -1
- package/lib/rules/utils/unicode/is-regional-indicator-symbol.js +1 -1
- package/lib/rules/utils/unicode/is-surrogate-pair.js +1 -1
- package/lib/rules/valid-typeof.js +152 -110
- package/lib/rules/vars-on-top.js +151 -144
- package/lib/rules/wrap-iife.js +203 -190
- package/lib/rules/wrap-regex.js +69 -57
- package/lib/rules/yield-star-spacing.js +144 -133
- package/lib/rules/yoda.js +282 -271
- package/lib/services/parser-service.js +35 -35
- package/lib/services/processor-service.js +66 -73
- package/lib/shared/ajv.js +14 -14
- package/lib/shared/assert.js +3 -4
- package/lib/shared/ast-utils.js +7 -6
- package/lib/shared/deep-merge-arrays.js +24 -22
- package/lib/shared/directives.js +3 -2
- package/lib/shared/flags.js +46 -17
- package/lib/shared/logging.js +24 -25
- package/lib/shared/option-utils.js +43 -36
- package/lib/shared/runtime-info.js +136 -127
- package/lib/shared/serialization.js +27 -27
- package/lib/shared/severity.js +22 -22
- package/lib/shared/stats.js +5 -5
- package/lib/shared/string-utils.js +16 -16
- package/lib/shared/text-table.js +28 -27
- package/lib/shared/traverser.js +153 -146
- package/lib/types/config-api.d.ts +8 -0
- package/lib/types/index.d.ts +2010 -1559
- package/lib/types/rules.d.ts +5312 -0
- package/lib/types/use-at-your-own-risk.d.ts +32 -30
- package/lib/unsupported-api.js +5 -5
- package/messages/all-files-ignored.js +3 -3
- package/messages/all-matched-files-ignored.js +3 -3
- package/messages/config-file-missing.js +2 -2
- package/messages/config-plugin-missing.js +3 -3
- package/messages/config-serialize-function.js +9 -7
- package/messages/eslintrc-incompat.js +13 -15
- package/messages/eslintrc-plugins.js +3 -4
- package/messages/extend-config-missing.js +3 -3
- package/messages/failed-to-read-json.js +3 -3
- package/messages/file-not-found.js +3 -3
- package/messages/invalid-rule-options.js +2 -2
- package/messages/invalid-rule-severity.js +2 -2
- package/messages/no-config-found.js +3 -3
- package/messages/plugin-conflict.js +8 -8
- package/messages/plugin-invalid.js +3 -3
- package/messages/plugin-missing.js +3 -3
- package/messages/print-config-with-directory-path.js +2 -2
- package/messages/shared.js +6 -1
- package/messages/whitespace-found.js +3 -3
- package/package.json +22 -20
- package/lib/types/rules/best-practices.d.ts +0 -1143
- package/lib/types/rules/deprecated.d.ts +0 -252
- package/lib/types/rules/ecmascript-6.d.ts +0 -647
- package/lib/types/rules/index.d.ts +0 -50
- package/lib/types/rules/node-commonjs.d.ts +0 -171
- package/lib/types/rules/possible-errors.d.ts +0 -685
- package/lib/types/rules/strict-mode.d.ts +0 -38
- package/lib/types/rules/stylistic-issues.d.ts +0 -2043
- package/lib/types/rules/variables.d.ts +0 -234
@@ -5,9 +5,28 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
-
const {
|
9
|
-
|
10
|
-
|
8
|
+
const {
|
9
|
+
isNullLiteral,
|
10
|
+
isConstant,
|
11
|
+
isReferenceToGlobalVariable,
|
12
|
+
isLogicalAssignmentOperator,
|
13
|
+
ECMASCRIPT_GLOBALS,
|
14
|
+
} = require("./utils/ast-utils");
|
15
|
+
|
16
|
+
const NUMERIC_OR_STRING_BINARY_OPERATORS = new Set([
|
17
|
+
"+",
|
18
|
+
"-",
|
19
|
+
"*",
|
20
|
+
"/",
|
21
|
+
"%",
|
22
|
+
"|",
|
23
|
+
"^",
|
24
|
+
"&",
|
25
|
+
"**",
|
26
|
+
"<<",
|
27
|
+
">>",
|
28
|
+
">>>",
|
29
|
+
]);
|
11
30
|
|
12
31
|
//------------------------------------------------------------------------------
|
13
32
|
// Helpers
|
@@ -23,11 +42,13 @@ const NUMERIC_OR_STRING_BINARY_OPERATORS = new Set(["+", "-", "*", "/", "%", "|"
|
|
23
42
|
* @public
|
24
43
|
*/
|
25
44
|
function isNullOrUndefined(scope, node) {
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
45
|
+
return (
|
46
|
+
isNullLiteral(node) ||
|
47
|
+
(node.type === "Identifier" &&
|
48
|
+
node.name === "undefined" &&
|
49
|
+
isReferenceToGlobalVariable(scope, node)) ||
|
50
|
+
(node.type === "UnaryExpression" && node.operator === "void")
|
51
|
+
);
|
31
52
|
}
|
32
53
|
|
33
54
|
/**
|
@@ -41,79 +62,88 @@ function isNullOrUndefined(scope, node) {
|
|
41
62
|
* @returns {boolean} Does `node` have constant nullishness?
|
42
63
|
*/
|
43
64
|
function hasConstantNullishness(scope, node, nonNullish) {
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
65
|
+
if (nonNullish && isNullOrUndefined(scope, node)) {
|
66
|
+
return false;
|
67
|
+
}
|
68
|
+
|
69
|
+
switch (node.type) {
|
70
|
+
case "ObjectExpression": // Objects are never nullish
|
71
|
+
case "ArrayExpression": // Arrays are never nullish
|
72
|
+
case "ArrowFunctionExpression": // Functions never nullish
|
73
|
+
case "FunctionExpression": // Functions are never nullish
|
74
|
+
case "ClassExpression": // Classes are never nullish
|
75
|
+
case "NewExpression": // Objects are never nullish
|
76
|
+
case "Literal": // Nullish, or non-nullish, literals never change
|
77
|
+
case "TemplateLiteral": // A string is never nullish
|
78
|
+
case "UpdateExpression": // Numbers are never nullish
|
79
|
+
case "BinaryExpression": // Numbers, strings, or booleans are never nullish
|
80
|
+
return true;
|
81
|
+
case "CallExpression": {
|
82
|
+
if (node.callee.type !== "Identifier") {
|
83
|
+
return false;
|
84
|
+
}
|
85
|
+
const functionName = node.callee.name;
|
86
|
+
|
87
|
+
return (
|
88
|
+
(functionName === "Boolean" ||
|
89
|
+
functionName === "String" ||
|
90
|
+
functionName === "Number") &&
|
91
|
+
isReferenceToGlobalVariable(scope, node.callee)
|
92
|
+
);
|
93
|
+
}
|
94
|
+
case "LogicalExpression": {
|
95
|
+
return (
|
96
|
+
node.operator === "??" &&
|
97
|
+
hasConstantNullishness(scope, node.right, true)
|
98
|
+
);
|
99
|
+
}
|
100
|
+
case "AssignmentExpression":
|
101
|
+
if (node.operator === "=") {
|
102
|
+
return hasConstantNullishness(scope, node.right, nonNullish);
|
103
|
+
}
|
104
|
+
|
105
|
+
/*
|
106
|
+
* Handling short-circuiting assignment operators would require
|
107
|
+
* walking the scope. We won't attempt that (for now...) /
|
108
|
+
*/
|
109
|
+
if (isLogicalAssignmentOperator(node.operator)) {
|
110
|
+
return false;
|
111
|
+
}
|
112
|
+
|
113
|
+
/*
|
114
|
+
* The remaining assignment expressions all result in a numeric or
|
115
|
+
* string (non-nullish) value:
|
116
|
+
* "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", "|=", "^=", "&="
|
117
|
+
*/
|
118
|
+
|
119
|
+
return true;
|
120
|
+
case "UnaryExpression":
|
121
|
+
/*
|
122
|
+
* "void" Always returns `undefined`
|
123
|
+
* "typeof" All types are strings, and thus non-nullish
|
124
|
+
* "!" Boolean is never nullish
|
125
|
+
* "delete" Returns a boolean, which is never nullish
|
126
|
+
* Math operators always return numbers or strings, neither of which
|
127
|
+
* are non-nullish "+", "-", "~"
|
128
|
+
*/
|
129
|
+
|
130
|
+
return true;
|
131
|
+
case "SequenceExpression": {
|
132
|
+
const last = node.expressions.at(-1);
|
133
|
+
|
134
|
+
return hasConstantNullishness(scope, last, nonNullish);
|
135
|
+
}
|
136
|
+
case "Identifier":
|
137
|
+
return (
|
138
|
+
node.name === "undefined" &&
|
139
|
+
isReferenceToGlobalVariable(scope, node)
|
140
|
+
);
|
141
|
+
case "JSXElement": // ESLint has a policy of not assuming any specific JSX behavior.
|
142
|
+
case "JSXFragment":
|
143
|
+
return false;
|
144
|
+
default:
|
145
|
+
return false;
|
146
|
+
}
|
117
147
|
}
|
118
148
|
|
119
149
|
/**
|
@@ -127,21 +157,26 @@ function hasConstantNullishness(scope, node, nonNullish) {
|
|
127
157
|
* @returns {boolean} Is `node` guaranteed to be a boolean?
|
128
158
|
*/
|
129
159
|
function isStaticBoolean(scope, node) {
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
160
|
+
switch (node.type) {
|
161
|
+
case "Literal":
|
162
|
+
return typeof node.value === "boolean";
|
163
|
+
case "CallExpression":
|
164
|
+
return (
|
165
|
+
node.callee.type === "Identifier" &&
|
166
|
+
node.callee.name === "Boolean" &&
|
167
|
+
isReferenceToGlobalVariable(scope, node.callee) &&
|
168
|
+
(node.arguments.length === 0 ||
|
169
|
+
isConstant(scope, node.arguments[0], true))
|
170
|
+
);
|
171
|
+
case "UnaryExpression":
|
172
|
+
return (
|
173
|
+
node.operator === "!" && isConstant(scope, node.argument, true)
|
174
|
+
);
|
175
|
+
default:
|
176
|
+
return false;
|
177
|
+
}
|
142
178
|
}
|
143
179
|
|
144
|
-
|
145
180
|
/**
|
146
181
|
* Test if an AST node will always give the same result when compared to a
|
147
182
|
* boolean value. Note that comparison to boolean values is different than
|
@@ -155,111 +190,116 @@ function isStaticBoolean(scope, node) {
|
|
155
190
|
* @returns {boolean} Will `node` always coerce to the same boolean value?
|
156
191
|
*/
|
157
192
|
function hasConstantLooseBooleanComparison(scope, node) {
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
193
|
+
switch (node.type) {
|
194
|
+
case "ObjectExpression":
|
195
|
+
case "ClassExpression":
|
196
|
+
/**
|
197
|
+
* In theory objects like:
|
198
|
+
*
|
199
|
+
* `{toString: () => a}`
|
200
|
+
* `{valueOf: () => a}`
|
201
|
+
*
|
202
|
+
* Or a classes like:
|
203
|
+
*
|
204
|
+
* `class { static toString() { return a } }`
|
205
|
+
* `class { static valueOf() { return a } }`
|
206
|
+
*
|
207
|
+
* Are not constant verifiably when `inBooleanPosition` is
|
208
|
+
* false, but it's an edge case we've opted not to handle.
|
209
|
+
*/
|
210
|
+
return true;
|
211
|
+
case "ArrayExpression": {
|
212
|
+
const nonSpreadElements = node.elements.filter(
|
213
|
+
e =>
|
214
|
+
// Elements can be `null` in sparse arrays: `[,,]`;
|
215
|
+
e !== null && e.type !== "SpreadElement",
|
216
|
+
);
|
217
|
+
|
218
|
+
/*
|
219
|
+
* Possible future direction if needed: We could check if the
|
220
|
+
* single value would result in variable boolean comparison.
|
221
|
+
* For now we will err on the side of caution since `[x]` could
|
222
|
+
* evaluate to `[0]` or `[1]`.
|
223
|
+
*/
|
224
|
+
return node.elements.length === 0 || nonSpreadElements.length > 1;
|
225
|
+
}
|
226
|
+
case "ArrowFunctionExpression":
|
227
|
+
case "FunctionExpression":
|
228
|
+
return true;
|
229
|
+
case "UnaryExpression":
|
230
|
+
if (
|
231
|
+
node.operator === "void" || // Always returns `undefined`
|
232
|
+
node.operator === "typeof" // All `typeof` strings, when coerced to number, are not 0 or 1.
|
233
|
+
) {
|
234
|
+
return true;
|
235
|
+
}
|
236
|
+
if (node.operator === "!") {
|
237
|
+
return isConstant(scope, node.argument, true);
|
238
|
+
}
|
239
|
+
|
240
|
+
/*
|
241
|
+
* We won't try to reason about +, -, ~, or delete
|
242
|
+
* In theory, for the mathematical operators, we could look at the
|
243
|
+
* argument and try to determine if it coerces to a constant numeric
|
244
|
+
* value.
|
245
|
+
*/
|
246
|
+
return false;
|
247
|
+
case "NewExpression": // Objects might have custom `.valueOf` or `.toString`.
|
248
|
+
return false;
|
249
|
+
case "CallExpression": {
|
250
|
+
if (
|
251
|
+
node.callee.type === "Identifier" &&
|
252
|
+
node.callee.name === "Boolean" &&
|
253
|
+
isReferenceToGlobalVariable(scope, node.callee)
|
254
|
+
) {
|
255
|
+
return (
|
256
|
+
node.arguments.length === 0 ||
|
257
|
+
isConstant(scope, node.arguments[0], true)
|
258
|
+
);
|
259
|
+
}
|
260
|
+
return false;
|
261
|
+
}
|
262
|
+
case "Literal": // True or false, literals never change
|
263
|
+
return true;
|
264
|
+
case "Identifier":
|
265
|
+
return (
|
266
|
+
node.name === "undefined" &&
|
267
|
+
isReferenceToGlobalVariable(scope, node)
|
268
|
+
);
|
269
|
+
case "TemplateLiteral":
|
270
|
+
/*
|
271
|
+
* In theory we could try to check if the quasi are sufficient to
|
272
|
+
* prove that the expression will always be true, but it would be
|
273
|
+
* tricky to get right. For example: `000.${foo}000`
|
274
|
+
*/
|
275
|
+
return node.expressions.length === 0;
|
276
|
+
case "AssignmentExpression":
|
277
|
+
if (node.operator === "=") {
|
278
|
+
return hasConstantLooseBooleanComparison(scope, node.right);
|
279
|
+
}
|
280
|
+
|
281
|
+
/*
|
282
|
+
* Handling short-circuiting assignment operators would require
|
283
|
+
* walking the scope. We won't attempt that (for now...)
|
284
|
+
*
|
285
|
+
* The remaining assignment expressions all result in a numeric or
|
286
|
+
* string (non-nullish) values which could be truthy or falsy:
|
287
|
+
* "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", "|=", "^=", "&="
|
288
|
+
*/
|
289
|
+
return false;
|
290
|
+
case "SequenceExpression": {
|
291
|
+
const last = node.expressions.at(-1);
|
292
|
+
|
293
|
+
return hasConstantLooseBooleanComparison(scope, last);
|
294
|
+
}
|
295
|
+
case "JSXElement": // ESLint has a policy of not assuming any specific JSX behavior.
|
296
|
+
case "JSXFragment":
|
297
|
+
return false;
|
298
|
+
default:
|
299
|
+
return false;
|
300
|
+
}
|
260
301
|
}
|
261
302
|
|
262
|
-
|
263
303
|
/**
|
264
304
|
* Test if an AST node will always give the same result when _strictly_ compared
|
265
305
|
* to a boolean value. This can happen if the expression can never be boolean, or
|
@@ -270,82 +310,90 @@ function hasConstantLooseBooleanComparison(scope, node) {
|
|
270
310
|
* static boolean value?
|
271
311
|
*/
|
272
312
|
function hasConstantStrictBooleanComparison(scope, node) {
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
313
|
+
switch (node.type) {
|
314
|
+
case "ObjectExpression": // Objects are not booleans
|
315
|
+
case "ArrayExpression": // Arrays are not booleans
|
316
|
+
case "ArrowFunctionExpression": // Functions are not booleans
|
317
|
+
case "FunctionExpression":
|
318
|
+
case "ClassExpression": // Classes are not booleans
|
319
|
+
case "NewExpression": // Objects are not booleans
|
320
|
+
case "TemplateLiteral": // Strings are not booleans
|
321
|
+
case "Literal": // True, false, or not boolean, literals never change.
|
322
|
+
case "UpdateExpression": // Numbers are not booleans
|
323
|
+
return true;
|
324
|
+
case "BinaryExpression":
|
325
|
+
return NUMERIC_OR_STRING_BINARY_OPERATORS.has(node.operator);
|
326
|
+
case "UnaryExpression": {
|
327
|
+
if (node.operator === "delete") {
|
328
|
+
return false;
|
329
|
+
}
|
330
|
+
if (node.operator === "!") {
|
331
|
+
return isConstant(scope, node.argument, true);
|
332
|
+
}
|
333
|
+
|
334
|
+
/*
|
335
|
+
* The remaining operators return either strings or numbers, neither
|
336
|
+
* of which are boolean.
|
337
|
+
*/
|
338
|
+
return true;
|
339
|
+
}
|
340
|
+
case "SequenceExpression": {
|
341
|
+
const last = node.expressions.at(-1);
|
342
|
+
|
343
|
+
return hasConstantStrictBooleanComparison(scope, last);
|
344
|
+
}
|
345
|
+
case "Identifier":
|
346
|
+
return (
|
347
|
+
node.name === "undefined" &&
|
348
|
+
isReferenceToGlobalVariable(scope, node)
|
349
|
+
);
|
350
|
+
case "AssignmentExpression":
|
351
|
+
if (node.operator === "=") {
|
352
|
+
return hasConstantStrictBooleanComparison(scope, node.right);
|
353
|
+
}
|
354
|
+
|
355
|
+
/*
|
356
|
+
* Handling short-circuiting assignment operators would require
|
357
|
+
* walking the scope. We won't attempt that (for now...)
|
358
|
+
*/
|
359
|
+
if (isLogicalAssignmentOperator(node.operator)) {
|
360
|
+
return false;
|
361
|
+
}
|
362
|
+
|
363
|
+
/*
|
364
|
+
* The remaining assignment expressions all result in either a number
|
365
|
+
* or a string, neither of which can ever be boolean.
|
366
|
+
*/
|
367
|
+
return true;
|
368
|
+
case "CallExpression": {
|
369
|
+
if (node.callee.type !== "Identifier") {
|
370
|
+
return false;
|
371
|
+
}
|
372
|
+
const functionName = node.callee.name;
|
373
|
+
|
374
|
+
if (
|
375
|
+
(functionName === "String" || functionName === "Number") &&
|
376
|
+
isReferenceToGlobalVariable(scope, node.callee)
|
377
|
+
) {
|
378
|
+
return true;
|
379
|
+
}
|
380
|
+
if (
|
381
|
+
functionName === "Boolean" &&
|
382
|
+
isReferenceToGlobalVariable(scope, node.callee)
|
383
|
+
) {
|
384
|
+
return (
|
385
|
+
node.arguments.length === 0 ||
|
386
|
+
isConstant(scope, node.arguments[0], true)
|
387
|
+
);
|
388
|
+
}
|
389
|
+
return false;
|
390
|
+
}
|
391
|
+
case "JSXElement": // ESLint has a policy of not assuming any specific JSX behavior.
|
392
|
+
case "JSXFragment":
|
393
|
+
return false;
|
394
|
+
default:
|
395
|
+
return false;
|
396
|
+
}
|
349
397
|
}
|
350
398
|
|
351
399
|
/**
|
@@ -355,51 +403,55 @@ function hasConstantStrictBooleanComparison(scope, node) {
|
|
355
403
|
* @returns {boolean} Will `node` always be new?
|
356
404
|
*/
|
357
405
|
function isAlwaysNew(scope, node) {
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
406
|
+
switch (node.type) {
|
407
|
+
case "ObjectExpression":
|
408
|
+
case "ArrayExpression":
|
409
|
+
case "ArrowFunctionExpression":
|
410
|
+
case "FunctionExpression":
|
411
|
+
case "ClassExpression":
|
412
|
+
return true;
|
413
|
+
case "NewExpression": {
|
414
|
+
if (node.callee.type !== "Identifier") {
|
415
|
+
return false;
|
416
|
+
}
|
417
|
+
|
418
|
+
/*
|
419
|
+
* All the built-in constructors are always new, but
|
420
|
+
* user-defined constructors could return a sentinel
|
421
|
+
* object.
|
422
|
+
*
|
423
|
+
* Catching these is especially useful for primitive constructors
|
424
|
+
* which return boxed values, a surprising gotcha' in JavaScript.
|
425
|
+
*/
|
426
|
+
return (
|
427
|
+
Object.hasOwn(ECMASCRIPT_GLOBALS, node.callee.name) &&
|
428
|
+
isReferenceToGlobalVariable(scope, node.callee)
|
429
|
+
);
|
430
|
+
}
|
431
|
+
case "Literal":
|
432
|
+
// Regular expressions are objects, and thus always new
|
433
|
+
return typeof node.regex === "object";
|
434
|
+
case "SequenceExpression": {
|
435
|
+
const last = node.expressions.at(-1);
|
436
|
+
|
437
|
+
return isAlwaysNew(scope, last);
|
438
|
+
}
|
439
|
+
case "AssignmentExpression":
|
440
|
+
if (node.operator === "=") {
|
441
|
+
return isAlwaysNew(scope, node.right);
|
442
|
+
}
|
443
|
+
return false;
|
444
|
+
case "ConditionalExpression":
|
445
|
+
return (
|
446
|
+
isAlwaysNew(scope, node.consequent) &&
|
447
|
+
isAlwaysNew(scope, node.alternate)
|
448
|
+
);
|
449
|
+
case "JSXElement": // ESLint has a policy of not assuming any specific JSX behavior.
|
450
|
+
case "JSXFragment":
|
451
|
+
return false;
|
452
|
+
default:
|
453
|
+
return false;
|
454
|
+
}
|
403
455
|
}
|
404
456
|
|
405
457
|
/**
|
@@ -411,22 +463,26 @@ function isAlwaysNew(scope, node) {
|
|
411
463
|
* @returns {ASTNode | null} The node which will cause the expression to have a constant result.
|
412
464
|
*/
|
413
465
|
function findBinaryExpressionConstantOperand(scope, a, b, operator) {
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
466
|
+
if (operator === "==" || operator === "!=") {
|
467
|
+
if (
|
468
|
+
(isNullOrUndefined(scope, a) &&
|
469
|
+
hasConstantNullishness(scope, b, false)) ||
|
470
|
+
(isStaticBoolean(scope, a) &&
|
471
|
+
hasConstantLooseBooleanComparison(scope, b))
|
472
|
+
) {
|
473
|
+
return b;
|
474
|
+
}
|
475
|
+
} else if (operator === "===" || operator === "!==") {
|
476
|
+
if (
|
477
|
+
(isNullOrUndefined(scope, a) &&
|
478
|
+
hasConstantNullishness(scope, b, false)) ||
|
479
|
+
(isStaticBoolean(scope, a) &&
|
480
|
+
hasConstantStrictBooleanComparison(scope, b))
|
481
|
+
) {
|
482
|
+
return b;
|
483
|
+
}
|
484
|
+
}
|
485
|
+
return null;
|
430
486
|
}
|
431
487
|
|
432
488
|
//------------------------------------------------------------------------------
|
@@ -435,74 +491,113 @@ function findBinaryExpressionConstantOperand(scope, a, b, operator) {
|
|
435
491
|
|
436
492
|
/** @type {import('../shared/types').Rule} */
|
437
493
|
module.exports = {
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
494
|
+
meta: {
|
495
|
+
type: "problem",
|
496
|
+
docs: {
|
497
|
+
description:
|
498
|
+
"Disallow expressions where the operation doesn't affect the value",
|
499
|
+
recommended: true,
|
500
|
+
url: "https://eslint.org/docs/latest/rules/no-constant-binary-expression",
|
501
|
+
},
|
502
|
+
schema: [],
|
503
|
+
messages: {
|
504
|
+
constantBinaryOperand:
|
505
|
+
"Unexpected constant binary expression. Compares constantly with the {{otherSide}}-hand side of the `{{operator}}`.",
|
506
|
+
constantShortCircuit:
|
507
|
+
"Unexpected constant {{property}} on the left-hand side of a `{{operator}}` expression.",
|
508
|
+
alwaysNew:
|
509
|
+
"Unexpected comparison to newly constructed object. These two values can never be equal.",
|
510
|
+
bothAlwaysNew:
|
511
|
+
"Unexpected comparison of two newly constructed objects. These two values can never be equal.",
|
512
|
+
},
|
513
|
+
},
|
514
|
+
|
515
|
+
create(context) {
|
516
|
+
const sourceCode = context.sourceCode;
|
517
|
+
|
518
|
+
return {
|
519
|
+
LogicalExpression(node) {
|
520
|
+
const { operator, left } = node;
|
521
|
+
const scope = sourceCode.getScope(node);
|
522
|
+
|
523
|
+
if (
|
524
|
+
(operator === "&&" || operator === "||") &&
|
525
|
+
isConstant(scope, left, true)
|
526
|
+
) {
|
527
|
+
context.report({
|
528
|
+
node: left,
|
529
|
+
messageId: "constantShortCircuit",
|
530
|
+
data: { property: "truthiness", operator },
|
531
|
+
});
|
532
|
+
} else if (
|
533
|
+
operator === "??" &&
|
534
|
+
hasConstantNullishness(scope, left, false)
|
535
|
+
) {
|
536
|
+
context.report({
|
537
|
+
node: left,
|
538
|
+
messageId: "constantShortCircuit",
|
539
|
+
data: { property: "nullishness", operator },
|
540
|
+
});
|
541
|
+
}
|
542
|
+
},
|
543
|
+
BinaryExpression(node) {
|
544
|
+
const scope = sourceCode.getScope(node);
|
545
|
+
const { right, left, operator } = node;
|
546
|
+
const rightConstantOperand =
|
547
|
+
findBinaryExpressionConstantOperand(
|
548
|
+
scope,
|
549
|
+
left,
|
550
|
+
right,
|
551
|
+
operator,
|
552
|
+
);
|
553
|
+
const leftConstantOperand = findBinaryExpressionConstantOperand(
|
554
|
+
scope,
|
555
|
+
right,
|
556
|
+
left,
|
557
|
+
operator,
|
558
|
+
);
|
559
|
+
|
560
|
+
if (rightConstantOperand) {
|
561
|
+
context.report({
|
562
|
+
node: rightConstantOperand,
|
563
|
+
messageId: "constantBinaryOperand",
|
564
|
+
data: { operator, otherSide: "left" },
|
565
|
+
});
|
566
|
+
} else if (leftConstantOperand) {
|
567
|
+
context.report({
|
568
|
+
node: leftConstantOperand,
|
569
|
+
messageId: "constantBinaryOperand",
|
570
|
+
data: { operator, otherSide: "right" },
|
571
|
+
});
|
572
|
+
} else if (operator === "===" || operator === "!==") {
|
573
|
+
if (isAlwaysNew(scope, left)) {
|
574
|
+
context.report({ node: left, messageId: "alwaysNew" });
|
575
|
+
} else if (isAlwaysNew(scope, right)) {
|
576
|
+
context.report({ node: right, messageId: "alwaysNew" });
|
577
|
+
}
|
578
|
+
} else if (operator === "==" || operator === "!=") {
|
579
|
+
/*
|
580
|
+
* If both sides are "new", then both sides are objects and
|
581
|
+
* therefore they will be compared by reference even with `==`
|
582
|
+
* equality.
|
583
|
+
*/
|
584
|
+
if (isAlwaysNew(scope, left) && isAlwaysNew(scope, right)) {
|
585
|
+
context.report({
|
586
|
+
node: left,
|
587
|
+
messageId: "bothAlwaysNew",
|
588
|
+
});
|
589
|
+
}
|
590
|
+
}
|
591
|
+
},
|
592
|
+
|
593
|
+
/*
|
594
|
+
* In theory we could handle short-circuiting assignment operators,
|
595
|
+
* for some constant values, but that would require walking the
|
596
|
+
* scope to find the value of the variable being assigned. This is
|
597
|
+
* dependant on https://github.com/eslint/eslint/issues/13776
|
598
|
+
*
|
599
|
+
* AssignmentExpression() {},
|
600
|
+
*/
|
601
|
+
};
|
602
|
+
},
|
508
603
|
};
|