Skip to content

Commit c11f20f

Browse files
committed
style: clippy fixes
1 parent 3c0c709 commit c11f20f

File tree

423 files changed

+2015
-2208
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

423 files changed

+2015
-2208
lines changed

crates/oxc_diagnostics/src/lib.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,11 @@ impl Diagnostic for OxcDiagnostic {
100100
.map(Box::new)
101101
.map(|b| b as Box<dyn Iterator<Item = LabeledSpan>>)
102102
}
103+
104+
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
105+
// self.code.is_some().then(|| Box::new(&self.code) as Box<dyn Display>)
106+
None
107+
}
103108
}
104109

105110
impl OxcDiagnostic {
@@ -138,21 +143,29 @@ impl OxcDiagnostic {
138143

139144
#[inline]
140145
pub fn with_error_code_scope<T: Into<Cow<'static, str>>>(mut self, code_scope: T) -> Self {
141-
self.inner.code.scope = Some(code_scope.into());
146+
self.inner.code.scope = match self.inner.code.scope {
147+
Some(scope) => Some(scope),
148+
None => Some(code_scope.into()),
149+
};
142150
debug_assert!(
143151
self.inner.code.scope.as_ref().is_some_and(|s| !s.is_empty()),
144152
"Error code scopes cannot be empty"
145153
);
154+
146155
self
147156
}
148157

149158
#[inline]
150159
pub fn with_error_code_num<T: Into<Cow<'static, str>>>(mut self, code_num: T) -> Self {
151-
self.inner.code.number = Some(code_num.into());
160+
self.inner.code.number = match self.inner.code.number {
161+
Some(num) => Some(num),
162+
None => Some(code_num.into()),
163+
};
152164
debug_assert!(
153165
self.inner.code.number.as_ref().is_some_and(|n| !n.is_empty()),
154166
"Error code numbers cannot be empty"
155167
);
168+
156169
self
157170
}
158171

crates/oxc_language_server/src/linter.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use oxc_linter::{
1313
AstroPartialLoader, JavaScriptSource, SveltePartialLoader, VuePartialLoader,
1414
LINT_PARTIAL_LOADER_EXT,
1515
},
16-
FixKind, LintContext, Linter,
16+
FixKind, Linter,
1717
};
1818
use oxc_parser::Parser;
1919
use oxc_semantic::SemanticBuilder;
@@ -304,12 +304,7 @@ impl IsolatedLintHandler {
304304
return Some(Self::wrap_diagnostics(path, &original_source_text, reports, start));
305305
};
306306

307-
let lint_ctx = LintContext::new(
308-
path.to_path_buf().into_boxed_path(),
309-
Rc::new(semantic_ret.semantic),
310-
);
311-
312-
let result = linter.run(lint_ctx);
307+
let result = linter.run(path, Rc::new(semantic_ret.semantic));
313308

314309
let reports = result
315310
.into_iter()

crates/oxc_linter/src/context.rs

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ use crate::{
1212
disable_directives::{DisableDirectives, DisableDirectivesBuilder},
1313
fixer::{FixKind, Message, RuleFix, RuleFixer},
1414
javascript_globals::GLOBALS,
15-
AllowWarnDeny, OxlintConfig, OxlintEnv, OxlintGlobals, OxlintSettings,
15+
AllowWarnDeny, FrameworkFlags, OxlintConfig, OxlintEnv, OxlintGlobals, OxlintSettings,
1616
};
1717

1818
#[derive(Clone)]
19+
#[must_use]
1920
pub struct LintContext<'a> {
2021
semantic: Rc<Semantic<'a>>,
2122

@@ -38,6 +39,7 @@ pub struct LintContext<'a> {
3839
eslint_config: Arc<OxlintConfig>,
3940

4041
// states
42+
current_plugin_prefix: &'static str,
4143
current_rule_name: &'static str,
4244

4345
/// Current rule severity. Allows for user severity overrides, e.g.
@@ -50,6 +52,7 @@ pub struct LintContext<'a> {
5052
/// }
5153
/// ```
5254
severity: Severity,
55+
frameworks: FrameworkFlags,
5356
}
5457

