diff --git a/lib/rules/id-match.js b/lib/rules/id-match.js index bcc07a8e372..15908da8cfc 100644 --- a/lib/rules/id-match.js +++ b/lib/rules/id-match.js @@ -67,6 +67,8 @@ module.exports = { onlyDeclarations = !!options.onlyDeclarations, ignoreDestructuring = !!options.ignoreDestructuring; + let globalScope; + //-------------------------------------------------------------------------- // Helpers //-------------------------------------------------------------------------- @@ -77,6 +79,19 @@ module.exports = { const DECLARATION_TYPES = new Set(["FunctionDeclaration", "VariableDeclarator"]); const IMPORT_TYPES = new Set(["ImportSpecifier", "ImportNamespaceSpecifier", "ImportDefaultSpecifier"]); + /** + * Checks whether the given node represents a reference to a global variable that is not declared in the source code. + * These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables. + * @param {ASTNode} node `Identifier` node to check. + * @returns {boolean} `true` if the node is a reference to a global variable. + */ + function isReferenceToGlobalVariable(node) { + const variable = globalScope.set.get(node.name); + + return variable && variable.defs.length === 0 && + variable.references.some(ref => ref.identifier === node); + } + /** * Checks if a string matches the provided pattern * @param {string} name The string to check. @@ -155,11 +170,19 @@ module.exports = { return { + Program() { + globalScope = context.getScope(); + }, + Identifier(node) { const name = node.name, parent = node.parent, effectiveParent = (parent.type === "MemberExpression") ? parent.parent : parent; + if (isReferenceToGlobalVariable(node)) { + return; + } + if (parent.type === "MemberExpression") { if (!checkProperties) { diff --git a/tests/lib/rules/id-match.js b/tests/lib/rules/id-match.js index d1fad2ecb8b..2f48a4b2d2c 100644 --- a/tests/lib/rules/id-match.js +++ b/tests/lib/rules/id-match.js @@ -185,6 +185,19 @@ ruleTester.run("id-match", rule, { }] }, + // Should not report for global references - https://github.com/eslint/eslint/issues/15395 + { + code: ` + const foo = Object.keys(bar); + const a = Array.from(b); + const bar = () => Array; + `, + options: ["^\\$?[a-z]+([A-Z0-9][a-z0-9]+)*$", { + properties: true + }], + parserOptions: { ecmaVersion: 2022 } + }, + // Class Methods { code: "class x { foo() {} }", @@ -641,6 +654,76 @@ ruleTester.run("id-match", rule, { ] }, + // https://github.com/eslint/eslint/issues/15395 + { + code: ` + const foo_variable = 1; + class MyClass { + } + let a = new MyClass(); + let b = {id: 1}; + let c = Object.keys(b); + let d = Array.from(b); + let e = (Object) => Object.keys(obj, prop); // not global Object + let f = (Array) => Array.from(obj, prop); // not global Array + foo.Array = 5; // not global Array + `, + options: ["^\\$?[a-z]+([A-Z0-9][a-z0-9]+)*$", { + properties: true + }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { + message: "Identifier 'foo_variable' does not match the pattern '^\\$?[a-z]+([A-Z0-9][a-z0-9]+)*$'.", + type: "Identifier", + line: 2, + column: 19 + }, + { + message: "Identifier 'MyClass' does not match the pattern '^\\$?[a-z]+([A-Z0-9][a-z0-9]+)*$'.", + type: "Identifier", + line: 3, + column: 19 + }, + + // let e = (Object) => Object.keys(obj, prop) + { + message: "Identifier 'Object' does not match the pattern '^\\$?[a-z]+([A-Z0-9][a-z0-9]+)*$'.", + type: "Identifier", + line: 9, + column: 22 + }, + { + message: "Identifier 'Object' does not match the pattern '^\\$?[a-z]+([A-Z0-9][a-z0-9]+)*$'.", + type: "Identifier", + line: 9, + column: 33 + }, + + // let f =(Array) => Array.from(obj, prop); + { + message: "Identifier 'Array' does not match the pattern '^\\$?[a-z]+([A-Z0-9][a-z0-9]+)*$'.", + type: "Identifier", + line: 10, + column: 22 + }, + { + message: "Identifier 'Array' does not match the pattern '^\\$?[a-z]+([A-Z0-9][a-z0-9]+)*$'.", + type: "Identifier", + line: 10, + column: 32 + }, + + // foo.Array = 5; + { + message: "Identifier 'Array' does not match the pattern '^\\$?[a-z]+([A-Z0-9][a-z0-9]+)*$'.", + type: "Identifier", + line: 11, + column: 17 + } + ] + }, + // Class Methods { code: "class x { _foo() {} }",