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/func-names.js
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
190 lines (165 sloc)
6.21 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 warn when a function expression does not have a name. | |
* @author Kyle T. Nunery | |
*/ | |
"use strict"; | |
//------------------------------------------------------------------------------ | |
// Requirements | |
//------------------------------------------------------------------------------ | |
const astUtils = require("./utils/ast-utils"); | |
/** | |
* Checks whether or not a given variable is a function name. | |
* @param {eslint-scope.Variable} variable A variable to check. | |
* @returns {boolean} `true` if the variable is a function name. | |
*/ | |
function isFunctionName(variable) { | |
return variable && variable.defs[0].type === "FunctionName"; | |
} | |
//------------------------------------------------------------------------------ | |
// Rule Definition | |
//------------------------------------------------------------------------------ | |
module.exports = { | |
meta: { | |
type: "suggestion", | |
docs: { | |
description: "require or disallow named `function` expressions", | |
category: "Stylistic Issues", | |
recommended: false, | |
url: "https://eslint.org/docs/rules/func-names" | |
}, | |
schema: { | |
definitions: { | |
value: { | |
enum: [ | |
"always", | |
"as-needed", | |
"never" | |
] | |
} | |
}, | |
items: [ | |
{ | |
$ref: "#/definitions/value" | |
}, | |
{ | |
type: "object", | |
properties: { | |
generators: { | |
$ref: "#/definitions/value" | |
} | |
}, | |
additionalProperties: false | |
} | |
] | |
}, | |
messages: { | |
unnamed: "Unexpected unnamed {{name}}.", | |
named: "Unexpected named {{name}}." | |
} | |
}, | |
create(context) { | |
const sourceCode = context.getSourceCode(); | |
/** | |
* Returns the config option for the given node. | |
* @param {ASTNode} node A node to get the config for. | |
* @returns {string} The config option. | |
*/ | |
function getConfigForNode(node) { | |
if ( | |
node.generator && | |
context.options.length > 1 && | |
context.options[1].generators | |
) { | |
return context.options[1].generators; | |
} | |
return context.options[0] || "always"; | |
} | |
/** | |
* Determines whether the current FunctionExpression node is a get, set, or | |
* shorthand method in an object literal or a class. | |
* @param {ASTNode} node A node to check. | |
* @returns {boolean} True if the node is a get, set, or shorthand method. | |
*/ | |
function isObjectOrClassMethod(node) { | |
const parent = node.parent; | |
return (parent.type === "MethodDefinition" || ( | |
parent.type === "Property" && ( | |
parent.method || | |
parent.kind === "get" || | |
parent.kind === "set" | |
) | |
)); | |
} | |
/** | |
* Determines whether the current FunctionExpression node has a name that would be | |
* inferred from context in a conforming ES6 environment. | |
* @param {ASTNode} node A node to check. | |
* @returns {boolean} True if the node would have a name assigned automatically. | |
*/ | |
function hasInferredName(node) { | |
const parent = node.parent; | |
return isObjectOrClassMethod(node) || | |
(parent.type === "VariableDeclarator" && parent.id.type === "Identifier" && parent.init === node) || | |
(parent.type === "Property" && parent.value === node) || | |
(parent.type === "AssignmentExpression" && parent.left.type === "Identifier" && parent.right === node) || | |
(parent.type === "AssignmentPattern" && parent.left.type === "Identifier" && parent.right === node); | |
} | |
/** | |
* Reports that an unnamed function should be named | |
* @param {ASTNode} node The node to report in the event of an error. | |
* @returns {void} | |
*/ | |
function reportUnexpectedUnnamedFunction(node) { | |
context.report({ | |
node, | |
messageId: "unnamed", | |
loc: astUtils.getFunctionHeadLoc(node, sourceCode), | |
data: { name: astUtils.getFunctionNameWithKind(node) } | |
}); | |
} | |
/** | |
* Reports that a named function should be unnamed | |
* @param {ASTNode} node The node to report in the event of an error. | |
* @returns {void} | |
*/ | |
function reportUnexpectedNamedFunction(node) { | |
context.report({ | |
node, | |
messageId: "named", | |
loc: astUtils.getFunctionHeadLoc(node, sourceCode), | |
data: { name: astUtils.getFunctionNameWithKind(node) } | |
}); | |
} | |
/** | |
* The listener for function nodes. | |
* @param {ASTNode} node function node | |
* @returns {void} | |
*/ | |
function handleFunction(node) { | |
// Skip recursive functions. | |
const nameVar = context.getDeclaredVariables(node)[0]; | |
if (isFunctionName(nameVar) && nameVar.references.length > 0) { | |
return; | |
} | |
const hasName = Boolean(node.id && node.id.name); | |
const config = getConfigForNode(node); | |
if (config === "never") { | |
if (hasName && node.type !== "FunctionDeclaration") { | |
reportUnexpectedNamedFunction(node); | |
} | |
} else if (config === "as-needed") { | |
if (!hasName && !hasInferredName(node)) { | |
reportUnexpectedUnnamedFunction(node); | |
} | |
} else { | |
if (!hasName && !isObjectOrClassMethod(node)) { | |
reportUnexpectedUnnamedFunction(node); | |
} | |
} | |
} | |
return { | |
"FunctionExpression:exit": handleFunction, | |
"ExportDefaultDeclaration > FunctionDeclaration": handleFunction | |
}; | |
} | |
}; |