Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ Start from the `crates/` directory when exploring the codebase.

Sub-directories named `generated` are code generated, do not edit files in these directories.


### Important Crates Overview

- **`oxc_parser`** - JavaScript/TypeScript parser
Expand Down
85 changes: 85 additions & 0 deletions crates/oxc_linter/tests/rule_configuration_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//! Test to ensure rule configuration consistency
//!
//! This test verifies that for all linter rules, the default configuration
//! obtained via `Default::default()` matches the configuration obtained via
//! `from_configuration(null)`. This helps prevent bugs where rules behave
//! differently depending on how they are initialized.

use oxc_linter::rules::RULES;
use serde_json::Value;

/// Test to ensure consistency between `rule::default()` and `rule::from_configuration(null)`.
///
/// This test helps prevent issues where a rule's default configuration differs from
/// its configuration when initialized with a null value. Both should produce identical
/// results to maintain consistency in rule behavior.
///
/// See issue #12718 for an example of why this test is important.
#[test]
fn test_rule_default_matches_from_configuration_null() {
let mut failures = Vec::new();

// Rules that are known to have configuration mismatches and need to be fixed
// TODO: These should be removed as the rules are fixed to have consistent defaults
// When fixing a rule, ensure that either:
// 1. The Default implementation returns the same values as from_configuration(null), or
// 2. The from_configuration method is updated to return Default::default() when given null
let exceptions = [
"eslint/new-cap",
"eslint/no-unneeded-ternary",
"eslint/no-else-return",
"import/extensions",
"import/no-anonymous-default-export",
"jest/no-deprecated-functions",
"jest/no-large-snapshots",
"jest/prefer-lowercase-title",
"react/jsx-filename-extension",
"typescript/no-this-alias",
"unicorn/prefer-object-from-entries",
"unicorn/prefer-structured-clone",
];

// Iterate through all available linter rules
for rule in RULES.iter() {
let plugin_name = rule.plugin_name();
let rule_name = rule.name();

// Skip rules that are known to have issues
if exceptions.contains(&format!("{plugin_name}/{rule_name}").as_str()) {
continue;
}

// Get the default configuration using rule::default
let default_rule = rule.clone();

// Get the configuration using rule::from_configuration(null)
let null_configured_rule = rule.read_json(Value::Null);

// Compare the two configurations
// Since RuleEnum doesn't implement PartialEq for the inner rule types,
// we need to compare them by running them and checking their behavior
// For now, we'll use the debug representation as a proxy
let default_debug = format!("{default_rule:?}");
let null_debug = format!("{null_configured_rule:?}");

if default_debug != null_debug {
failures.push(format!(
"Rule '{plugin_name}/{rule_name}' has different configurations between default() and from_configuration(null).\n\
Default: {default_debug}\n\
From null: {null_debug}",

));
}
}

assert!(
failures.is_empty(),
"Found {} rules with configuration mismatches:\n\n{}\n\n\
To fix these issues:\n\
1. Update the Default implementation to match from_configuration(null), or\n\
2. Update from_configuration to return Default::default() when given null\n\
3. Add the rule to the exceptions list above if the mismatch is intentional",
failures.len(),
failures.join("\n\n")
);
}
Loading