2 * @fileoverview Rule to warn when a function expression does not have a name.
3 * @author Kyle T. Nunery
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const astUtils = require("./utils/ast-utils");
15 * Checks whether or not a given variable is a function name.
16 * @param {eslint-scope.Variable} variable A variable to check.
17 * @returns {boolean} `true` if the variable is a function name.
19 function isFunctionName(variable) {
20 return variable && variable.defs[0].type === "FunctionName";
23 //------------------------------------------------------------------------------
25 //------------------------------------------------------------------------------
32 description: "require or disallow named `function` expressions",
33 category: "Stylistic Issues",
35 url: "https://eslint.org/docs/rules/func-names"
50 $ref: "#/definitions/value"
56 $ref: "#/definitions/value"
59 additionalProperties: false
65 unnamed: "Unexpected unnamed {{name}}.",
66 named: "Unexpected named {{name}}."
72 const sourceCode = context.getSourceCode();
75 * Returns the config option for the given node.
76 * @param {ASTNode} node A node to get the config for.
77 * @returns {string} The config option.
79 function getConfigForNode(node) {
82 context.options.length > 1 &&
83 context.options[1].generators
85 return context.options[1].generators;
88 return context.options[0] || "always";
92 * Determines whether the current FunctionExpression node is a get, set, or
93 * shorthand method in an object literal or a class.
94 * @param {ASTNode} node A node to check.
95 * @returns {boolean} True if the node is a get, set, or shorthand method.
97 function isObjectOrClassMethod(node) {
98 const parent = node.parent;
100 return (parent.type === "MethodDefinition" || (
101 parent.type === "Property" && (
103 parent.kind === "get" ||
104 parent.kind === "set"
110 * Determines whether the current FunctionExpression node has a name that would be
111 * inferred from context in a conforming ES6 environment.
112 * @param {ASTNode} node A node to check.
113 * @returns {boolean} True if the node would have a name assigned automatically.
115 function hasInferredName(node) {
116 const parent = node.parent;
118 return isObjectOrClassMethod(node) ||
119 (parent.type === "VariableDeclarator" && parent.id.type === "Identifier" && parent.init === node) ||
120 (parent.type === "Property" && parent.value === node) ||
121 (parent.type === "AssignmentExpression" && parent.left.type === "Identifier" && parent.right === node) ||
122 (parent.type === "AssignmentPattern" && parent.left.type === "Identifier" && parent.right === node);
126 * Reports that an unnamed function should be named
127 * @param {ASTNode} node The node to report in the event of an error.
130 function reportUnexpectedUnnamedFunction(node) {
133 messageId: "unnamed",
134 loc: astUtils.getFunctionHeadLoc(node, sourceCode),
135 data: { name: astUtils.getFunctionNameWithKind(node) }
140 * Reports that a named function should be unnamed
141 * @param {ASTNode} node The node to report in the event of an error.
144 function reportUnexpectedNamedFunction(node) {
148 loc: astUtils.getFunctionHeadLoc(node, sourceCode),
149 data: { name: astUtils.getFunctionNameWithKind(node) }
154 * The listener for function nodes.
155 * @param {ASTNode} node function node
158 function handleFunction(node) {
160 // Skip recursive functions.
161 const nameVar = context.getDeclaredVariables(node)[0];
163 if (isFunctionName(nameVar) && nameVar.references.length > 0) {
167 const hasName = Boolean(node.id && node.id.name);
168 const config = getConfigForNode(node);
170 if (config === "never") {
171 if (hasName && node.type !== "FunctionDeclaration") {
172 reportUnexpectedNamedFunction(node);
174 } else if (config === "as-needed") {
175 if (!hasName && !hasInferredName(node)) {
176 reportUnexpectedUnnamedFunction(node);
179 if (!hasName && !isObjectOrClassMethod(node)) {
180 reportUnexpectedUnnamedFunction(node);
186 "FunctionExpression:exit": handleFunction,
187 "ExportDefaultDeclaration > FunctionDeclaration": handleFunction