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
9 changes: 9 additions & 0 deletions apps/oxlint/fixtures/invalid_config_enum/.oxlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"categories": {
"correctness": "off"
},
"rules": {
// Invalid config, valid values are "except-parens" and "always".
"eslint/no-return-assign": ["error", "foobar"]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"plugins": ["jest"],
"categories": {
"correctness": "off"
},
"rules": {
// Invalid config, the only valid option is `allow`.
"jest/no-hooks": ["error", { "foo": "bar" }]
}
}
20 changes: 20 additions & 0 deletions apps/oxlint/fixtures/invalid_config_in_override/.oxlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"plugins": ["jest"],
"categories": {
"correctness": "off"
},
"rules": {
// Valid config
"jest/no-hooks": "error"
},
"overrides": [
{
"files": ["**/*.js"],
"rules": {
// Invalid config, `allow` should only have string values. Ensure that the error
// for a rule defined in an override is properly reported.
"jest/no-hooks": ["error", { "allow": [true, false] }]
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"plugins": ["jest"],
"categories": {
"correctness": "off"
},
"rules": {
// First invalid rule: unknown option field `foo`
"jest/no-hooks": ["error", { "foo": "bar" }],

// Second invalid rule: invalid enum value
"eslint/no-return-assign": ["error", "foobar"]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"plugins": ["jest"],
"categories": {
"correctness": "off"
},
"rules": {
// Invalid config, `allow` should only have string values
"jest/no-hooks": ["error", { "allow": [true, false] }]
}
}
29 changes: 29 additions & 0 deletions apps/oxlint/src/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1472,4 +1472,33 @@ export { redundant };
&["--type-aware", "-D", "no-unnecessary-type-assertion"],
);
}

#[test]
fn test_invalid_config_invalid_config_enum() {
Tester::new().with_cwd("fixtures/invalid_config_enum".into()).test_and_snapshot(&[]);
}

#[test]
fn test_invalid_config_invalid_config_extra_options() {
Tester::new()
.with_cwd("fixtures/invalid_config_extra_options".into())
.test_and_snapshot(&[]);
}

#[test]
fn test_invalid_config_invalid_config_in_override() {
Tester::new().with_cwd("fixtures/invalid_config_in_override".into()).test_and_snapshot(&[]);
}

#[test]
fn test_invalid_config_invalid_config_multiple_rules() {
Tester::new().with_cwd("fixtures/invalid_config_in_override".into()).test_and_snapshot(&[]);
}

#[test]
fn test_invalid_config_invalid_config_type_difference() {
Tester::new()
.with_cwd("fixtures/invalid_config_type_difference".into())
.test_and_snapshot(&[]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
source: apps/oxlint/src/tester.rs
---
##########
arguments:
working directory: fixtures/invalid_config_enum
----------
Failed to parse oxlint configuration file.

x Invalid configuration for rule `no-return-assign`: unknown variant `foobar`, expected `always` or `except-parens`, received `"foobar"`

----------
CLI result: InvalidOptionConfig
----------
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
source: apps/oxlint/src/tester.rs
---
##########
arguments:
working directory: fixtures/invalid_config_extra_options
----------
Failed to parse oxlint configuration file.

x Invalid configuration for rule `jest/no-hooks`: unknown field `foo`, expected `allow`, received `{ "foo": "bar" }`

----------
CLI result: InvalidOptionConfig
----------
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
source: apps/oxlint/src/tester.rs
---
##########
arguments:
working directory: fixtures/invalid_config_in_override
----------
Failed to build configuration.

x Invalid configuration for rule `jest/no-hooks`: invalid type: boolean `true`, expected a string, received `{ "allow": [ true, false ] }`

----------
CLI result: InvalidOptionConfig
----------
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
source: apps/oxlint/src/tester.rs
---
##########
arguments:
working directory: fixtures/invalid_config_type_difference
----------
Failed to parse oxlint configuration file.

x Invalid configuration for rule `jest/no-hooks`: invalid type: boolean `true`, expected a string, received `{ "allow": [ true, false ] }`

----------
CLI result: InvalidOptionConfig
----------
48 changes: 31 additions & 17 deletions crates/oxc_linter/src/config/config_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ use crate::{
external_plugins::ExternalPluginEntry,
overrides::OxlintOverride,
plugins::{LintPlugins, is_normal_plugin_name, normalize_plugin_name},
rules::OverrideRulesError,
},
external_linter::ExternalLinter,
external_plugin_store::{ExternalOptionsId, ExternalRuleId, ExternalRuleLookupError},
external_plugin_store::{ExternalOptionsId, ExternalRuleId},
rules::RULES,
};

Expand Down Expand Up @@ -227,15 +228,12 @@ impl ConfigStoreBuilder {
{
let all_rules = builder.get_all_rules();

oxlintrc
.rules
.override_rules(
&mut builder.rules,
&mut builder.external_rules,
&all_rules,
external_plugin_store,
)
.map_err(ConfigBuilderError::ExternalRuleLookupError)?;
oxlintrc.rules.override_rules(
&mut builder.rules,
&mut builder.external_rules,
&all_rules,
external_plugin_store,
)?;
}

Ok(builder)
Expand Down Expand Up @@ -402,9 +400,7 @@ impl ConfigStoreBuilder {
}

let overrides = std::mem::take(&mut self.overrides);
let resolved_overrides = self
.resolve_overrides(overrides, external_plugin_store)
.map_err(ConfigBuilderError::ExternalRuleLookupError)?;
let resolved_overrides = self.resolve_overrides(overrides, external_plugin_store)?;

let mut rules: Vec<_> = self
.rules
Expand Down Expand Up @@ -432,7 +428,7 @@ impl ConfigStoreBuilder {
&self,
overrides: OxlintOverrides,
external_plugin_store: &mut ExternalPluginStore,
) -> Result<ResolvedOxlintOverrides, ExternalRuleLookupError> {
) -> Result<ResolvedOxlintOverrides, Vec<OverrideRulesError>> {
let resolved = overrides
.into_iter()
.map(|override_config| {
Expand All @@ -459,7 +455,7 @@ impl ConfigStoreBuilder {
.map(|(rule_id, (options_id, severity))| (rule_id, options_id, severity)),
);

Ok(ResolvedOxlintOverride {
Ok::<_, Vec<OverrideRulesError>>(ResolvedOxlintOverride {
files: override_config.files,
env: override_config.env,
globals: override_config.globals,
Expand Down Expand Up @@ -652,13 +648,17 @@ pub enum ConfigBuilderError {
plugin_specifier: String,
error: String,
},
ExternalRuleLookupError(ExternalRuleLookupError),
NoExternalLinterConfigured {
plugin_specifier: String,
},
ReservedExternalPluginName {
plugin_name: String,
},
/// Multiple errors parsing rule configuration options
RuleConfigurationErrors {
/// The errors that occurred
errors: Vec<OverrideRulesError>,
},
}

impl Display for ConfigBuilderError {
Expand Down Expand Up @@ -709,13 +709,27 @@ impl Display for ConfigBuilderError {
)?;
Ok(())
}
ConfigBuilderError::ExternalRuleLookupError(e) => std::fmt::Display::fmt(&e, f),
ConfigBuilderError::RuleConfigurationErrors { errors } => {
for (i, error) in errors.iter().enumerate() {
if i > 0 {
f.write_str("\n")?;
}
write!(f, "{error}")?;
}
Ok(())
}
}
}
}

impl std::error::Error for ConfigBuilderError {}

impl From<Vec<OverrideRulesError>> for ConfigBuilderError {
fn from(errors: Vec<OverrideRulesError>) -> Self {
ConfigBuilderError::RuleConfigurationErrors { errors }
}
}

#[cfg(test)]
mod test {
use std::path::PathBuf;
Expand Down
Loading
Loading