5558
impl<'a> LintContext<'a> {
@@ -74,36 +77,51 @@ impl<'a> LintContext<'a> {
7477
fix: FixKind::None,
7578
file_path: file_path.into(),
7679
eslint_config: Arc::new(OxlintConfig::default()),
80+
current_plugin_prefix: "eslint",
7781
current_rule_name: "",
7882
severity: Severity::Warning,
83+
frameworks: FrameworkFlags::empty(),
7984
}
8085
}
8186

8287
/// Enable/disable automatic code fixes.
83-
#[must_use]
8488
pub fn with_fix(mut self, fix: FixKind) -> Self {
8589
self.fix = fix;
8690
self
8791
}
8892

89-
#[must_use]
9093
pub fn with_eslint_config(mut self, eslint_config: &Arc<OxlintConfig>) -> Self {
9194
self.eslint_config = Arc::clone(eslint_config);
9295
self
9396
}
9497

95-
#[must_use]
98+
pub fn with_plugin_name(mut self, plugin: &'static str) -> Self {
99+
self.current_plugin_prefix = plugin_name_to_prefix(plugin);
100+
self
101+
}
102+
96103
pub fn with_rule_name(mut self, name: &'static str) -> Self {
97104
self.current_rule_name = name;
98105
self
99106
}
100107

101-
#[must_use]
102108
pub fn with_severity(mut self, severity: AllowWarnDeny) -> Self {
103109
self.severity = Severity::from(severity);
104110
self
105111
}
106112

