diff --git a/crates/oxc_linter/src/rules/eslint/func_names.rs b/crates/oxc_linter/src/rules/eslint/func_names.rs index 3973f1d790bc1..77cbb1bcf4013 100644 --- a/crates/oxc_linter/src/rules/eslint/func_names.rs +++ b/crates/oxc_linter/src/rules/eslint/func_names.rs @@ -11,7 +11,7 @@ use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_semantic::NodeId; use oxc_span::{Atom, GetSpan, Span}; -use oxc_syntax::identifier::is_identifier_name; +use oxc_syntax::{identifier::is_identifier_name, keyword::is_reserved_keyword_or_global_object}; use crate::{ AstNode, @@ -289,9 +289,6 @@ fn is_recursive_function(func: &Function, func_name: &str, ctx: &LintContext) -> false } -const INVALID_IDENTIFIER_NAMES: [&str; 9] = - ["arguments", "async", "await", "constructor", "default", "eval", "null", "undefined", "yield"]; - fn diagnostic_invalid_function( func: &Function, node: &AstNode, @@ -327,7 +324,12 @@ fn diagnostic_invalid_function( } fn is_valid_identifier_name(name: &str) -> bool { - !INVALID_IDENTIFIER_NAMES.contains(&name) && is_identifier_name(name) + // Reserved keywords and global objects (e.g., `undefined`, `NaN`) cannot be used as function names. + // Additionally, `arguments`, `eval`, and `constructor` have special semantics in strict mode + // or class contexts and should be avoided. + !is_reserved_keyword_or_global_object(name) + && !matches!(name, "arguments" | "eval" | "constructor" | "async") + && is_identifier_name(name) } /// Determines whether the current FunctionExpression node is a get, set, or @@ -450,27 +452,38 @@ fn apply_rule_fix( } fn guess_function_name<'a>(ctx: &LintContext<'a>, node_id: NodeId) -> Option> { - ctx.nodes().ancestor_kinds(node_id).find_map(|parent_kind| match parent_kind { - AstKind::AssignmentExpression(assign) => assign - .left - .get_identifier_name() - .filter(|name| is_valid_identifier_name(name)) - .map(Cow::Borrowed), - AstKind::VariableDeclarator(decl) => decl - .id - .get_identifier_name() - .as_ref() - .map(Atom::as_str) - .filter(|name| is_valid_identifier_name(name)) - .map(Cow::Borrowed), - AstKind::ObjectProperty(prop) => { - prop.key.static_name().filter(|name| is_valid_identifier_name(name)) - } - AstKind::PropertyDefinition(prop_def) => { - prop_def.key.static_name().filter(|name| is_valid_identifier_name(name)) + for parent_kind in ctx.nodes().ancestor_kinds(node_id) { + match parent_kind { + AstKind::AssignmentExpression(assign) => { + // Stop here - we found the direct assignment context + return assign + .left + .get_identifier_name() + .filter(|name| is_valid_identifier_name(name)) + .map(Cow::Borrowed); + } + AstKind::VariableDeclarator(decl) => { + // Stop here - we found the direct declarator context + return decl + .id + .get_identifier_name() + .as_ref() + .map(Atom::as_str) + .filter(|name| is_valid_identifier_name(name)) + .map(Cow::Borrowed); + } + AstKind::ObjectProperty(prop) => { + // Stop here - we found the direct property context + return prop.key.static_name().filter(|name| is_valid_identifier_name(name)); + } + AstKind::PropertyDefinition(prop_def) => { + // Stop here - we found the direct class property context + return prop_def.key.static_name().filter(|name| is_valid_identifier_name(name)); + } + _ => {} } - _ => None, - }) + } + None } #[test] @@ -778,6 +791,11 @@ fn test() { ( "const { default: foo = function() {} } = {}", "const { default: foo = function() {} } = {}", + always.clone(), + ), + ( + "const x = { delete: async function () {} }", + "const x = { delete: async function () {} }", always, ), ];