Skip to content

Commit 7d9bb1b

Browse files
committed
fix(linter): Update eslint/func-names to error on invalid rule config options, improve docs. (#18510)
Another part of #14743 and #16023. This fixes up the func-names rule so it matches most other rules in the codebase that have a config struct defining the configuration object it accepts. Now it will error if provided with an invalid config, and also auto-generates proper documentation for the rule config options. AI Disclosure: Generated using Claude Code, modified further and reviewed by me. ---- Generated docs: ````md ## Configuration ### The 1st option type: `"always" | "as-needed" | "never"` #### `"always"` Requires all function expressions to have a name. #### `"as-needed"` Requires a name only if one is not automatically inferred. #### `"never"` Disallows names for function expressions. ### The 2nd option This option is an object with the following properties: #### generators Configuration for generator function expressions. If not specified, uses the primary configuration. Accepts `always`, `as-needed`, or `never`. Generator functions are those defined using the `function*` syntax. ```js function* foobar(i) { yield i; yield i + 10; } ``` ````
1 parent bffd134 commit 7d9bb1b

File tree

2 files changed

+38
-54
lines changed

2 files changed

+38
-54
lines changed

crates/oxc_linter/src/rules/eslint/func_names.rs

Lines changed: 38 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ use oxc_semantic::NodeId;
1313
use oxc_span::{GetSpan, Span};
1414
use oxc_syntax::{identifier::is_identifier_name, keyword::is_reserved_keyword_or_global_object};
1515

16+
use schemars::JsonSchema;
17+
use serde::Deserialize;
18+
1619
use crate::{
1720
AstNode,
1821
ast_util::get_function_name_with_kind,
1922
context::LintContext,
2023
fixer::{RuleFix, RuleFixer},
21-
rule::Rule,
24+
rule::{Rule, TupleRuleConfig},
2225
};
2326

2427
fn named_diagnostic(function_name: &str, span: Span) -> OxcDiagnostic {
@@ -33,32 +36,43 @@ fn unnamed_diagnostic(inferred_name_or_description: &str, span: Span) -> OxcDiag
3336
.with_help("Consider giving this function expression a name.")
3437
}
3538

36-
#[derive(Debug, Clone, Default)]
37-
struct FuncNamesConfig {
38-
functions: FuncNamesConfigType,
39-
generators: FuncNamesConfigType,
40-
}
41-
42-
#[derive(Debug, Default, Clone)]
43-
pub struct FuncNames {
44-
config: FuncNamesConfig,
45-
}
46-
47-
#[derive(Debug, Default, Clone, Copy, PartialEq)]
39+
#[derive(Debug, Default, Clone, Copy, PartialEq, JsonSchema, Deserialize)]
40+
#[serde(rename_all = "kebab-case")]
4841
enum FuncNamesConfigType {
42+
/// Requires all function expressions to have a name.
4943
#[default]
5044
Always,
45+
/// Requires a name only if one is not automatically inferred.
5146
AsNeeded,
47+
/// Disallows names for function expressions.
5248
Never,
5349
}
5450

55-
impl From<&serde_json::Value> for FuncNamesConfigType {
56-
fn from(raw: &serde_json::Value) -> Self {
57-
match raw.as_str() {
58-
Some("as-needed") => Self::AsNeeded,
59-
Some("never") => Self::Never,
60-
_ => Self::Always,
61-
}
51+
#[derive(Debug, Default, Clone, JsonSchema, Deserialize)]
52+
#[serde(default)]
53+
pub struct FuncNames(FuncNamesConfigType, FuncNamesGeneratorsConfig);
54+
55+
#[derive(Debug, Default, Clone, JsonSchema, Deserialize)]
56+
#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
57+
pub struct FuncNamesGeneratorsConfig {
58+
/// Configuration for generator function expressions. If not specified, uses the
59+
/// primary configuration.
60+
///
61+
/// Accepts `always`, `as-needed`, or `never`.
62+
///
63+
/// Generator functions are those defined using the `function*` syntax.
64+
/// ```js
65+
/// function* foobar(i) {
66+
/// yield i;
67+
/// yield i + 10;
68+
/// }
69+
/// ```
70+
generators: Option<FuncNamesConfigType>,
71+
}
72+
73+
impl FuncNames {
74+
fn generators_config(&self) -> FuncNamesConfigType {
75+
self.1.generators.unwrap_or(self.0)
6276
}
6377
}
6478

@@ -74,24 +88,6 @@ declare_oxc_lint!(
7488
/// This makes it more difficult to find where an error is thrown.
7589
/// Providing an explicit name also improves readability and consistency.
7690
///
77-
/// ### Options
78-
///
79-
/// First option:
80-
/// - Type: `string`
81-
/// - Default: `"always"`
82-
/// - Possible values:
83-
/// - `"always"` - requires all function expressions to have a name.
84-
/// - `"as-needed"` - requires a name only if one is not automatically inferred.
85-
/// - `"never"` - disallows names for function expressions.
86-
///
87-
/// Second option:
88-
/// - Type: `object`
89-
/// - Properties:
90-
/// - `generators`: `("always" | "as-needed" | "never")` (default: falls back to first option)
91-
/// - `"always"` - require named generator function expressions.
92-
/// - `"as-needed"` - require a name only when not inferred.
93-
/// - `"never"` - disallow names for generator function expressions.
94-
///
9591
/// Example configuration:
9692
/// ```json
9793
/// {
@@ -221,30 +217,19 @@ declare_oxc_lint!(
221217
FuncNames,
222218
eslint,
223219
style,
224-
conditional_fix_suggestion
220+
conditional_fix_suggestion,
221+
config = FuncNames,
225222
);
226223

227224
impl Rule for FuncNames {
228225
fn from_configuration(value: serde_json::Value) -> Result<Self, serde_json::error::Error> {
229-
let Some(functions_config) = value.get(0) else {
230-
return Ok(Self::default());
231-
};
232-
let generators_config =
233-
value.get(1).and_then(|v| v.get("generators")).unwrap_or(functions_config);
234-
235-
Ok(Self {
236-
config: FuncNamesConfig {
237-
functions: FuncNamesConfigType::from(functions_config),
238-
generators: FuncNamesConfigType::from(generators_config),
239-
},
240-
})
226+
serde_json::from_value::<TupleRuleConfig<Self>>(value).map(TupleRuleConfig::into_inner)
241227
}
242228

243229
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
244230
if let AstKind::Function(func) = node.kind() {
245231
let parent_node = ctx.nodes().parent_node(node.id());
246-
let config =
247-
if func.generator { self.config.generators } else { self.config.functions };
232+
let config = if func.generator { self.generators_config() } else { self.0 };
248233

249234
if is_invalid_function(config, func, parent_node) {
250235
// For named functions, check if they're recursive (need their name for recursion)

crates/oxc_linter/tests/rule_configuration_documentation_test.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ fn test_rules_with_custom_configuration_have_schema() {
3030
// list - newly-created rules should always be documented before being merged!
3131
let exceptions: &[&str] = &[
3232
// eslint
33-
"eslint/func-names",
3433
"eslint/no-empty-function",
3534
"eslint/no-restricted-imports",
3635
"eslint/no-warning-comments",

0 commit comments

Comments
 (0)