113+
/// Set [`FrameworkFlags`], overwriting any existing flags.
114+
pub fn with_frameworks(mut self, frameworks: FrameworkFlags) -> Self {
115+
self.frameworks = frameworks;
116+
self
117+
}
118+
119+
/// Add additional [`FrameworkFlags`]
120+
pub fn and_frameworks(mut self, frameworks: FrameworkFlags) -> Self {
121+
self.frameworks |= frameworks;
122+
self
123+
}
124+
107125
pub fn semantic(&self) -> &Rc<Semantic<'a>> {
108126
&self.semantic
109127
}
@@ -182,6 +200,8 @@ impl<'a> LintContext<'a> {
182200
fn add_diagnostic(&self, message: Message<'a>) {
183201
if !self.disable_directives.contains(self.current_rule_name, message.span()) {
184202
let mut message = message;
203+
message.error =
204+
message.error.with_error_code(self.current_plugin_prefix, self.current_rule_name);
185205
if message.error.severity != self.severity {
186206
message.error = message.error.with_severity(self.severity);
187207
}
@@ -306,6 +326,10 @@ impl<'a> LintContext<'a> {
306326
}
307327
}
308328

329+
pub fn frameworks(&self) -> FrameworkFlags {
330+
self.frameworks
331+
}
332+
309333
/// AST nodes
310334
///
311335
/// Shorthand for `self.semantic().nodes()`.
@@ -340,4 +364,34 @@ impl<'a> LintContext<'a> {
340364
pub fn jsdoc(&self) -> &JSDocFinder<'a> {
341365
self.semantic().jsdoc()
342366
}
367+
368+
// #[inline]
369+
// fn plugin_name_to_prefix(&self, plugin_name: &'static str) -> &'static str {
370+
// let plugin_name = if self. plugin_name == "jest" && self.frameworks.contains(FrameworkFlags::Vitest) {
371+
// "vitest"
372+
// } else {
373+
// plugin_name
374+
// };
375+
// PLUGIN_PREFIXES.get(plugin_name).copied().unwrap_or(plugin_name)
376+
// }
343377
}
378+
379+
#[inline]
380+
fn plugin_name_to_prefix(plugin_name: &'static str) -> &'static str {
381+
PLUGIN_PREFIXES.get(plugin_name).copied().unwrap_or(plugin_name)
382+
}
383+
384+
const PLUGIN_PREFIXES: phf::Map<&'static str, &'static str> = phf::phf_map! {
385+
"import" => "eslint-plugin-import",
386+
"jest" => "eslint-plugin-jest",
387+
"jsdoc" => "eslint-plugin-jsdoc",
388+
"jsx_a11y" => "eslint-plugin-jsx-a11y",
389+
"nextjs" => "eslint-plugin-next",
390+
"promise" => "eslint-plugin-promise",
391+
"react_perf" => "eslint-plugin-react-perf",
392+
"react" => "eslint-plugin-react",
393+
"tree_shaking" => "eslint-plugin-tree-shaking",
394+
"typescript" => "typescript-eslint",
395+
"unicorn" => "eslint-plugin-unicorn",
396+
"vitest" => "eslint-plugin-vitest",
397+
};
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
use bitflags::bitflags;
2+
use oxc_semantic::ModuleRecord;
3+
use std::{hash, path::Path};
4+
5+
bitflags! {
6+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7+
pub struct FrameworkFlags: u32 {
8+
// front-end frameworks
9+
10+
/// Uses [React](https://reactjs.org/).
11+
///
12+
/// May be part of a meta-framework like Next.js.
13+
const React = 1 << 0;
14+
/// Uses [Preact](https://preactjs.com/).
15+
const Preact = 1 << 1;
16+
/// Uses [Next.js](https://nextjs.org/).
17+
const NextOnly = 1 << 2;
18+
const Next = Self::NextOnly.bits() | Self::React.bits();
19+
const JsxLike = Self::React.bits() | Self::Preact.bits() | Self::Next.bits();
20+
21+
const Vue = 1 << 3;
22+
const NuxtOnly = 1 << 4;
23+
const Nuxt = Self::NuxtOnly.bits() | Self::Vue.bits();
24+
25+
const Angular = 1 << 5;
26+
27+
const Svelte = 1 << 6;
28+
const SvelteKitOnly = 1 << 7;
29+
const SvelteKit = Self::SvelteKitOnly.bits() | Self::Svelte.bits();
30+
31+
const Astro = 1 << 8;
32+
33+
// Testing frameworks
34+
const Jest = 1 << 9;
35+
const Vitest = 1 << 10;
36+
const OtherTest = 1 << 11;
37+
const Test = Self::Jest.bits() | Self::Vitest.bits() | Self::OtherTest.bits();
38+
}
39+
}
40+
41+
impl Default for FrameworkFlags {
42+
#[inline]
43+
fn default() -> Self {
44+
Self::empty()
45+
}
46+
}
47+
impl hash::Hash for FrameworkFlags {
48+
#[inline]
49+
fn hash<H: hash::Hasher>(&self, state: &mut H) {
50+
state.write_u32(self.bits());
51+
}
52+
}
53+
54+
impl FrameworkFlags {
55+
#[inline]
56+
pub const fn is_test(self) -> bool {
57+
self.intersects(Self::Test)
58+
}
59+
60+
#[inline]
61+
pub const fn is_vitest(self) -> bool {
62+
self.contains(Self::Vitest)
63+
}
64+
}
65+
66+
/// <https://jestjs.io/docs/configuration#testmatch-arraystring>
67+
pub(crate) fn is_jestlike_file(path: &Path) -> bool {
68+
use std::ffi::OsStr;
69+
70+
if path.components().any(|c| match c {
71+
std::path::Component::Normal(p) => p == OsStr::new("__tests__"),
72+
_ => false,
73+
}) {
74+
return true;
75+
}
76+
77+
path.file_name() // foo/bar/baz.test.ts -> baz.test.ts
78+
.and_then(OsStr::to_str)
79+
.and_then(|filename| filename.split('.').rev().nth(1)) // baz.test.ts -> test
80+
.is_some_and(|name_or_first_ext| name_or_first_ext == "test" || name_or_first_ext == "spec")
81+
}
82+
83+
pub(crate) fn has_vitest_imports(module_record: &ModuleRecord) -> bool {
84+
module_record.import_entries.iter().any(|entry| entry.module_request.name() == "vitest")
85+
}

crates/oxc_linter/src/lib.rs

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod config;
88
mod context;
99
mod disable_directives;
1010
mod fixer;
11+
mod frameworks;
1112
mod globals;
1213
mod javascript_globals;
1314
mod options;
@@ -19,15 +20,16 @@ mod utils;
1920
pub mod partial_loader;
2021
pub mod table;
2122

22-
use std::{io::Write, rc::Rc, sync::Arc};
23+
use std::{io::Write, path::Path, rc::Rc, sync::Arc};
2324

2425
use oxc_diagnostics::Error;
25-
use oxc_semantic::AstNode;
26+
use oxc_semantic::{AstNode, Semantic};
2627

2728
pub use crate::{
2829
config::OxlintConfig,
2930
context::LintContext,
3031
fixer::FixKind,
32+
frameworks::FrameworkFlags,
3133
options::{AllowWarnDeny, LintOptions},
3234
rule::{RuleCategory, RuleMeta, RuleWithSeverity},
3335
service::{LintService, LintServiceOptions},
@@ -109,15 +111,26 @@ impl Linter {
109111
self.rules.len()
110112
}
111113

112-
pub fn run<'a>(&self, ctx: LintContext<'a>) -> Vec<Message<'a>> {
114+
// pub fn run<'a>(&self, ctx: LintContext<'a>) -> Vec<Message<'a>> {
115+
pub fn run<'a>(&self, path: &Path, semantic: Rc<Semantic<'a>>) -> Vec<Message<'a>> {
116+
let ctx = self.create_ctx(path, semantic);
113117
let semantic = Rc::clone(ctx.semantic());
114118

115119
let ctx = ctx.with_fix(self.options.fix).with_eslint_config(&self.eslint_config);
116120
let rules = self
117121
.rules
118122
.iter()
119123
.map(|rule| {
120-
(rule, ctx.clone().with_rule_name(rule.name()).with_severity(rule.severity))
124+
let rule_name = rule.name();
125+
let plugin_name = self.map_jest(rule.plugin_name(), rule_name);
126+
127+
(
128+
rule,
129+
ctx.clone()
130+
.with_plugin_name(plugin_name)
131+
.with_rule_name(rule_name)
132+
.with_severity(rule.severity),
133+
)
121134
})
122135
.collect::<Vec<_>>();
123136

@@ -149,6 +162,40 @@ impl Linter {
149162
writeln!(writer, "Default: {}", table.turned_on_by_default_count).unwrap();
150163
writeln!(writer, "Total: {}", table.total).unwrap();
151164
}
165+
166+
fn create_ctx<'a>(&self, path: &Path, semantic: Rc<Semantic<'a>>) -> LintContext<'a> {
167+
let mut ctx = LintContext::new(path.to_path_buf().into_boxed_path(), semantic)
168+
.with_fix(self.options.fix)
169+
.with_eslint_config(&self.eslint_config)
170+
.with_frameworks(self.options.framework_hints);
171+
172+
// set file-specific jest/vitest flags
173+
if self.options.jest_plugin || self.options.vitest_plugin {
174+
let mut test_flags = FrameworkFlags::empty();
175+
176+
if frameworks::is_jestlike_file(path) {
177+
test_flags.set(FrameworkFlags::Jest, self.options.jest_plugin);
178+
test_flags.set(FrameworkFlags::Vitest, self.options.vitest_plugin);
179+
} else if frameworks::has_vitest_imports(ctx.module_record()) {
180+
test_flags.set(FrameworkFlags::Vitest, true);
181+
}
182+
183+
ctx = ctx.and_frameworks(test_flags);
184+
}
185+
186+
ctx
187+
}
188+
189+
fn map_jest(&self, plugin_name: &'static str, rule_name: &str) -> &'static str {
190+
if self.options.vitest_plugin
191+
&& plugin_name == "jest"
192+
&& utils::is_jest_rule_adapted_to_vitest(rule_name)
193+
{
194+
"vitest"
195+
} else {
196+
plugin_name
197+
}
198+
}
152199
}
153200

154201
#[cfg(test)]

0 commit comments

Comments
 (0)