Skip to content
Draft
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
2 changes: 1 addition & 1 deletion apps/oxlint/src/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ impl CliRunner {
if self.options.output_options.format == OutputFormat::Default {
// Build the set of enabled builtin rule names from the resolved config.
let enabled: FxHashSet<&str> =
config_store.rules().iter().map(|(rule, _)| rule.name()).collect();
config_store.rules().iter().map(|(rule, _, _)| rule.name()).collect();

let table = RuleTable::default();
for section in &table.sections {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ working directory:
`----
help: Add assertion(s) in this Test

x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jest/no-disabled-tests.html\eslint-plugin-jest(no-disabled-tests)]8;;\: Disabled test
x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/vitest/no-disabled-tests.html\eslint-plugin-vitest(no-disabled-tests)]8;;\: Disabled test
,-[fixtures/eslintrc_vitest_replace/foo.test.js:1:1]
1 | test.skip('foo', () => {
: ^^^^^^^^^
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,20 @@ working directory:
`----
help: Consider removing this declaration.

x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-loss-of-precision.html\eslint(no-loss-of-precision)]8;;\: This number literal will lose precision at runtime.
! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-unused-expressions.html\eslint(no-unused-expressions)]8;;\: Expected expression to be used
,-[fixtures/typescript_eslint/test.ts:4:1]
3 |
4 | 9007199254740993 // no-loss-of-precision
: ^^^^^^^^^^^^^^^^
`----
help: Consider using this expression or removing it

! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-unused-expressions.html\eslint(no-unused-expressions)]8;;\: Expected expression to be used
x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/typescript/no-loss-of-precision.html\typescript-eslint(no-loss-of-precision)]8;;\: This number literal will lose precision at runtime.
,-[fixtures/typescript_eslint/test.ts:4:1]
3 |
4 | 9007199254740993 // no-loss-of-precision
: ^^^^^^^^^^^^^^^^
`----
help: Consider using this expression or removing it

Found 2 warnings and 1 error.
Finished in <variable>ms on 1 file with 53 rules using 1 threads.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,20 @@ working directory:
`----
help: Consider removing this declaration.

x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-loss-of-precision.html\eslint(no-loss-of-precision)]8;;\: This number literal will lose precision at runtime.
! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-unused-expressions.html\eslint(no-unused-expressions)]8;;\: Expected expression to be used
,-[fixtures/typescript_eslint/test.ts:4:1]
3 |
4 | 9007199254740993 // no-loss-of-precision
: ^^^^^^^^^^^^^^^^
`----
help: Consider using this expression or removing it

! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-unused-expressions.html\eslint(no-unused-expressions)]8;;\: Expected expression to be used
x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/typescript/no-loss-of-precision.html\typescript-eslint(no-loss-of-precision)]8;;\: This number literal will lose precision at runtime.
,-[fixtures/typescript_eslint/test.ts:4:1]
3 |
4 | 9007199254740993 // no-loss-of-precision
: ^^^^^^^^^^^^^^^^
`----
help: Consider using this expression or removing it

Found 3 warnings and 1 error.
Finished in <variable>ms on 1 file with 65 rules using 1 threads.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ arguments: -c .oxlintrc-vitest.json --report-unused-disable-directives test.js
working directory: fixtures/disable_vitest_rules
----------

x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jest/no-restricted-jest-methods.html\eslint-plugin-jest(no-restricted-jest-methods)]8;;\: Use of `advanceTimersByTime` is not allowed
x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/vitest/no-restricted-jest-methods.html\eslint-plugin-vitest(no-restricted-jest-methods)]8;;\: Use of `advanceTimersByTime` is not allowed
,-[test.js:7:6]
6 | it('calls the callback after 1 second via advanceTimersByTime', () => {
7 | vi.advanceTimersByTime(1000);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ arguments: -c oxlint-typescript.json test.js
working directory: fixtures/eslint_and_typescript_alias_rules
----------

x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-magic-numbers.html\eslint(no-magic-numbers)]8;;\: No magic number: 0.19
x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/typescript/no-magic-numbers.html\typescript-eslint(no-magic-numbers)]8;;\: No magic number: 0.19
,-[test.js:4:32]
3 | const price = 200;
4 | const price_with_tax = price * 0.19; // taxes are expensive
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ arguments: -c oxlint-vitest.json test.js
working directory: fixtures/jest_and_vitest_alias_rules
----------

x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jest/no-identical-title.html\eslint-plugin-jest(no-identical-title)]8;;\: Test title is used multiple times in the same describe block.
x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/vitest/no-identical-title.html\eslint-plugin-vitest(no-identical-title)]8;;\: Test title is used multiple times in the same describe block.
,-[test.js:3:6]
2 | it("works", () => {});
3 | it("works", () => {});
Expand Down
79 changes: 44 additions & 35 deletions crates/oxc_linter/src/config/config_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::{
AllowWarnDeny, ExternalPluginStore, LintConfig, LintFilter, LintFilterKind, Oxlintrc,
RuleCategory, RuleEnum,
config::{
ESLintRule, OxlintOverrides, OxlintRules,
ConfiguredNamespace, ESLintRule, OxlintOverrides, OxlintRules,
external_plugins::ExternalPluginEntry,
overrides::OxlintOverride,
plugins::{LintPlugins, is_normal_plugin_name, normalize_plugin_name},
Expand All @@ -32,7 +32,7 @@ use super::{

#[must_use = "You dropped your builder without building a Linter! Did you mean to call .build()?"]
pub struct ConfigStoreBuilder {
pub(super) rules: FxHashMap<RuleEnum, AllowWarnDeny>,
pub(super) rules: FxHashMap<RuleEnum, (AllowWarnDeny, ConfiguredNamespace)>,
pub(super) external_rules: FxHashMap<ExternalRuleId, (ExternalOptionsId, AllowWarnDeny)>,
config: LintConfig,
categories: OxlintCategories,
Expand Down Expand Up @@ -73,7 +73,7 @@ impl ConfigStoreBuilder {
let config = LintConfig { plugins: LintPlugins::all(), ..LintConfig::default() };
let overrides = OxlintOverrides::default();
let categories: OxlintCategories = OxlintCategories::default();
let rules = RULES.iter().map(|rule| (rule.clone(), AllowWarnDeny::Warn)).collect();
let rules = RULES.iter().map(|rule| (rule.clone(), (AllowWarnDeny::Warn, None))).collect();
let external_rules = FxHashMap::default();
let extended_paths = Vec::new();
Self { rules, external_rules, config, categories, overrides, extended_paths }
Expand Down Expand Up @@ -281,8 +281,13 @@ impl ConfigStoreBuilder {
}

#[cfg(test)]
pub(crate) fn with_rule(mut self, rule: RuleEnum, severity: AllowWarnDeny) -> Self {
self.rules.insert(rule, severity);
pub(crate) fn with_rule_and_namespace(
mut self,
rule: RuleEnum,
severity: AllowWarnDeny,
namespace: super::rules::ConfiguredNamespace,
) -> Self {
self.rules.insert(rule, (severity, namespace));
self
}

Expand Down Expand Up @@ -376,10 +381,10 @@ impl ConfigStoreBuilder {
// If the rule is already in the list, just update its severity.
// Otherwise, add it to the map.

if let Some(existing_rule) = self.rules.get_mut(rule) {
*existing_rule = severity;
if let Some((existing_severity, _)) = self.rules.get_mut(rule) {
*existing_severity = severity;
} else {
self.rules.insert(rule.clone(), severity);
self.rules.insert(rule.clone(), (severity, None));
}
}
}
Expand Down Expand Up @@ -451,8 +456,10 @@ impl ConfigStoreBuilder {
external_plugin_store,
)?;

// Convert to vectors
builtin_rules.extend(rules_map.into_iter());
// Convert to vectors - convert from (RuleEnum, (AllowWarnDeny, ConfiguredNamespace)) to (RuleEnum, AllowWarnDeny, ConfiguredNamespace)
builtin_rules.extend(
rules_map.into_iter().map(|(rule, (severity, ns))| (rule, severity, ns)),
);
external_rules.extend(
external_rules_map
.into_iter()
Expand All @@ -473,7 +480,9 @@ impl ConfigStoreBuilder {
}

/// Warn for all correctness rules in the given set of plugins.
fn warn_correctness(mut plugins: LintPlugins) -> FxHashMap<RuleEnum, AllowWarnDeny> {
fn warn_correctness(
mut plugins: LintPlugins,
) -> FxHashMap<RuleEnum, (AllowWarnDeny, ConfiguredNamespace)> {
if plugins.contains(LintPlugins::VITEST) {
plugins |= LintPlugins::JEST;
}
Expand All @@ -486,7 +495,7 @@ impl ConfigStoreBuilder {
&& LintPlugins::try_from(rule.plugin_name())
.is_ok_and(|plugin_flag| plugins.contains(plugin_flag))
})
.map(|rule| (rule.clone(), AllowWarnDeny::Warn))
.map(|rule| (rule.clone(), (AllowWarnDeny::Warn, None)))
.collect()
}

Expand All @@ -506,7 +515,7 @@ impl ConfigStoreBuilder {
.rules
.iter()
.sorted_unstable_by_key(|(r, _)| (r.plugin_name(), r.name()))
.map(|(r, severity)| ESLintRule {
.map(|(r, (severity, _configured_namespace))| ESLintRule {
plugin_name: r.plugin_name().to_string(),
rule_name: r.name().to_string(),
severity: *severity,
Expand Down Expand Up @@ -729,7 +738,7 @@ mod test {

// populated with all correctness-level ESLint rules at a "warn" severity
assert!(!builder.rules.is_empty());
for (rule, severity) in &builder.rules {
for (rule, (severity, _)) in &builder.rules {
assert_eq!(rule.category(), RuleCategory::Correctness);
assert_eq!(*severity, AllowWarnDeny::Warn);
let plugin_name = rule.plugin_name();
Expand Down Expand Up @@ -762,7 +771,7 @@ mod test {
assert!(!builder.rules.is_empty());
assert_eq!(initial_rule_count, rule_count_after_deny);

for (rule, severity) in &builder.rules {
for (rule, (severity, _)) in &builder.rules {
assert_eq!(rule.category(), RuleCategory::Correctness);
assert_eq!(*severity, AllowWarnDeny::Deny);

Expand Down Expand Up @@ -791,7 +800,7 @@ mod test {
"Changing a single rule from warn to deny should not add a new one, just modify what's already there."
);

let (_, severity) = builder
let (_, (severity, _)) = builder
.rules
.iter()
.find(|(r, _)| r.plugin_name() == "eslint" && r.name() == "no-const-assign")
Expand All @@ -810,7 +819,7 @@ mod test {
// sanity check: not already turned on
assert!(!builder.rules.iter().any(|(r, _)| r.name() == "no-console"));
let builder = builder.with_filter(&filter);
let (_, severity) = builder
let (_, (severity, _)) = builder
.rules
.iter()
.find(|(r, _)| r.plugin_name() == "eslint" && r.name() == "no-console")
Expand All @@ -831,7 +840,7 @@ mod test {
!builder.rules.is_empty(),
"warning on categories after allowing all rules should populate the rules set"
);
for (rule, severity) in &builder.rules {
for (rule, (severity, _)) in &builder.rules {
let plugin = rule.plugin_name();
let name = rule.name();
assert_eq!(
Expand Down Expand Up @@ -873,7 +882,7 @@ mod test {
.with_builtin_plugins(desired_plugins)
.build(&mut external_plugin_store)
.unwrap();
for (rule, _) in linter.base.rules.iter() {
for (rule, _, _) in linter.base.rules.iter() {
let name = rule.name();
let plugin = rule.plugin_name();
assert_ne!(
Expand Down Expand Up @@ -944,7 +953,7 @@ mod test {
&LintFilter::new(AllowWarnDeny::Deny, "react-hooks/exhaustive-deps").unwrap(),
);

let (rule, sev) = builder
let (rule, (sev, _)) = builder
.rules
.iter()
.find(|(r, _)| r.plugin_name() == "react" && r.name() == "exhaustive-deps")
Expand Down Expand Up @@ -989,7 +998,7 @@ mod test {
ConfigStoreBuilder::from_oxlintrc(false, oxlintrc, None, &mut external_plugin_store)
.unwrap()
};
for (rule, severity) in &builder.rules {
for (rule, (severity, _)) in &builder.rules {
let name = rule.name();
let plugin = rule.plugin_name();
let category = rule.category();
Expand Down Expand Up @@ -1054,25 +1063,25 @@ mod test {
update_rules_config
.rules()
.iter()
.any(|(r, severity)| r.name() == "no-debugger" && *severity == AllowWarnDeny::Warn)
.any(|(r, severity, _)| r.name() == "no-debugger"
&& *severity == AllowWarnDeny::Warn)
);
assert!(
update_rules_config
.rules()
.iter()
.any(|(r, severity)| r.name() == "no-console" && *severity == AllowWarnDeny::Warn)
update_rules_config.rules().iter().any(
|(r, severity, _)| r.name() == "no-console" && *severity == AllowWarnDeny::Warn
)
);
assert!(
!update_rules_config
.rules()
.iter()
.any(|(r, severity)| r.name() == "no-null" && *severity == AllowWarnDeny::Allow)
.any(|(r, severity, _)| r.name() == "no-null" && *severity == AllowWarnDeny::Allow)
);
assert!(
update_rules_config
.rules()
.iter()
.any(|(r, severity)| r.name() == "prefer-as-const"
.any(|(r, severity, _)| r.name() == "prefer-as-const"
&& *severity == AllowWarnDeny::Warn)
);
}
Expand All @@ -1090,7 +1099,7 @@ mod test {
}
"#,
);
assert!(warn_all.rules().iter().all(|(_, severity)| *severity == AllowWarnDeny::Warn));
assert!(warn_all.rules().iter().all(|(_, severity, _)| *severity == AllowWarnDeny::Warn));

let deny_all = config_store_from_str(
r#"
Expand All @@ -1103,7 +1112,7 @@ mod test {
}
"#,
);
assert!(deny_all.rules().iter().all(|(_, severity)| *severity == AllowWarnDeny::Deny));
assert!(deny_all.rules().iter().all(|(_, severity, _)| *severity == AllowWarnDeny::Deny));

let allow_all = config_store_from_str(
r#"
Expand All @@ -1116,7 +1125,7 @@ mod test {
}
"#,
);
assert!(allow_all.rules().iter().all(|(_, severity)| *severity == AllowWarnDeny::Allow));
assert!(allow_all.rules().iter().all(|(_, severity, _)| *severity == AllowWarnDeny::Allow));
assert_eq!(allow_all.number_of_rules(), 0);

let allow_and_override_config = config_store_from_str(
Expand All @@ -1138,20 +1147,20 @@ mod test {
allow_and_override_config
.rules()
.iter()
.any(|(r, severity)| r.name() == "no-var" && *severity == AllowWarnDeny::Warn)
.any(|(r, severity, _)| r.name() == "no-var" && *severity == AllowWarnDeny::Warn)
);
assert!(
allow_and_override_config
.rules()
.iter()
.any(|(r, severity)| r.name() == "approx-constant"
.any(|(r, severity, _)| r.name() == "approx-constant"
&& *severity == AllowWarnDeny::Deny)
);
assert!(
allow_and_override_config
.rules()
.iter()
.any(|(r, severity)| r.name() == "no-null" && *severity == AllowWarnDeny::Deny)
.any(|(r, severity, _)| r.name() == "no-null" && *severity == AllowWarnDeny::Deny)
);
}

Expand Down Expand Up @@ -1325,7 +1334,7 @@ mod test {
// because current config's override sets it to "off", which should take priority
// over the extended config's override which sets it to "error"
let no_const_assign_rule =
resolved.rules.iter().find(|(rule, _)| rule.name() == "no-const-assign");
resolved.rules.iter().find(|(rule, _, _)| rule.name() == "no-const-assign");

assert!(
no_const_assign_rule.is_none(),
Expand Down
Loading