Skip to content

Commit 3f91f24

Browse files
committed
refactor(linter): remove RulesCache (#11981)
Keeping `RulesCache` makes `ConfigStoreBuilder` wayy too complicated. For now, we are going to remove it (ther may be a tiny perf hit, but in the majority of repos, this code is ony run once so should be ok. Once custom JS plugins are complete, we can consider adding back in the cache
1 parent 54582cb commit 3f91f24

File tree

2 files changed

+40
-119
lines changed

2 files changed

+40
-119
lines changed

crates/oxc_linter/src/config/config_builder.rs

Lines changed: 39 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use std::{
2-
cell::{Ref, RefCell},
32
fmt::{self, Debug, Display},
43
path::PathBuf,
54
};
@@ -23,7 +22,6 @@ pub struct ConfigStoreBuilder {
2322
config: LintConfig,
2423
categories: OxlintCategories,
2524
overrides: OxlintOverrides,
26-
cache: RulesCache,
2725

2826
// Collect all `extends` file paths for the language server.
2927
// The server will tell the clients to watch for the extends files.
@@ -46,10 +44,9 @@ impl ConfigStoreBuilder {
4644
let rules = FxHashMap::default();
4745
let categories: OxlintCategories = OxlintCategories::default();
4846
let overrides = OxlintOverrides::default();
49-
let cache = RulesCache::new(config.plugins);
5047
let extended_paths = Vec::new();
5148

52-
Self { rules, config, categories, overrides, cache, extended_paths }
49+
Self { rules, config, categories, overrides, extended_paths }
5350
}
5451

5552
/// Warn on all rules in all plugins and categories, including those in `nursery`.
@@ -60,10 +57,9 @@ impl ConfigStoreBuilder {
6057
let config = LintConfig { plugins: LintPlugins::all(), ..LintConfig::default() };
6158
let overrides = OxlintOverrides::default();
6259
let categories: OxlintCategories = OxlintCategories::default();
63-
let cache = RulesCache::new(config.plugins);
6460
let rules = RULES.iter().map(|rule| (rule.clone(), AllowWarnDeny::Warn)).collect();
6561
let extended_paths = Vec::new();
66-
Self { rules, config, categories, overrides, cache, extended_paths }
62+
Self { rules, config, categories, overrides, extended_paths }
6763
}
6864

6965
/// Create a [`ConfigStoreBuilder`] from a loaded or manually built [`Oxlintrc`].
@@ -154,25 +150,18 @@ impl ConfigStoreBuilder {
154150
globals: oxlintrc.globals,
155151
path: Some(oxlintrc.path),
156152
};
157-
let cache = RulesCache::new(config.plugins);
158-
159-
let mut builder = Self {
160-
rules,
161-
config,
162-
categories,
163-
overrides: oxlintrc.overrides,
164-
cache,
165-
extended_paths,
166-
};
153+
154+
let mut builder =
155+
Self { rules, config, categories, overrides: oxlintrc.overrides, extended_paths };
167156

168157
for filter in oxlintrc.categories.filters() {
169158
builder = builder.with_filter(&filter);
170159
}
171160

172161
{
173-
let all_rules = builder.cache.borrow();
162+
let all_rules = builder.get_all_rules();
174163

175-
oxlintrc.rules.override_rules(&mut builder.rules, all_rules.as_slice());
164+
oxlintrc.rules.override_rules(&mut builder.rules, &all_rules);
176165
}
177166

178167
Ok(builder)
@@ -194,7 +183,6 @@ impl ConfigStoreBuilder {
194183
#[inline]
195184
pub fn with_plugins(mut self, plugins: LintPlugins) -> Self {
196185
self.config.plugins = plugins;
197-
self.cache.set_plugins(plugins);
198186
self
199187
}
200188

@@ -210,7 +198,6 @@ impl ConfigStoreBuilder {
210198
#[inline]
211199
pub fn and_plugins(mut self, plugins: LintPlugins, enabled: bool) -> Self {
212200
self.config.plugins.set(plugins, enabled);
213-
self.cache.set_plugins(self.config.plugins);
214201
self
215202
}
216203

@@ -272,11 +259,30 @@ impl ConfigStoreBuilder {
272259
/// Warn/Deny a let of rules based on some predicate. Rules already in `self.rules` get
273260
/// re-configured, while those that are not are added. Affects rules where `query` returns
274261
/// `true`.
262+
fn get_all_rules(&self) -> Vec<RuleEnum> {
263+
if self.config.plugins.is_all() {
264+
RULES.clone()
265+
} else {
266+
let mut plugins = self.config.plugins;
267+
268+
// we need to include some jest rules when vitest is enabled, see [`VITEST_COMPATIBLE_JEST_RULES`]
269+
if plugins.contains(LintPlugins::VITEST) {
270+
plugins = plugins.union(LintPlugins::JEST);
271+
}
272+
273+
RULES
274+
.iter()
275+
.filter(|rule| plugins.contains(LintPlugins::from(rule.plugin_name())))
276+
.cloned()
277+
.collect()
278+
}
279+
}
280+
275281
fn upsert_where<F>(&mut self, severity: AllowWarnDeny, query: F)
276282
where
277283
F: Fn(&&RuleEnum) -> bool,
278284
{
279-
let all_rules = self.cache.borrow();
285+
let all_rules = self.get_all_rules();
280286
// NOTE: we may want to warn users if they're configuring a rule that does not exist.
281287
let rules_to_configure = all_rules.iter().filter(query);
282288
for rule in rules_to_configure {
@@ -295,15 +301,18 @@ impl ConfigStoreBuilder {
295301
// When a plugin gets disabled before build(), rules for that plugin aren't removed until
296302
// with_filters() gets called. If the user never calls it, those now-undesired rules need
297303
// to be taken out.
298-
let plugins = self.plugins();
299-
let mut rules = if self.cache.is_stale() {
300-
self.rules
301-
.into_iter()
302-
.filter(|(r, _)| plugins.contains(r.plugin_name().into()))
303-
.collect()
304-
} else {
305-
self.rules.into_iter().collect::<Vec<_>>()
306-
};
304+
let mut plugins = self.plugins();
305+
306+
// Apply the same Vitest->Jest logic as in get_all_rules()
307+
if plugins.contains(LintPlugins::VITEST) {
308+
plugins = plugins.union(LintPlugins::JEST);
309+
}
310+
311+
let mut rules: Vec<_> = self
312+
.rules
313+
.into_iter()
314+
.filter(|(r, _)| plugins.contains(r.plugin_name().into()))
315+
.collect();
307316
rules.sort_unstable_by_key(|(r, _)| r.id());
308317
Config::new(rules, self.categories, self.config, self.overrides)
309318
}
@@ -411,94 +420,6 @@ impl Display for ConfigBuilderError {
411420

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

414-
struct RulesCache {
415-
all_rules: RefCell<Option<Vec<RuleEnum>>>,
416-
plugins: LintPlugins,
417-
last_fresh_plugins: LintPlugins,
418-
}
419-
420-
impl RulesCache {
421-
#[inline]
422-
#[must_use]
423-
pub fn new(plugins: LintPlugins) -> Self {
424-
Self { all_rules: RefCell::new(None), plugins, last_fresh_plugins: plugins }
425-
}
426-
427-
pub fn set_plugins(&mut self, plugins: LintPlugins) {
428-
if self.plugins == plugins {
429-
return;
430-
}
431-
self.last_fresh_plugins = self.plugins;
432-
self.plugins = plugins;
433-
self.clear();
434-
}
435-
436-
pub fn is_stale(&self) -> bool {
437-
// NOTE: After all_rules cache has been initialized _at least once_ (e.g. its borrowed, or
438-
// initialize() is called), all_rules will be some if and only if last_fresh_plugins ==
439-
// plugins. Right before creation, (::new()) and before initialize() is called, these two
440-
// fields will be equal _but all_rules will be none_. This is OK for this function, but is
441-
// a possible future foot-gun. ConfigBuilder uses this to re-build its rules list in
442-
// ::build(). If cache is created but never made stale (by changing plugins),
443-
// ConfigBuilder's rule list won't need updating anyways, meaning its sound for this to
444-
// return `false`.
445-
self.last_fresh_plugins != self.plugins
446-
}
447-
448-
#[must_use]
449-
fn borrow(&self) -> Ref<'_, Vec<RuleEnum>> {
450-
let cached = self.all_rules.borrow();
451-
if cached.is_some() {
452-
Ref::map(cached, |cached| cached.as_ref().unwrap())
453-
} else {
454-
drop(cached);
455-
self.initialize();
456-
Ref::map(self.all_rules.borrow(), |cached| cached.as_ref().unwrap())
457-
}
458-
}
459-
460-
/// # Panics
461-
/// If the cache cell is currently borrowed.
462-
fn clear(&self) {
463-
*self.all_rules.borrow_mut() = None;
464-
}
465-
466-
/// Forcefully initialize this cache with all rules in all plugins currently
467-
/// enabled.
468-
///
469-
/// This will clobber whatever value is currently stored. It should only be
470-
/// called when the cache is not populated, either because it has not been
471-
/// initialized yet or it was cleared with [`Self::clear`].
472-
///
473-
/// # Panics
474-
/// If the cache cell is currently borrowed.
475-
fn initialize(&self) {
476-
debug_assert!(
477-
self.all_rules.borrow().is_none(),
478-
"Cannot re-initialize a populated rules cache. It must be cleared first."
479-
);
480-
481-
let all_rules: Vec<_> = if self.plugins.is_all() {
482-
RULES.clone()
483-
} else {
484-
let mut plugins = self.plugins;
485-
486-
// we need to include some jest rules when vitest is enabled, see [`VITEST_COMPATIBLE_JEST_RULES`]
487-
if plugins.contains(LintPlugins::VITEST) {
488-
plugins = plugins.union(LintPlugins::JEST);
489-
}
490-
491-
RULES
492-
.iter()
493-
.filter(|rule| plugins.contains(LintPlugins::from(rule.plugin_name())))
494-
.cloned()
495-
.collect()
496-
};
497-
498-
*self.all_rules.borrow_mut() = Some(all_rules);
499-
}
500-
}
501-
502423
#[cfg(test)]
503424
mod test {
504425
use std::path::PathBuf;

crates/oxc_linter/src/tester.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ impl Tester {
510510
ConfigStoreBuilder::from_oxlintrc(true, Oxlintrc::deserialize(v).unwrap())
511511
.unwrap()
512512
})
513-
.with_plugins(self.plugins)
513+
.with_plugins(self.plugins.union(LintPlugins::from(self.plugin_name)))
514514
.with_rule(rule, AllowWarnDeny::Warn)
515515
.build(),
516516
FxHashMap::default(),

0 commit comments

Comments
 (0)