Permalink
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
codeql-action/node_modules/eslint/lib/rules/no-underscore-dangle.js
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
335 lines (300 sloc)
12.8 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @fileoverview Rule to flag dangling underscores in variable declarations. | |
* @author Matt DuVall <http://www.mattduvall.com> | |
*/ | |
"use strict"; | |
//------------------------------------------------------------------------------ | |
// Rule Definition | |
//------------------------------------------------------------------------------ | |
/** @type {import('../shared/types').Rule} */ | |
module.exports = { | |
meta: { | |
type: "suggestion", | |
docs: { | |
description: "Disallow dangling underscores in identifiers", | |
recommended: false, | |
url: "https://eslint.org/docs/latest/rules/no-underscore-dangle" | |
}, | |
schema: [ | |
{ | |
type: "object", | |
properties: { | |
allow: { | |
type: "array", | |
items: { | |
type: "string" | |
} | |
}, | |
allowAfterThis: { | |
type: "boolean", | |
default: false | |
}, | |
allowAfterSuper: { | |
type: "boolean", | |
default: false | |
}, | |
allowAfterThisConstructor: { | |
type: "boolean", | |
default: false | |
}, | |
enforceInMethodNames: { | |
type: "boolean", | |
default: false | |
}, | |
allowFunctionParams: { | |
type: "boolean", | |
default: true | |
}, | |
enforceInClassFields: { | |
type: "boolean", | |
default: false | |
}, | |
allowInArrayDestructuring: { | |
type: "boolean", | |
default: true | |
}, | |
allowInObjectDestructuring: { | |
type: "boolean", | |
default: true | |
} | |
}, | |
additionalProperties: false | |
} | |
], | |
messages: { | |
unexpectedUnderscore: "Unexpected dangling '_' in '{{identifier}}'." | |
} | |
}, | |
create(context) { | |
const options = context.options[0] || {}; | |
const ALLOWED_VARIABLES = options.allow ? options.allow : []; | |
const allowAfterThis = typeof options.allowAfterThis !== "undefined" ? options.allowAfterThis : false; | |
const allowAfterSuper = typeof options.allowAfterSuper !== "undefined" ? options.allowAfterSuper : false; | |
const allowAfterThisConstructor = typeof options.allowAfterThisConstructor !== "undefined" ? options.allowAfterThisConstructor : false; | |
const enforceInMethodNames = typeof options.enforceInMethodNames !== "undefined" ? options.enforceInMethodNames : false; | |
const enforceInClassFields = typeof options.enforceInClassFields !== "undefined" ? options.enforceInClassFields : false; | |
const allowFunctionParams = typeof options.allowFunctionParams !== "undefined" ? options.allowFunctionParams : true; | |
const allowInArrayDestructuring = typeof options.allowInArrayDestructuring !== "undefined" ? options.allowInArrayDestructuring : true; | |
const allowInObjectDestructuring = typeof options.allowInObjectDestructuring !== "undefined" ? options.allowInObjectDestructuring : true; | |
const sourceCode = context.sourceCode; | |
//------------------------------------------------------------------------- | |
// Helpers | |
//------------------------------------------------------------------------- | |
/** | |
* Check if identifier is present inside the allowed option | |
* @param {string} identifier name of the node | |
* @returns {boolean} true if its is present | |
* @private | |
*/ | |
function isAllowed(identifier) { | |
return ALLOWED_VARIABLES.includes(identifier); | |
} | |
/** | |
* Check if identifier has a dangling underscore | |
* @param {string} identifier name of the node | |
* @returns {boolean} true if its is present | |
* @private | |
*/ | |
function hasDanglingUnderscore(identifier) { | |
const len = identifier.length; | |
return identifier !== "_" && (identifier[0] === "_" || identifier[len - 1] === "_"); | |
} | |
/** | |
* Check if identifier is a special case member expression | |
* @param {string} identifier name of the node | |
* @returns {boolean} true if its is a special case | |
* @private | |
*/ | |
function isSpecialCaseIdentifierForMemberExpression(identifier) { | |
return identifier === "__proto__"; | |
} | |
/** | |
* Check if identifier is a special case variable expression | |
* @param {string} identifier name of the node | |
* @returns {boolean} true if its is a special case | |
* @private | |
*/ | |
function isSpecialCaseIdentifierInVariableExpression(identifier) { | |
// Checks for the underscore library usage here | |
return identifier === "_"; | |
} | |
/** | |
* Check if a node is a member reference of this.constructor | |
* @param {ASTNode} node node to evaluate | |
* @returns {boolean} true if it is a reference on this.constructor | |
* @private | |
*/ | |
function isThisConstructorReference(node) { | |
return node.object.type === "MemberExpression" && | |
node.object.property.name === "constructor" && | |
node.object.object.type === "ThisExpression"; | |
} | |
/** | |
* Check if function parameter has a dangling underscore. | |
* @param {ASTNode} node function node to evaluate | |
* @returns {void} | |
* @private | |
*/ | |
function checkForDanglingUnderscoreInFunctionParameters(node) { | |
if (!allowFunctionParams) { | |
node.params.forEach(param => { | |
const { type } = param; | |
let nodeToCheck; | |
if (type === "RestElement") { | |
nodeToCheck = param.argument; | |
} else if (type === "AssignmentPattern") { | |
nodeToCheck = param.left; | |
} else { | |
nodeToCheck = param; | |
} | |
if (nodeToCheck.type === "Identifier") { | |
const identifier = nodeToCheck.name; | |
if (hasDanglingUnderscore(identifier) && !isAllowed(identifier)) { | |
context.report({ | |
node: param, | |
messageId: "unexpectedUnderscore", | |
data: { | |
identifier | |
} | |
}); | |
} | |
} | |
}); | |
} | |
} | |
/** | |
* Check if function has a dangling underscore | |
* @param {ASTNode} node node to evaluate | |
* @returns {void} | |
* @private | |
*/ | |
function checkForDanglingUnderscoreInFunction(node) { | |
if (node.type === "FunctionDeclaration" && node.id) { | |
const identifier = node.id.name; | |
if (typeof identifier !== "undefined" && hasDanglingUnderscore(identifier) && !isAllowed(identifier)) { | |
context.report({ | |
node, | |
messageId: "unexpectedUnderscore", | |
data: { | |
identifier | |
} | |
}); | |
} | |
} | |
checkForDanglingUnderscoreInFunctionParameters(node); | |
} | |
/** | |
* Check if variable expression has a dangling underscore | |
* @param {ASTNode} node node to evaluate | |
* @returns {void} | |
* @private | |
*/ | |
function checkForDanglingUnderscoreInVariableExpression(node) { | |
sourceCode.getDeclaredVariables(node).forEach(variable => { | |
const definition = variable.defs.find(def => def.node === node); | |
const identifierNode = definition.name; | |
const identifier = identifierNode.name; | |
let parent = identifierNode.parent; | |
while (!["VariableDeclarator", "ArrayPattern", "ObjectPattern"].includes(parent.type)) { | |
parent = parent.parent; | |
} | |
if ( | |
hasDanglingUnderscore(identifier) && | |
!isSpecialCaseIdentifierInVariableExpression(identifier) && | |
!isAllowed(identifier) && | |
!(allowInArrayDestructuring && parent.type === "ArrayPattern") && | |
!(allowInObjectDestructuring && parent.type === "ObjectPattern") | |
) { | |
context.report({ | |
node, | |
messageId: "unexpectedUnderscore", | |
data: { | |
identifier | |
} | |
}); | |
} | |
}); | |
} | |
/** | |
* Check if member expression has a dangling underscore | |
* @param {ASTNode} node node to evaluate | |
* @returns {void} | |
* @private | |
*/ | |
function checkForDanglingUnderscoreInMemberExpression(node) { | |
const identifier = node.property.name, | |
isMemberOfThis = node.object.type === "ThisExpression", | |
isMemberOfSuper = node.object.type === "Super", | |
isMemberOfThisConstructor = isThisConstructorReference(node); | |
if (typeof identifier !== "undefined" && hasDanglingUnderscore(identifier) && | |
!(isMemberOfThis && allowAfterThis) && | |
!(isMemberOfSuper && allowAfterSuper) && | |
!(isMemberOfThisConstructor && allowAfterThisConstructor) && | |
!isSpecialCaseIdentifierForMemberExpression(identifier) && !isAllowed(identifier)) { | |
context.report({ | |
node, | |
messageId: "unexpectedUnderscore", | |
data: { | |
identifier | |
} | |
}); | |
} | |
} | |
/** | |
* Check if method declaration or method property has a dangling underscore | |
* @param {ASTNode} node node to evaluate | |
* @returns {void} | |
* @private | |
*/ | |
function checkForDanglingUnderscoreInMethod(node) { | |
const identifier = node.key.name; | |
const isMethod = node.type === "MethodDefinition" || node.type === "Property" && node.method; | |
if (typeof identifier !== "undefined" && enforceInMethodNames && isMethod && hasDanglingUnderscore(identifier) && !isAllowed(identifier)) { | |
context.report({ | |
node, | |
messageId: "unexpectedUnderscore", | |
data: { | |
identifier: node.key.type === "PrivateIdentifier" | |
? `#${identifier}` | |
: identifier | |
} | |
}); | |
} | |
} | |
/** | |
* Check if a class field has a dangling underscore | |
* @param {ASTNode} node node to evaluate | |
* @returns {void} | |
* @private | |
*/ | |
function checkForDanglingUnderscoreInClassField(node) { | |
const identifier = node.key.name; | |
if (typeof identifier !== "undefined" && hasDanglingUnderscore(identifier) && | |
enforceInClassFields && | |
!isAllowed(identifier)) { | |
context.report({ | |
node, | |
messageId: "unexpectedUnderscore", | |
data: { | |
identifier: node.key.type === "PrivateIdentifier" | |
? `#${identifier}` | |
: identifier | |
} | |
}); | |
} | |
} | |
//-------------------------------------------------------------------------- | |
// Public API | |
//-------------------------------------------------------------------------- | |
return { | |
FunctionDeclaration: checkForDanglingUnderscoreInFunction, | |
VariableDeclarator: checkForDanglingUnderscoreInVariableExpression, | |
MemberExpression: checkForDanglingUnderscoreInMemberExpression, | |
MethodDefinition: checkForDanglingUnderscoreInMethod, | |
PropertyDefinition: checkForDanglingUnderscoreInClassField, | |
Property: checkForDanglingUnderscoreInMethod, | |
FunctionExpression: checkForDanglingUnderscoreInFunction, | |
ArrowFunctionExpression: checkForDanglingUnderscoreInFunction | |
}; | |
} | |
}; |