eslint 8.4.1 → 8.8.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.
@@ -216,6 +216,9 @@ function freezeDeeply(x) {
216
216
  * @returns {string} The sanitized text.
217
217
  */
218
218
  function sanitize(text) {
219
+ if (typeof text !== "string") {
220
+ return "";
221
+ }
219
222
  return text.replace(
220
223
  /[\u0000-\u0009\u000b-\u001a]/gu, // eslint-disable-line no-control-regex -- Escaping controls
221
224
  c => `\\u${c.codePointAt(0).toString(16).padStart(4, "0")}`
@@ -691,6 +694,13 @@ class RuleTester {
691
694
  * @private
692
695
  */
693
696
  function testValidTemplate(item) {
697
+ const code = typeof item === "object" ? item.code : item;
698
+
699
+ assert.ok(typeof code === "string", "Test case must specify a string value for 'code'");
700
+ if (item.name) {
701
+ assert.ok(typeof item.name === "string", "Optional test case property 'name' must be a string");
702
+ }
703
+
694
704
  const result = runRuleForItem(item);
695
705
  const messages = result.messages;
696
706
 
@@ -731,6 +741,10 @@ class RuleTester {
731
741
  * @private
732
742
  */
733
743
  function testInvalidTemplate(item) {
744
+ assert.ok(typeof item.code === "string", "Test case must specify a string value for 'code'");
745
+ if (item.name) {
746
+ assert.ok(typeof item.name === "string", "Optional test case property 'name' must be a string");
747
+ }
734
748
  assert.ok(item.errors || item.errors === 0,
735
749
  `Did not specify errors for an invalid test of ${ruleName}`);
736
750
 
@@ -963,10 +977,10 @@ class RuleTester {
963
977
  * This creates a mocha test suite and pipes all supplied info through
964
978
  * one of the templates above.
965
979
  */
966
- RuleTester.describe(ruleName, () => {
967
- RuleTester.describe("valid", () => {
980
+ this.constructor.describe(ruleName, () => {
981
+ this.constructor.describe("valid", () => {
968
982
  test.valid.forEach(valid => {
969
- RuleTester[valid.only ? "itOnly" : "it"](
983
+ this.constructor[valid.only ? "itOnly" : "it"](
970
984
  sanitize(typeof valid === "object" ? valid.name || valid.code : valid),
971
985
  () => {
972
986
  testValidTemplate(valid);
@@ -975,9 +989,9 @@ class RuleTester {
975
989
  });
976
990
  });
977
991
 
978
- RuleTester.describe("invalid", () => {
992
+ this.constructor.describe("invalid", () => {
979
993
  test.invalid.forEach(invalid => {
980
- RuleTester[invalid.only ? "itOnly" : "it"](
994
+ this.constructor[invalid.only ? "itOnly" : "it"](
981
995
  sanitize(invalid.name || invalid.code),
982
996
  () => {
983
997
  testInvalidTemplate(invalid);
@@ -5,6 +5,12 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ const astUtils = require("./utils/ast-utils");
13
+
8
14
  //------------------------------------------------------------------------------
9
15
  // Rule Definition
10
16
  //------------------------------------------------------------------------------
@@ -165,7 +171,7 @@ module.exports = {
165
171
  case "ImportSpecifier":
166
172
  return (
167
173
  parent.local === node &&
168
- parent.imported.name === localName
174
+ astUtils.getModuleExportName(parent.imported) === localName
169
175
  );
170
176
 
171
177
  default:
@@ -67,6 +67,8 @@ module.exports = {
67
67
  onlyDeclarations = !!options.onlyDeclarations,
68
68
  ignoreDestructuring = !!options.ignoreDestructuring;
69
69
 
70
+ let globalScope;
71
+
70
72
  //--------------------------------------------------------------------------
71
73
  // Helpers
72
74
  //--------------------------------------------------------------------------
@@ -77,6 +79,19 @@ module.exports = {
77
79
  const DECLARATION_TYPES = new Set(["FunctionDeclaration", "VariableDeclarator"]);
78
80
  const IMPORT_TYPES = new Set(["ImportSpecifier", "ImportNamespaceSpecifier", "ImportDefaultSpecifier"]);
79
81
 
82
+ /**
83
+ * Checks whether the given node represents a reference to a global variable that is not declared in the source code.
84
+ * These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
85
+ * @param {ASTNode} node `Identifier` node to check.
86
+ * @returns {boolean} `true` if the node is a reference to a global variable.
87
+ */
88
+ function isReferenceToGlobalVariable(node) {
89
+ const variable = globalScope.set.get(node.name);
90
+
91
+ return variable && variable.defs.length === 0 &&
92
+ variable.references.some(ref => ref.identifier === node);
93
+ }
94
+
80
95
  /**
81
96
  * Checks if a string matches the provided pattern
82
97
  * @param {string} name The string to check.
@@ -155,11 +170,19 @@ module.exports = {
155
170
 
156
171
  return {
157
172
 
173
+ Program() {
174
+ globalScope = context.getScope();
175
+ },
176
+
158
177
  Identifier(node) {
159
178
  const name = node.name,
160
179
  parent = node.parent,
161
180
  effectiveParent = (parent.type === "MemberExpression") ? parent.parent : parent;
162
181
 
182
+ if (isReferenceToGlobalVariable(node)) {
183
+ return;
184
+ }
185
+
163
186
  if (parent.type === "MemberExpression") {
164
187
 
165
188
  if (!checkProperties) {
@@ -188,6 +211,17 @@ module.exports = {
188
211
  }
189
212
  }
190
213
 
214
+ // For https://github.com/eslint/eslint/issues/15123
215
+ } else if (
216
+ parent.type === "Property" &&
217
+ parent.parent.type === "ObjectExpression" &&
218
+ parent.key === node &&
219
+ !parent.computed
220
+ ) {
221
+ if (checkProperties && isInvalid(name)) {
222
+ report(node);
223
+ }
224
+
191
225
  /*
192
226
  * Properties have their own rules, and
193
227
  * AssignmentPattern nodes can be treated like Properties:
@@ -216,7 +250,7 @@ module.exports = {
216
250
  }
217
251
 
218
252
  // never check properties or always ignore destructuring
219
- if (!checkProperties || (ignoreDestructuring && isInsideObjectPattern(node))) {
253
+ if ((!checkProperties && !parent.computed) || (ignoreDestructuring && isInsideObjectPattern(node))) {
220
254
  return;
221
255
  }
222
256
 
@@ -255,6 +255,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
255
255
  "prefer-exponentiation-operator": () => require("./prefer-exponentiation-operator"),
256
256
  "prefer-named-capture-group": () => require("./prefer-named-capture-group"),
257
257
  "prefer-numeric-literals": () => require("./prefer-numeric-literals"),
258
+ "prefer-object-has-own": () => require("./prefer-object-has-own"),
258
259
  "prefer-object-spread": () => require("./prefer-object-spread"),
259
260
  "prefer-promise-reject-errors": () => require("./prefer-promise-reject-errors"),
260
261
  "prefer-reflect": () => require("./prefer-reflect"),
@@ -469,6 +469,7 @@ module.exports = {
469
469
  const asToken = sourceCode.getTokenBefore(node.exported);
470
470
 
471
471
  checkSpacingBefore(asToken, PREV_TOKEN_M);
472
+ checkSpacingAfter(asToken, NEXT_TOKEN_M);
472
473
  }
473
474
 
474
475
  if (node.source) {
@@ -479,6 +480,35 @@ module.exports = {
479
480
  }
480
481
  }
481
482
 
483
+ /**
484
+ * Reports `as` keyword of a given node if usage of spacing around this
485
+ * keyword is invalid.
486
+ * @param {ASTNode} node An `ImportSpecifier` node to check.
487
+ * @returns {void}
488
+ */
489
+ function checkSpacingForImportSpecifier(node) {
490
+ if (node.imported.range[0] !== node.local.range[0]) {
491
+ const asToken = sourceCode.getTokenBefore(node.local);
492
+
493
+ checkSpacingBefore(asToken, PREV_TOKEN_M);
494
+ }
495
+ }
496
+
497
+ /**
498
+ * Reports `as` keyword of a given node if usage of spacing around this
499
+ * keyword is invalid.
500
+ * @param {ASTNode} node An `ExportSpecifier` node to check.
501
+ * @returns {void}
502
+ */
503
+ function checkSpacingForExportSpecifier(node) {
504
+ if (node.local.range[0] !== node.exported.range[0]) {
505
+ const asToken = sourceCode.getTokenBefore(node.exported);
506
+
507
+ checkSpacingBefore(asToken, PREV_TOKEN_M);
508
+ checkSpacingAfter(asToken, NEXT_TOKEN_M);
509
+ }
510
+ }
511
+
482
512
  /**
483
513
  * Reports `as` keyword of a given node if usage of spacing around this
484
514
  * keyword is invalid.
@@ -588,6 +618,8 @@ module.exports = {
588
618
  YieldExpression: checkSpacingBeforeFirstToken,
589
619
 
590
620
  // Others
621
+ ImportSpecifier: checkSpacingForImportSpecifier,
622
+ ExportSpecifier: checkSpacingForExportSpecifier,
591
623
  ImportNamespaceSpecifier: checkSpacingForImportNamespaceSpecifier,
592
624
  MethodDefinition: checkSpacingForProperty,
593
625
  PropertyDefinition: checkSpacingForProperty,
@@ -124,7 +124,8 @@ module.exports = {
124
124
  * Checks if a node has a constant truthiness value.
125
125
  * @param {ASTNode} node The AST node to check.
126
126
  * @param {boolean} inBooleanPosition `false` if checking branch of a condition.
127
- * `true` in all other cases
127
+ * `true` in all other cases. When `false`, checks if -- for both string and
128
+ * number -- if coerced to that type, the value will be constant.
128
129
  * @returns {Bool} true when node's truthiness is constant
129
130
  * @private
130
131
  */
@@ -138,15 +139,31 @@ module.exports = {
138
139
  case "Literal":
139
140
  case "ArrowFunctionExpression":
140
141
  case "FunctionExpression":
141
- case "ObjectExpression":
142
+ return true;
142
143
  case "ClassExpression":
144
+ case "ObjectExpression":
145
+
146
+ /**
147
+ * In theory objects like:
148
+ *
149
+ * `{toString: () => a}`
150
+ * `{valueOf: () => a}`
151
+ *
152
+ * Or a classes like:
153
+ *
154
+ * `class { static toString() { return a } }`
155
+ * `class { static valueOf() { return a } }`
156
+ *
157
+ * Are not constant verifiably when `inBooleanPosition` is
158
+ * false, but it's an edge case we've opted not to handle.
159
+ */
143
160
  return true;
144
161
  case "TemplateLiteral":
145
162
  return (inBooleanPosition && node.quasis.some(quasi => quasi.value.cooked.length)) ||
146
- node.expressions.every(exp => isConstant(exp, inBooleanPosition));
163
+ node.expressions.every(exp => isConstant(exp, false));
147
164
 
148
165
  case "ArrayExpression": {
149
- if (node.parent.type === "BinaryExpression" && node.parent.operator === "+") {
166
+ if (!inBooleanPosition) {
150
167
  return node.elements.every(element => isConstant(element, false));
151
168
  }
152
169
  return true;
@@ -196,6 +213,8 @@ module.exports = {
196
213
 
197
214
  case "SequenceExpression":
198
215
  return isConstant(node.expressions[node.expressions.length - 1], inBooleanPosition);
216
+ case "SpreadElement":
217
+ return isConstant(node.argument, inBooleanPosition);
199
218
 
200
219
  // no default
201
220
  }
@@ -11,6 +11,21 @@
11
11
 
12
12
  const astUtils = require("./utils/ast-utils");
13
13
 
14
+ //------------------------------------------------------------------------------
15
+ // Helpers
16
+ //------------------------------------------------------------------------------
17
+
18
+ /**
19
+ * Determines if the given code path is a code path with lexical `this` binding.
20
+ * That is, if `this` within the code path refers to `this` of surrounding code path.
21
+ * @param {CodePath} codePath Code path.
22
+ * @param {ASTNode} node Node that started the code path.
23
+ * @returns {boolean} `true` if it is a code path with lexical `this` binding.
24
+ */
25
+ function isCodePathWithLexicalThis(codePath, node) {
26
+ return codePath.origin === "function" && node.type === "ArrowFunctionExpression";
27
+ }
28
+
14
29
  //------------------------------------------------------------------------------
15
30
  // Rule Definition
16
31
  //------------------------------------------------------------------------------
@@ -72,71 +87,53 @@ module.exports = {
72
87
  return current;
73
88
  };
74
89
 
75
- /**
76
- * Pushs new checking context into the stack.
77
- *
78
- * The checking context is not initialized yet.
79
- * Because most functions don't have `this` keyword.
80
- * When `this` keyword was found, the checking context is initialized.
81
- * @param {ASTNode} node A function node that was entered.
82
- * @returns {void}
83
- */
84
- function enterFunction(node) {
85
-
86
- // `this` can be invalid only under strict mode.
87
- stack.push({
88
- init: !context.getScope().isStrict,
89
- node,
90
- valid: true
91
- });
92
- }
90
+ return {
93
91
 
94
- /**
95
- * Pops the current checking context from the stack.
96
- * @returns {void}
97
- */
98
- function exitFunction() {
99
- stack.pop();
100
- }
92
+ onCodePathStart(codePath, node) {
93
+ if (isCodePathWithLexicalThis(codePath, node)) {
94
+ return;
95
+ }
101
96
 
102
- return {
97
+ if (codePath.origin === "program") {
98
+ const scope = context.getScope();
99
+ const features = context.parserOptions.ecmaFeatures || {};
100
+
101
+ stack.push({
102
+ init: true,
103
+ node,
104
+ valid: !(
105
+ scope.isStrict ||
106
+ node.sourceType === "module" ||
107
+ (features.globalReturn && scope.childScopes[0].isStrict)
108
+ )
109
+ });
103
110
 
104
- /*
105
- * `this` is invalid only under strict mode.
106
- * Modules is always strict mode.
107
- */
108
- Program(node) {
109
- const scope = context.getScope(),
110
- features = context.parserOptions.ecmaFeatures || {};
111
+ return;
112
+ }
111
113
 
114
+ /*
115
+ * `init: false` means that `valid` isn't determined yet.
116
+ * Most functions don't use `this`, and the calculation for `valid`
117
+ * is relatively costly, so we'll calculate it lazily when the first
118
+ * `this` within the function is traversed. A special case are non-strict
119
+ * functions, because `this` refers to the global object and therefore is
120
+ * always valid, so we can set `init: true` right away.
121
+ */
112
122
  stack.push({
113
- init: true,
123
+ init: !context.getScope().isStrict,
114
124
  node,
115
- valid: !(
116
- scope.isStrict ||
117
- node.sourceType === "module" ||
118
- (features.globalReturn && scope.childScopes[0].isStrict)
119
- )
125
+ valid: true
120
126
  });
121
127
  },
122
128
 
123
- "Program:exit"() {
129
+ onCodePathEnd(codePath, node) {
130
+ if (isCodePathWithLexicalThis(codePath, node)) {
131
+ return;
132
+ }
133
+
124
134
  stack.pop();
125
135
  },
126
136
 
127
- FunctionDeclaration: enterFunction,
128
- "FunctionDeclaration:exit": exitFunction,
129
- FunctionExpression: enterFunction,
130
- "FunctionExpression:exit": exitFunction,
131
-
132
- // Field initializers are implicit functions.
133
- "PropertyDefinition > *.value": enterFunction,
134
- "PropertyDefinition > *.value:exit": exitFunction,
135
-
136
- // Class static blocks are implicit functions.
137
- StaticBlock: enterFunction,
138
- "StaticBlock:exit": exitFunction,
139
-
140
137
  // Reports if `this` of the current context is invalid.
141
138
  ThisExpression(node) {
142
139
  const current = stack.getCurrent();
@@ -5,6 +5,12 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ const astUtils = require("./utils/ast-utils");
13
+
8
14
  //------------------------------------------------------------------------------
9
15
  // Rule Definition
10
16
  //------------------------------------------------------------------------------
@@ -44,12 +50,12 @@ module.exports = {
44
50
  const restrictedNames = new Set(context.options[0] && context.options[0].restrictedNamedExports);
45
51
 
46
52
  /**
47
- * Checks and reports given exported identifier.
48
- * @param {ASTNode} node exported `Identifier` node to check.
53
+ * Checks and reports given exported name.
54
+ * @param {ASTNode} node exported `Identifier` or string `Literal` node to check.
49
55
  * @returns {void}
50
56
  */
51
57
  function checkExportedName(node) {
52
- const name = node.name;
58
+ const name = astUtils.getModuleExportName(node);
53
59
 
54
60
  if (restrictedNames.has(name)) {
55
61
  context.report({
@@ -4,6 +4,12 @@
4
4
  */
5
5
  "use strict";
6
6
 
7
+ //------------------------------------------------------------------------------
8
+ // Requirements
9
+ //------------------------------------------------------------------------------
10
+
11
+ const astUtils = require("./utils/ast-utils");
12
+
7
13
  //------------------------------------------------------------------------------
8
14
  // Rule Definition
9
15
  //------------------------------------------------------------------------------
@@ -63,6 +69,9 @@ const arrayOfStringsOrObjectPatterns = {
63
69
  message: {
64
70
  type: "string",
65
71
  minLength: 1
72
+ },
73
+ caseSensitive: {
74
+ type: "boolean"
66
75
  }
67
76
  },
68
77
  additionalProperties: false,
@@ -142,10 +151,18 @@ module.exports = {
142
151
  }, {});
143
152
 
144
153
  // Handle patterns too, either as strings or groups
145
- const restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
146
- const restrictedPatternGroups = restrictedPatterns.length > 0 && typeof restrictedPatterns[0] === "string"
147
- ? [{ matcher: ignore().add(restrictedPatterns) }]
148
- : restrictedPatterns.map(({ group, message }) => ({ matcher: ignore().add(group), customMessage: message }));
154
+ let restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
155
+
156
+ // standardize to array of objects if we have an array of strings
157
+ if (restrictedPatterns.length > 0 && typeof restrictedPatterns[0] === "string") {
158
+ restrictedPatterns = [{ group: restrictedPatterns }];
159
+ }
160
+
161
+ // relative paths are supported for this rule
162
+ const restrictedPatternGroups = restrictedPatterns.map(({ group, message, caseSensitive }) => ({
163
+ matcher: ignore({ allowRelativePaths: true, ignorecase: !caseSensitive }).add(group),
164
+ customMessage: message
165
+ }));
149
166
 
150
167
  // if no imports are restricted we don't need to check
151
168
  if (Object.keys(restrictedPaths).length === 0 && restrictedPatternGroups.length === 0) {
@@ -269,12 +286,12 @@ module.exports = {
269
286
  } else if (specifier.type === "ImportNamespaceSpecifier") {
270
287
  name = "*";
271
288
  } else if (specifier.imported) {
272
- name = specifier.imported.name;
289
+ name = astUtils.getModuleExportName(specifier.imported);
273
290
  } else if (specifier.local) {
274
- name = specifier.local.name;
291
+ name = astUtils.getModuleExportName(specifier.local);
275
292
  }
276
293
 
277
- if (name) {
294
+ if (typeof name === "string") {
278
295
  if (importNames.has(name)) {
279
296
  importNames.get(name).push(specifierData);
280
297
  } else {
@@ -103,7 +103,8 @@ module.exports = {
103
103
  return {};
104
104
  }
105
105
 
106
- const ig = ignore().add(restrictedPatterns);
106
+ // relative paths are supported for this rule
107
+ const ig = ignore({ allowRelativePaths: true }).add(restrictedPatterns);
107
108
 
108
109
 
109
110
  /**
@@ -174,7 +174,7 @@ module.exports = {
174
174
 
175
175
  return {
176
176
  AssignmentExpression(node) {
177
- if (node.operator === "=") {
177
+ if (["=", "&&=", "||=", "??="].includes(node.operator)) {
178
178
  eachSelfAssignment(node.left, node.right, props, report);
179
179
  }
180
180
  }
@@ -132,8 +132,10 @@ module.exports = {
132
132
  return;
133
133
  }
134
134
 
135
- if (node.imported.name === node.local.name &&
136
- node.imported.range[0] !== node.local.range[0]) {
135
+ if (
136
+ node.imported.range[0] !== node.local.range[0] &&
137
+ astUtils.getModuleExportName(node.imported) === node.local.name
138
+ ) {
137
139
  reportError(node, node.imported, "Import");
138
140
  }
139
141
  }
@@ -148,8 +150,10 @@ module.exports = {
148
150
  return;
149
151
  }
150
152
 
151
- if (node.local.name === node.exported.name &&
152
- node.local.range[0] !== node.exported.range[0]) {
153
+ if (
154
+ node.local.range[0] !== node.exported.range[0] &&
155
+ astUtils.getModuleExportName(node.local) === astUtils.getModuleExportName(node.exported)
156
+ ) {
153
157
  reportError(node, node.local, "Export");
154
158
  }
155
159
 
@@ -0,0 +1,112 @@
1
+ /**
2
+ * @fileoverview Prefers Object.hasOwn() instead of Object.prototype.hasOwnProperty.call()
3
+ * @author Nitin Kumar
4
+ * @author Gautam Arora
5
+ */
6
+
7
+ "use strict";
8
+
9
+ //------------------------------------------------------------------------------
10
+ // Requirements
11
+ //------------------------------------------------------------------------------
12
+
13
+ const astUtils = require("./utils/ast-utils");
14
+
15
+ //------------------------------------------------------------------------------
16
+ // Helpers
17
+ //------------------------------------------------------------------------------
18
+
19
+ /**
20
+ * Checks if the given node is considered to be an access to a property of `Object.prototype`.
21
+ * @param {ASTNode} node `MemberExpression` node to evaluate.
22
+ * @returns {boolean} `true` if `node.object` is `Object`, `Object.prototype`, or `{}` (empty 'ObjectExpression' node).
23
+ */
24
+ function hasLeftHandObject(node) {
25
+
26
+ /*
27
+ * ({}).hasOwnProperty.call(obj, prop) - `true`
28
+ * ({ foo }.hasOwnProperty.call(obj, prop)) - `false`, object literal should be empty
29
+ */
30
+ if (node.object.type === "ObjectExpression" && node.object.properties.length === 0) {
31
+ return true;
32
+ }
33
+
34
+ const objectNodeToCheck = node.object.type === "MemberExpression" && astUtils.getStaticPropertyName(node.object) === "prototype" ? node.object.object : node.object;
35
+
36
+ if (objectNodeToCheck.type === "Identifier" && objectNodeToCheck.name === "Object") {
37
+ return true;
38
+ }
39
+
40
+ return false;
41
+ }
42
+
43
+ //------------------------------------------------------------------------------
44
+ // Rule Definition
45
+ //------------------------------------------------------------------------------
46
+
47
+ /** @type {import('../shared/types').Rule} */
48
+ module.exports = {
49
+ meta: {
50
+ type: "suggestion",
51
+ docs: {
52
+ description:
53
+ "disallow use of `Object.prototype.hasOwnProperty.call()` and prefer use of `Object.hasOwn()`",
54
+ recommended: false,
55
+ url: "https://eslint.org/docs/rules/prefer-object-has-own"
56
+ },
57
+ schema: [],
58
+ messages: {
59
+ useHasOwn: "Use 'Object.hasOwn()' instead of 'Object.prototype.hasOwnProperty.call()'."
60
+ },
61
+ fixable: "code"
62
+ },
63
+ create(context) {
64
+ return {
65
+ CallExpression(node) {
66
+ if (!(node.callee.type === "MemberExpression" && node.callee.object.type === "MemberExpression")) {
67
+ return;
68
+ }
69
+
70
+ const calleePropertyName = astUtils.getStaticPropertyName(node.callee);
71
+ const objectPropertyName = astUtils.getStaticPropertyName(node.callee.object);
72
+ const isObject = hasLeftHandObject(node.callee.object);
73
+
74
+ // check `Object` scope
75
+ const scope = context.getScope();
76
+ const variable = astUtils.getVariableByName(scope, "Object");
77
+
78
+ if (
79
+ calleePropertyName === "call" &&
80
+ objectPropertyName === "hasOwnProperty" &&
81
+ isObject &&
82
+ variable && variable.scope.type === "global"
83
+ ) {
84
+ context.report({
85
+ node,
86
+ messageId: "useHasOwn",
87
+ fix(fixer) {
88
+ const sourceCode = context.getSourceCode();
89
+
90
+ if (sourceCode.getCommentsInside(node.callee).length > 0) {
91
+ return null;
92
+ }
93
+
94
+ const tokenJustBeforeNode = sourceCode.getTokenBefore(node.callee, { includeComments: true });
95
+
96
+ // for https://github.com/eslint/eslint/pull/15346#issuecomment-991417335
97
+ if (
98
+ tokenJustBeforeNode &&
99
+ tokenJustBeforeNode.range[1] === node.callee.range[0] &&
100
+ !astUtils.canTokensBeAdjacent(tokenJustBeforeNode, "Object.hasOwn")
101
+ ) {
102
+ return fixer.replaceText(node.callee, " Object.hasOwn");
103
+ }
104
+
105
+ return fixer.replaceText(node.callee, "Object.hasOwn");
106
+ }
107
+ });
108
+ }
109
+ }
110
+ };
111
+ }
112
+ };