perf(linter): move shared context info to ContextHost (#5795)

> Related to #5770

## What This PR Does

Moves state that is constant over a linted file out of `LintContext` and into a shared `ContextHost` struct, turning `LintContext` into a [flyweight](https://en.wikipedia.org/wiki/Flyweight_pattern).

This brings `LintContext` from 144 bytes down to 96. `Linter::run` iterates over `(rule, ctx)` pairs in a very tight loop, and each rule instance gets its own clone of `ctx`. A smaller `LintContext` means better cache locality and a smaller heap allocation for this vec. I'm hoping to eventually get it small enough to fit in a single cache line.

I made a PR a while ago that did something similar to this one, but instead of using an `Rc`, each `LintContext` stored a read-only reference. This added an extra lifetime parameter, making it slightly unwieldy, but I saw up to 15%/25% perf improvements on local benchmarks. I'll dig around for it and add a link shortly.

### Architecture
![image](https://github.com/user-attachments/assets/9e8352ae-a581-46a3-a578-9eb855d4ebaf)
----
![image](https://github.com/user-attachments/assets/49213cd9-3c31-40dc-97ad-ddf010705ab6)
This commit is contained in:
DonIsaac 2024-09-16 04:22:07 +00:00
parent 17cd9038b7
commit e413cad0d7
67 changed files with 574 additions and 274 deletions

View file

@ -0,0 +1,177 @@
use oxc_semantic::Semantic;
use oxc_span::SourceType;
use std::{cell::RefCell, path::Path, rc::Rc, sync::Arc};
use crate::{
config::LintConfig,
disable_directives::{DisableDirectives, DisableDirectivesBuilder},
fixer::FixKind,
frameworks,
options::{LintOptions, LintPlugins},
utils, FrameworkFlags, RuleWithSeverity,
};
use super::{plugin_name_to_prefix, LintContext};
/// Stores shared information about a file being linted.
///
/// When linting a file, there are a number of shared resources that are
/// independent of the rule being linted. [`ContextHost`] stores this context
/// subset. When a lint rule is run, a [`LintContext`] with rule-specific
/// information is spawned using [`ContextHost::spawn`].
///
/// ## API Encapsulation
///
/// In most cases, lint rules should be interacting with a [`LintContext`]. The
/// only current exception to this is
/// [should_run](`crate::rule::Rule::should_run`). Just before a file is linted,
/// rules are filtered out based on that method. Creating a [`LintContext`] for
/// rules that would never get run is a waste of resources, so they must use
/// [`ContextHost`].
///
/// ## References
/// - [Flyweight Pattern](https://en.wikipedia.org/wiki/Flyweight_pattern)
#[must_use]
#[non_exhaustive]
pub struct ContextHost<'a> {
pub(super) semantic: Rc<Semantic<'a>>,
pub(super) disable_directives: DisableDirectives<'a>,
/// Whether or not to apply code fixes during linting. Defaults to
/// [`FixKind::None`] (no fixing).
///
/// Set via the `--fix`, `--fix-suggestions`, and `--fix-dangerously` CLI
/// flags.
pub(super) fix: FixKind,
pub(super) file_path: Box<Path>,
pub(super) config: Arc<LintConfig>,
pub(super) frameworks: FrameworkFlags,
pub(super) plugins: LintPlugins,
}
impl<'a> ContextHost<'a> {
/// # Panics
/// If `semantic.cfg()` is `None`.
pub fn new<P: AsRef<Path>>(
file_path: P,
semantic: Rc<Semantic<'a>>,
options: LintOptions,
) -> Self {
// We should always check for `semantic.cfg()` being `Some` since we depend on it and it is
// unwrapped without any runtime checks after construction.
assert!(
semantic.cfg().is_some(),
"`LintContext` depends on `Semantic::cfg`, Build your semantic with cfg enabled(`SemanticBuilder::with_cfg`)."
);
let disable_directives =
DisableDirectivesBuilder::new(semantic.source_text(), semantic.trivias().clone())
.build();
let file_path = file_path.as_ref().to_path_buf().into_boxed_path();
Self {
semantic,
disable_directives,
fix: options.fix,
file_path,
config: Arc::new(LintConfig::default()),
frameworks: options.framework_hints,
plugins: options.plugins,
}
.sniff_for_frameworks()
}
#[inline]
pub(crate) fn with_config(mut self, config: &Arc<LintConfig>) -> Self {
self.config = Arc::clone(config);
self
}
#[inline]
pub fn semantic(&self) -> &Semantic<'a> {
&self.semantic
}
/// Path to the file being linted.
///
/// When created from a [`LintService`](`crate::service::LintService`), this
/// will be an absolute path.
#[inline]
pub fn file_path(&self) -> &Path {
&self.file_path
}
/// The source type of the file being linted, e.g. JavaScript, TypeScript,
/// CJS, ESM, etc.
#[inline]
pub fn source_type(&self) -> &SourceType {
self.semantic.source_type()
}
pub(crate) fn spawn(self: Rc<Self>, rule: &RuleWithSeverity) -> LintContext<'a> {
const DIAGNOSTICS_INITIAL_CAPACITY: usize = 128;
let rule_name = rule.name();
let plugin_name = self.map_jest(rule.plugin_name(), rule_name);
LintContext {
parent: self,
diagnostics: RefCell::new(Vec::with_capacity(DIAGNOSTICS_INITIAL_CAPACITY)),
current_rule_name: rule_name,
current_plugin_name: plugin_name,
current_plugin_prefix: plugin_name_to_prefix(plugin_name),
#[cfg(debug_assertions)]
current_rule_fix_capabilities: rule.rule.fix(),
severity: rule.severity.into(),
}
}
#[cfg(test)]
pub(crate) fn spawn_for_test(self: Rc<Self>) -> LintContext<'a> {
const DIAGNOSTICS_INITIAL_CAPACITY: usize = 128;
LintContext {
parent: Rc::clone(&self),
diagnostics: RefCell::new(Vec::with_capacity(DIAGNOSTICS_INITIAL_CAPACITY)),
current_rule_name: "",
current_plugin_name: "eslint",
current_plugin_prefix: "eslint",
#[cfg(debug_assertions)]
current_rule_fix_capabilities: crate::rule::RuleFixMeta::None,
severity: oxc_diagnostics::Severity::Warning,
}
}
fn map_jest(&self, plugin_name: &'static str, rule_name: &str) -> &'static str {
if self.plugins.has_vitest()
&& plugin_name == "jest"
&& utils::is_jest_rule_adapted_to_vitest(rule_name)
{
"vitest"
} else {
plugin_name
}
}
/// Inspect the target file for clues about what frameworks are being used.
/// Should only be called once immediately after construction.
///
/// Before invocation, `self.frameworks` contains hints obtained at the
/// project level. For example, Oxlint may (eventually) search for a
/// `package.json`` and look for relevant dependencies. This method builds
/// on top of those hints, providing a more granular understanding of the
/// frameworks in use.
fn sniff_for_frameworks(mut self) -> Self {
if self.plugins.has_test() {
// let mut test_flags = FrameworkFlags::empty();
let vitest_like = frameworks::has_vitest_imports(self.semantic.module_record());
let jest_like = frameworks::is_jestlike_file(&self.file_path)
|| frameworks::has_jest_imports(self.semantic.module_record());
self.frameworks.set(FrameworkFlags::Vitest, vitest_like);
self.frameworks.set(FrameworkFlags::Jest, jest_like);
}
self
}
}

View file

@ -1,5 +1,7 @@
#![allow(rustdoc::private_intra_doc_links)] // useful for intellisense
use std::{cell::RefCell, path::Path, rc::Rc, sync::Arc};
mod host;
use std::{cell::RefCell, path::Path, rc::Rc};
use oxc_cfg::ControlFlowGraph;
use oxc_diagnostics::{OxcDiagnostic, Severity};
@ -10,36 +12,25 @@ use oxc_syntax::module_record::ModuleRecord;
#[cfg(debug_assertions)]
use crate::rule::RuleFixMeta;
use crate::{
config::LintConfig,
disable_directives::{DisableDirectives, DisableDirectivesBuilder},
disable_directives::DisableDirectives,
fixer::{FixKind, Message, RuleFix, RuleFixer},
javascript_globals::GLOBALS,
AllowWarnDeny, FrameworkFlags, OxlintEnv, OxlintGlobals, OxlintSettings,
};
pub use host::ContextHost;
#[derive(Clone)]
#[must_use]
pub struct LintContext<'a> {
semantic: Rc<Semantic<'a>>,
/// Shared context independent of the rule being linted.
parent: Rc<ContextHost<'a>>,
/// Diagnostics reported by the linter.
///
/// Contains diagnostics for all rules across all files.
diagnostics: RefCell<Vec<Message<'a>>>,
disable_directives: Rc<DisableDirectives<'a>>,
/// Whether or not to apply code fixes during linting. Defaults to
/// [`FixKind::None`] (no fixing).
///
/// Set via the `--fix`, `--fix-suggestions`, and `--fix-dangerously` CLI
/// flags.
fix: FixKind,
file_path: Rc<Path>,
config: Arc<LintConfig>,
// states
current_plugin_name: &'static str,
current_plugin_prefix: &'static str,
@ -57,54 +48,11 @@ pub struct LintContext<'a> {
/// }
/// ```
severity: Severity,
frameworks: FrameworkFlags,
}
impl<'a> LintContext<'a> {
const WEBSITE_BASE_URL: &'static str = "https://oxc.rs/docs/guide/usage/linter/rules";
/// # Panics
/// If `semantic.cfg()` is `None`.
pub fn new(file_path: Box<Path>, semantic: Rc<Semantic<'a>>) -> Self {
const DIAGNOSTICS_INITIAL_CAPACITY: usize = 128;
// We should always check for `semantic.cfg()` being `Some` since we depend on it and it is
// unwrapped without any runtime checks after construction.
assert!(
semantic.cfg().is_some(),
"`LintContext` depends on `Semantic::cfg`, Build your semantic with cfg enabled(`SemanticBuilder::with_cfg`)."
);
let disable_directives =
DisableDirectivesBuilder::new(semantic.source_text(), semantic.trivias().clone())
.build();
Self {
semantic,
diagnostics: RefCell::new(Vec::with_capacity(DIAGNOSTICS_INITIAL_CAPACITY)),
disable_directives: Rc::new(disable_directives),
fix: FixKind::None,
file_path: file_path.into(),
config: Arc::new(LintConfig::default()),
current_plugin_name: "eslint",
current_plugin_prefix: "eslint",
current_rule_name: "",
#[cfg(debug_assertions)]
current_rule_fix_capabilities: RuleFixMeta::None,
severity: Severity::Warning,
frameworks: FrameworkFlags::empty(),
}
}
/// Enable/disable automatic code fixes.
pub fn with_fix(mut self, fix: FixKind) -> Self {
self.fix = fix;
self
}
pub(crate) fn with_config(mut self, config: &Arc<LintConfig>) -> Self {
self.config = Arc::clone(config);
self
}
pub fn with_plugin_name(mut self, plugin: &'static str) -> Self {
self.current_plugin_name = plugin;
self.current_plugin_prefix = plugin_name_to_prefix(plugin);
@ -122,38 +70,37 @@ impl<'a> LintContext<'a> {
self
}
/// Update the severity of diagnostics reported by the rule this context is
/// associated with.
#[inline]
pub fn with_severity(mut self, severity: AllowWarnDeny) -> Self {
self.severity = Severity::from(severity);
self
}
/// Set [`FrameworkFlags`], overwriting any existing flags.
pub fn with_frameworks(mut self, frameworks: FrameworkFlags) -> Self {
self.frameworks = frameworks;
self
}
/// Add additional [`FrameworkFlags`]
pub fn and_frameworks(mut self, frameworks: FrameworkFlags) -> Self {
self.frameworks |= frameworks;
self
}
/// Get information such as the control flow graph, bound symbols, AST, etc.
/// for the file being linted.
///
/// Refer to [`Semantic`]'s documentation for more information.
#[inline]
pub fn semantic(&self) -> &Rc<Semantic<'a>> {
&self.semantic
&self.parent.semantic
}
#[inline]
pub fn cfg(&self) -> &ControlFlowGraph {
// SAFETY: `LintContext::new` is the only way to construct a `LintContext` and we always
// assert the existence of control flow so it should always be `Some`.
unsafe { self.semantic().cfg().unwrap_unchecked() }
}
#[inline]
pub fn disable_directives(&self) -> &DisableDirectives<'a> {
&self.disable_directives
&self.parent.disable_directives
}
/// Source code of the file being linted.
#[inline]
pub fn source_text(&self) -> &'a str {
self.semantic().source_text()
}
@ -165,29 +112,34 @@ impl<'a> LintContext<'a> {
}
/// [`SourceType`] of the file currently being linted.
#[inline]
pub fn source_type(&self) -> &SourceType {
self.semantic().source_type()
}
/// Path to the file currently being linted.
#[inline]
pub fn file_path(&self) -> &Path {
&self.file_path
&self.parent.file_path
}
/// Plugin settings
#[inline]
pub fn settings(&self) -> &OxlintSettings {
&self.config.settings
&self.parent.config.settings
}
#[inline]
pub fn globals(&self) -> &OxlintGlobals {
&self.config.globals
&self.parent.config.globals
}
/// Runtime environments turned on/off by the user.
///
/// Examples of environments are `builtin`, `browser`, `node`, etc.
#[inline]
pub fn env(&self) -> &OxlintEnv {
&self.config.env
&self.parent.config.env
}
pub fn env_contains_var(&self, var: &str) -> bool {
@ -211,7 +163,7 @@ impl<'a> LintContext<'a> {
}
fn add_diagnostic(&self, mut message: Message<'a>) {
if self.disable_directives.contains(self.current_rule_name, message.span()) {
if self.parent.disable_directives.contains(self.current_rule_name, message.span()) {
return;
}
message.error = message
@ -334,7 +286,7 @@ impl<'a> LintContext<'a> {
(Some(message), None) => diagnostic.with_help(message.to_owned()),
_ => diagnostic,
};
if self.fix.can_apply(rule_fix.kind()) {
if self.parent.fix.can_apply(rule_fix.kind()) {
let fix = rule_fix.into_fix(self.source_text());
self.add_diagnostic(Message::new(diagnostic, Some(fix)));
} else {
@ -343,7 +295,7 @@ impl<'a> LintContext<'a> {
}
pub fn frameworks(&self) -> FrameworkFlags {
self.frameworks
self.parent.frameworks
}
/// AST nodes
@ -380,16 +332,6 @@ impl<'a> LintContext<'a> {
pub fn jsdoc(&self) -> &JSDocFinder<'a> {
self.semantic().jsdoc()
}
// #[inline]
// fn plugin_name_to_prefix(&self, plugin_name: &'static str) -> &'static str {
// let plugin_name = if self. plugin_name == "jest" && self.frameworks.contains(FrameworkFlags::Vitest) {
// "vitest"
// } else {
// plugin_name
// };
// PLUGIN_PREFIXES.get(plugin_name).copied().unwrap_or(plugin_name)
// }
}
#[inline]

View file

@ -23,6 +23,7 @@ pub mod table;
use std::{io::Write, path::Path, rc::Rc, sync::Arc};
use config::LintConfig;
use context::ContextHost;
use options::LintOptions;
use oxc_diagnostics::Error;
use oxc_semantic::{AstNode, Semantic};
@ -113,22 +114,22 @@ impl Linter {
self.rules.len()
}
// pub fn run<'a>(&self, ctx: LintContext<'a>) -> Vec<Message<'a>> {
pub fn run<'a>(&self, path: &Path, semantic: Rc<Semantic<'a>>) -> Vec<Message<'a>> {
let ctx = self.create_ctx(path, semantic);
let semantic = Rc::clone(ctx.semantic());
let ctx_host =
Rc::new(ContextHost::new(path, semantic, self.options).with_config(&self.config));
let rules = self
.rules
.iter()
.filter(|rule| rule.should_run(&ctx))
.map(|rule| (rule, self.ctx_for_rule(&ctx, rule)))
.filter(|rule| rule.should_run(&ctx_host))
.map(|rule| (rule, Rc::clone(&ctx_host).spawn(rule)))
.collect::<Vec<_>>();
for (rule, ctx) in &rules {
rule.run_once(ctx);
}
let semantic = ctx_host.semantic();
for symbol in semantic.symbols().symbol_ids() {
for (rule, ctx) in &rules {
rule.run_on_symbol(symbol, ctx);
@ -153,53 +154,6 @@ impl Linter {
writeln!(writer, "Default: {}", table.turned_on_by_default_count).unwrap();
writeln!(writer, "Total: {}", table.total).unwrap();
}
fn create_ctx<'a>(&self, path: &Path, semantic: Rc<Semantic<'a>>) -> LintContext<'a> {
let mut ctx = LintContext::new(path.to_path_buf().into_boxed_path(), semantic)
.with_fix(self.options.fix)
.with_config(&self.config)
.with_frameworks(self.options.framework_hints);
// set file-specific jest/vitest flags
if self.options.plugins.has_test() {
let mut test_flags = FrameworkFlags::empty();
if frameworks::has_vitest_imports(ctx.module_record()) {
test_flags.set(FrameworkFlags::Vitest, true);
} else if frameworks::is_jestlike_file(path)
|| frameworks::has_jest_imports(ctx.module_record())
{
test_flags.set(FrameworkFlags::Jest, true);
}
ctx = ctx.and_frameworks(test_flags);
}
ctx
}
fn ctx_for_rule<'a>(&self, ctx: &LintContext<'a>, rule: &RuleWithSeverity) -> LintContext<'a> {
let rule_name = rule.name();
let plugin_name = self.map_jest(rule.plugin_name(), rule_name);
#[cfg(debug_assertions)]
let ctx = ctx.clone().with_rule_fix_capabilities(rule.rule.fix());
#[cfg(not(debug_assertions))]
let ctx = ctx.clone();
ctx.with_plugin_name(plugin_name).with_rule_name(rule_name).with_severity(rule.severity)
}
fn map_jest(&self, plugin_name: &'static str, rule_name: &str) -> &'static str {
if self.options.plugins.has_vitest()
&& plugin_name == "jest"
&& utils::is_jest_rule_adapted_to_vitest(rule_name)
{
"vitest"
} else {
plugin_name
}
}
}
#[cfg(test)]

View file

@ -6,12 +6,11 @@ use std::{convert::From, path::PathBuf};
use filter::LintFilterKind;
use oxc_diagnostics::Error;
use plugins::LintPlugins;
use rustc_hash::FxHashSet;
pub use allow_warn_deny::AllowWarnDeny;
pub use filter::{InvalidFilterKind, LintFilter};
pub use plugins::LintPluginOptions;
pub use plugins::{LintPluginOptions, LintPlugins};
use crate::{
config::{LintConfig, OxlintConfig},
@ -26,7 +25,7 @@ use crate::{
/// outside of this crate.
///
/// [`Linter`]: crate::Linter
#[derive(Debug, Default)]
#[derive(Debug, Default, Clone, Copy)]
#[cfg_attr(test, derive(PartialEq))]
pub(crate) struct LintOptions {
pub fix: FixKind,

View file

@ -7,7 +7,10 @@ use std::{
use oxc_semantic::SymbolId;
use crate::{context::LintContext, AllowWarnDeny, AstNode, FixKind, RuleEnum};
use crate::{
context::{ContextHost, LintContext},
AllowWarnDeny, AstNode, FixKind, RuleEnum,
};
pub trait Rule: Sized + Default + fmt::Debug {
/// Initialize from eslint json configuration
@ -39,7 +42,7 @@ pub trait Rule: Sized + Default + fmt::Debug {
/// [`linter`]: crate::Linter
#[expect(unused_variables)]
#[inline]
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
true
}
}

View file

@ -18,7 +18,10 @@ use oxc_semantic::{AstNode, ScopeFlags, SymbolFlags, SymbolId};
use oxc_span::GetSpan;
use symbol::Symbol;
use crate::{context::LintContext, rule::Rule};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
};
#[derive(Debug, Default, Clone)]
pub struct NoUnusedVars(Box<NoUnusedVarsOptions>);
@ -210,7 +213,7 @@ impl Rule for NoUnusedVars {
self.run_on_symbol_internal(&symbol, ctx);
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
// ignore .d.ts and vue files.
// 1. declarations have side effects (they get merged together)
// 2. vue scripts declare variables that get used in the template, which

View file

@ -2,7 +2,7 @@ use oxc_diagnostics::{LabeledSpan, OxcDiagnostic};
use oxc_macros::declare_oxc_lint;
// use oxc_span::{CompactStr, Span};
use crate::{context::LintContext, rule::Rule};
use crate::{context::{LintContext, ContextHost}, rule::Rule};
// #[derive(Debug, Error, Diagnostic)]
// #[error("")]

View file

@ -1,7 +1,7 @@
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule};
use crate::{context::{LintContext, ContextHost}, rule::Rule};
fn no_exports_found(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("No exports found")

View file

@ -288,6 +288,32 @@ fn convert_pattern(pattern: &str) -> String {
format!("(?ui)^{pattern}(\\.|$)")
}
#[test]
fn debug() {
use crate::tester::Tester;
let mut pass: Vec<(&str, Option<serde_json::Value>)> = vec![];
let mut fail = vec![];
let pass_vitest = vec![(
"
import { test } from 'vitest';
test.skip(\"skipped test\", () => {})
",
None,
)];
let fail_vitest = vec![];
pass.extend(pass_vitest);
fail.extend(fail_vitest);
Tester::new(ExpectExpect::NAME, pass, fail)
.with_jest_plugin(true)
.with_vitest_plugin(true)
.test();
}
#[test]
fn test() {
use crate::tester::Tester;

View file

@ -7,7 +7,11 @@ use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use phf::phf_set;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn no_typos_diagnostic(typo: &str, suggestion: &str, span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(format!("{typo} may be a typo. Did you mean {suggestion}?"))
@ -47,7 +51,7 @@ const NEXTJS_DATA_FETCHING_FUNCTIONS: phf::Set<&'static str> = phf_set! {
const THRESHOLD: i32 = 1;
impl Rule for NoTypos {
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
let Some(path) = ctx.file_path().to_str() else {
return false;
};

View file

@ -10,7 +10,7 @@ use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use crate::{
context::LintContext,
context::{ContextHost, LintContext},
rule::Rule,
utils::{get_prop_value, has_jsx_prop_ignore_case, is_create_element_call},
AstNode,
@ -152,7 +152,7 @@ impl Rule for ButtonHasType {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -8,7 +8,12 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, utils::get_prop_value, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
utils::get_prop_value,
AstNode,
};
fn boolean_value_diagnostic(attr: &str, span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(format!("Value must be omitted for boolean attribute {attr:?}"))
@ -149,7 +154,7 @@ impl Rule for JsxBooleanValue {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -12,7 +12,11 @@ use oxc_semantic::NodeId;
use oxc_span::{GetSpan as _, Span};
use serde_json::Value;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn jsx_curly_brace_presence_unnecessary_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Curly braces are unnecessary here.").with_label(span)
@ -350,7 +354,7 @@ impl Rule for JsxCurlyBracePresence {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -7,7 +7,11 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn missing_key_prop_for_element_in_array(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(r#"Missing "key" prop for element in array."#).with_label(span)
@ -68,7 +72,7 @@ impl Rule for JsxKey {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -5,7 +5,11 @@ use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use regex::Regex;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn jsx_no_comment_textnodes_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Comments inside children section of tag should be placed inside braces")
@ -61,7 +65,7 @@ impl Rule for JsxNoCommentTextnodes {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -7,7 +7,11 @@ use oxc_macros::declare_oxc_lint;
use oxc_span::{Atom, Span};
use rustc_hash::FxHashMap;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn jsx_no_duplicate_props_diagnostic(x0: &str, span1: Span, span2: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(format!("No duplicate props allowed. The prop \"{x0}\" is duplicated."))
@ -72,7 +76,7 @@ impl Rule for JsxNoDuplicateProps {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -11,7 +11,11 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{CompactStr, GetSpan, Span};
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn target_blank_without_noreferrer(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Using target=`_blank` without rel=`noreferrer` (which implies rel=`noopener`) is a security risk in older browsers: see https://mathiasbynens.github.io/rel-noopener/#recommendations")
@ -241,7 +245,7 @@ impl Rule for JsxNoTargetBlank {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -6,7 +6,11 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn jsx_no_undef_diagnostic(ident_name: &str, span1: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Disallow undeclared variables in JSX")
@ -72,7 +76,7 @@ impl Rule for JsxNoUndef {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -10,7 +10,11 @@ use oxc_macros::declare_oxc_lint;
use oxc_semantic::NodeId;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn needs_more_children(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Fragments should contain more than one child.").with_label(span)
@ -78,7 +82,7 @@ impl Rule for JsxNoUselessFragment {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -6,7 +6,7 @@ use oxc_span::{Atom, Span};
use rustc_hash::FxHashMap;
use crate::{
context::LintContext,
context::{ContextHost, LintContext},
fixer::{Fix, RuleFix},
rule::Rule,
utils::is_same_member_expression,
@ -122,7 +122,7 @@ impl Rule for JsxPropsNoSpreadMulti {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -6,7 +6,12 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use crate::{context::LintContext, rule::Rule, utils::is_create_element_call, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
utils::is_create_element_call,
AstNode,
};
fn no_children_prop_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Avoid passing children using a prop.")
@ -86,7 +91,7 @@ impl Rule for NoChildrenProp {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -7,7 +7,7 @@ use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use crate::{
context::LintContext,
context::{ContextHost, LintContext},
rule::Rule,
utils::{has_jsx_prop, is_create_element_call},
AstNode,
@ -91,7 +91,7 @@ impl Rule for NoDanger {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -7,7 +7,7 @@ use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use crate::{
context::LintContext,
context::{ContextHost, LintContext},
rule::Rule,
utils::{is_es5_component, is_es6_component},
AstNode,
@ -118,7 +118,7 @@ impl Rule for NoDirectMutationState {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -3,7 +3,11 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn no_find_dom_node_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Unexpected call to `findDOMNode`.")
@ -67,7 +71,7 @@ impl Rule for NoFindDomNode {
ctx.diagnostic(no_find_dom_node_diagnostic(span));
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -3,7 +3,11 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn no_is_mounted_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Do not use isMounted")
@ -67,7 +71,7 @@ impl Rule for NoIsMounted {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -3,7 +3,11 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn no_render_return_value_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Do not depend on the return value from ReactDOM.render.")
@ -82,7 +86,7 @@ impl Rule for NoRenderReturnValue {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -3,7 +3,12 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use crate::{context::LintContext, rule::Rule, utils::get_parent_component, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
utils::get_parent_component,
AstNode,
};
fn no_set_state_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Do not use setState").with_label(span)
@ -66,7 +71,7 @@ impl Rule for NoSetState {
ctx.diagnostic(no_set_state_diagnostic(call_expr.callee.span()));
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -9,7 +9,12 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use crate::{context::LintContext, rule::Rule, utils::get_parent_component, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
utils::get_parent_component,
AstNode,
};
fn this_refs_deprecated(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Using this.refs is deprecated.")
@ -125,7 +130,7 @@ impl Rule for NoStringRefs {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -4,7 +4,11 @@ use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use phf::{phf_map, Map};
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn no_unescaped_entities_diagnostic(span: Span, unescaped: char, escaped: &str) -> OxcDiagnostic {
OxcDiagnostic::warn(format!("`{unescaped}` can be escaped with {escaped}")).with_label(span)
@ -62,7 +66,7 @@ impl Rule for NoUnescapedEntities {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -15,7 +15,12 @@ use regex::Regex;
use rustc_hash::FxHashSet;
use serde::Deserialize;
use crate::{context::LintContext, rule::Rule, utils::get_jsx_attribute_name, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
utils::get_jsx_attribute_name,
AstNode,
};
fn invalid_prop_on_tag(span: Span, prop: &str, tag: &str) -> OxcDiagnostic {
OxcDiagnostic::warn("Invalid property found")
@ -540,7 +545,7 @@ impl Rule for NoUnknownProperty {
});
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -4,7 +4,7 @@ use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use crate::{
context::LintContext,
context::{ContextHost, LintContext},
rule::Rule,
utils::{is_es5_component, is_es6_component},
AstNode,
@ -75,7 +75,7 @@ impl Rule for PreferEs6Class {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -3,7 +3,11 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn react_in_jsx_scope_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("'React' must be in scope when using JSX")
@ -59,7 +63,7 @@ impl Rule for ReactInJsxScope {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -8,7 +8,7 @@ use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use crate::{
context::LintContext,
context::{ContextHost, LintContext},
rule::Rule,
utils::{is_es5_component, is_es6_component},
AstNode,
@ -79,7 +79,7 @@ impl Rule for RequireRenderReturn {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -6,7 +6,12 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, globals::HTML_TAG, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
globals::HTML_TAG,
rule::Rule,
AstNode,
};
fn self_closing_comp_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Unnecessary closing tag")
@ -119,7 +124,7 @@ impl Rule for SelfClosingComp {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -10,7 +10,12 @@ use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use phf::phf_set;
use crate::{context::LintContext, rule::Rule, utils::is_create_element_call, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
utils::is_create_element_call,
AstNode,
};
fn void_dom_elements_no_children_diagnostic(tag: &str, span: Span) -> OxcDiagnostic {
// TODO: use imperative phrasing
@ -142,7 +147,7 @@ impl Rule for VoidDomElementsNoChildren {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -9,7 +9,11 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{CompactStr, GetSpan, Span};
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn adjacent_overload_signatures_diagnostic(
fn_name: &str,
@ -320,7 +324,7 @@ impl Rule for AdjacentOverloadSignatures {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -7,7 +7,10 @@ use oxc_macros::declare_oxc_lint;
use oxc_semantic::AstNode;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
};
#[derive(Debug, Default, Clone)]
pub struct ArrayType(Box<ArrayTypeConfig>);
@ -136,7 +139,7 @@ impl Rule for ArrayType {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -5,7 +5,10 @@ use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use regex::Regex;
use crate::{context::LintContext, rule::Rule};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
};
fn comment(ts_comment_name: &str, span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(format!(
@ -213,7 +216,7 @@ impl Rule for BanTsComment {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -4,7 +4,11 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn type_diagnostic(banned_type: &str, suggested_type: &str, span2: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(format!(
@ -88,7 +92,7 @@ impl Rule for BanTypes {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -6,7 +6,11 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn consistent_indexed_object_style_diagnostic(a: &str, b: &str, span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(format!("A {a} is preferred over an {b}."))
@ -245,7 +249,7 @@ impl Rule for ConsistentIndexedObjectStyle {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -6,7 +6,11 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn consistent_type_definitions_diagnostic(
preferred_type_kind: &str,
@ -220,7 +224,7 @@ impl Rule for ConsistentTypeDefinitions {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -14,7 +14,7 @@ use oxc_semantic::{Reference, SymbolId};
use oxc_span::{GetSpan, Span};
use crate::{
context::LintContext,
context::{ContextHost, LintContext},
fixer::{RuleFix, RuleFixer},
rule::Rule,
AstNode,
@ -268,7 +268,7 @@ impl Rule for ConsistentTypeImports {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -14,7 +14,7 @@ use oxc_syntax::operator::UnaryOperator;
use crate::{
ast_util::outermost_paren_parent,
context::LintContext,
context::{ContextHost, LintContext},
rule::Rule,
rules::eslint::array_callback_return::return_checker::{
check_statement, StatementReturnStatus,
@ -300,7 +300,7 @@ impl Rule for ExplicitFunctionReturnType {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -7,7 +7,11 @@ use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use oxc_syntax::operator::{AssignmentOperator, BinaryOperator};
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
#[derive(Debug, Default, Clone)]
pub struct NoConfusingNonNullAssertion;
@ -111,7 +115,7 @@ impl Rule for NoConfusingNonNullAssertion {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -4,7 +4,11 @@ use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use rustc_hash::FxHashMap;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn no_duplicate_enum_values_diagnostic(span: Span, span1: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Disallow duplicate enum member values")
@ -67,7 +71,7 @@ impl Rule for NoDuplicateEnumValues {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -4,7 +4,11 @@ use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use serde_json::Value;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn no_empty_interface_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("an empty interface is equivalent to `{}`").with_label(span)
@ -69,7 +73,7 @@ impl Rule for NoEmptyInterface {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -4,7 +4,11 @@ use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use serde_json::Value;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn no_explicit_any_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Unexpected any. Specify a different type.")
@ -114,7 +118,7 @@ impl Rule for NoExplicitAny {
Self { fix_to_unknown, ignore_rest_args }
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -3,7 +3,11 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn no_extra_non_null_assertion_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("extra non-null assertion").with_label(span)
@ -63,7 +67,7 @@ impl Rule for NoExtraNonNullAssertion {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -6,7 +6,12 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use crate::{context::LintContext, fixer::Fix, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
fixer::Fix,
rule::Rule,
AstNode,
};
fn no_import_type_side_effects_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("TypeScript will only remove the inline type specifiers which will leave behind a side effect import at runtime.")
@ -117,7 +122,7 @@ impl Rule for NoImportTypeSideEffects {
);
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -6,7 +6,11 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn no_namespace_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("ES2015 module syntax is preferred over namespaces.")
@ -99,7 +103,7 @@ impl Rule for NoNamespace {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -4,7 +4,11 @@ use oxc_macros::declare_oxc_lint;
use oxc_semantic::SymbolId;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
#[derive(Debug, Default, Clone)]
pub struct NoNonNullAssertedNullishCoalescing;
@ -48,7 +52,7 @@ impl Rule for NoNonNullAssertedNullishCoalescing {
ctx.diagnostic(no_non_null_asserted_nullish_coalescing_diagnostic(ts_non_null_expr.span));
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -6,7 +6,11 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn no_non_null_asserted_optional_chain_diagnostic(span: Span, span1: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("non-null assertions after an optional chain expression")
@ -91,7 +95,7 @@ impl Rule for NoNonNullAssertedOptionalChain {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -3,7 +3,11 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
#[derive(Debug, Default, Clone)]
pub struct NoNonNullAssertion;
@ -37,7 +41,7 @@ impl Rule for NoNonNullAssertion {
ctx.diagnostic(no_non_null_assertion_diagnostic(expr.span));
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -8,7 +8,11 @@ use oxc_span::{CompactStr, GetSpan, Span};
use rustc_hash::FxHashSet;
use serde_json::Value;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn no_this_alias_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Unexpected aliasing of 'this' to local variable.")
@ -153,7 +157,7 @@ impl Rule for NoThisAlias {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -3,7 +3,11 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn no_unnecessary_type_constraint_diagnostic(
generic_type: &str,
@ -68,7 +72,7 @@ impl Rule for NoUnnecessaryTypeConstraint {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -4,7 +4,11 @@ use oxc_macros::declare_oxc_lint;
use oxc_semantic::SymbolId;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn no_unsafe_declaration_merging_diagnostic(span: Span, span1: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Unsafe declaration merging between classes and interfaces.")
@ -61,7 +65,7 @@ impl Rule for NoUnsafeDeclarationMerging {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -3,7 +3,12 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use crate::{ast_util::is_global_require_call, context::LintContext, rule::Rule, AstNode};
use crate::{
ast_util::is_global_require_call,
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn no_var_requires_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Require statement not part of import statement.")
@ -66,7 +71,7 @@ impl Rule for NoVarRequires {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -6,7 +6,11 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn prefer_as_const_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Expected a `const` assertion instead of a literal type annotation.")
@ -82,7 +86,7 @@ impl Rule for PreferAsConst {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -3,7 +3,11 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn prefer_enum_initializers_diagnostic(
member_name: &str,
@ -59,7 +63,7 @@ impl Rule for PreferEnumInitializers {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -6,7 +6,12 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, fixer::Fix, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
fixer::Fix,
rule::Rule,
AstNode,
};
fn prefer_function_type_diagnostic(suggestion: &str, span: Span) -> OxcDiagnostic {
// FIXME: use imperative message phrasing
@ -397,7 +402,7 @@ impl Rule for PreferFunctionType {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -4,7 +4,11 @@ use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use oxc_syntax::operator::{BinaryOperator, UnaryOperator};
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn prefer_literal_enum_member_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(
@ -109,7 +113,7 @@ impl Rule for PreferLiteralEnumMember {
ctx.diagnostic(prefer_literal_enum_member_diagnostic(decl.span));
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -6,7 +6,11 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
AstNode,
};
fn prefer_namespace_keyword_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Use 'namespace' instead of 'module' to declare custom TypeScript modules.")
@ -69,7 +73,7 @@ impl Rule for PreferNamespaceKeyword {
});
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -4,7 +4,10 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
};
fn prefer_ts_expect_error_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Enforce using `@ts-expect-error` over `@ts-ignore`")
@ -74,7 +77,7 @@ impl Rule for PreferTsExpectError {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -8,7 +8,10 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use crate::{context::LintContext, rule::Rule};
use crate::{
context::{ContextHost, LintContext},
rule::Rule,
};
fn triple_slash_reference_diagnostic(ref_kind: &str, span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(format!("Do not use a triple slash reference for {ref_kind}, use `import` style instead."))
@ -163,7 +166,7 @@ impl Rule for TripleSlashReference {
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

View file

@ -302,14 +302,14 @@ pub fn is_equality_matcher(matcher: &KnownMemberExpressionProperty) -> bool {
#[cfg(test)]
mod test {
use std::{path::Path, rc::Rc};
use std::rc::Rc;
use oxc_allocator::Allocator;
use oxc_parser::Parser;
use oxc_semantic::SemanticBuilder;
use oxc_span::SourceType;
use crate::LintContext;
use crate::{options::LintOptions, ContextHost};
#[test]
fn test_is_jest_file() {
@ -320,16 +320,18 @@ mod test {
let semantic_ret = SemanticBuilder::new("").with_cfg(true).build(program).semantic;
let semantic_ret = Rc::new(semantic_ret);
let path = Path::new("foo.js");
let ctx = LintContext::new(Box::from(path), Rc::clone(&semantic_ret));
let build_ctx = |path: &'static str| {
Rc::new(ContextHost::new(path, Rc::clone(&semantic_ret), LintOptions::default()))
.spawn_for_test()
};
let ctx = build_ctx("foo.js");
assert!(!super::is_jest_file(&ctx));
let path = Path::new("foo.test.js");
let ctx = LintContext::new(Box::from(path), Rc::clone(&semantic_ret));
let ctx = build_ctx("foo.test.js");
assert!(super::is_jest_file(&ctx));
let path = Path::new("__tests__/foo/test.spec.js");
let ctx = LintContext::new(Box::from(path), semantic_ret);
let ctx = build_ctx("__tests__/foo/test.spec.js");
assert!(super::is_jest_file(&ctx));
}
}

View file

@ -11,7 +11,7 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_semantic::SymbolId;
use oxc_span::Span;
use crate::{rule::Rule, AstNode, LintContext};
use crate::{context::ContextHost, rule::Rule, AstNode, LintContext};
fn react_perf_inline_diagnostic(message: &'static str, attr_span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(message)
@ -119,7 +119,7 @@ where
}
}
fn should_run(&self, ctx: &LintContext) -> bool {
fn should_run(&self, ctx: &ContextHost) -> bool {
ctx.source_type().is_jsx()
}
}

View file

@ -61,7 +61,7 @@ pub fn declare_all_lint_rules(metadata: AllLintRulesMeta) -> TokenStream {
let expanded = quote! {
#(pub use self::#use_stmts::#struct_names;)*
use crate::{context::LintContext, rule::{Rule, RuleCategory, RuleFixMeta, RuleMeta}, AstNode};
use crate::{context::{ContextHost, LintContext}, rule::{Rule, RuleCategory, RuleFixMeta, RuleMeta}, AstNode};
use oxc_semantic::SymbolId;
#[derive(Debug, Clone)]
@ -134,7 +134,7 @@ pub fn declare_all_lint_rules(metadata: AllLintRulesMeta) -> TokenStream {
}
}
pub(super) fn should_run(&self, ctx: &LintContext) -> bool {
pub(super) fn should_run(&self, ctx: &ContextHost) -> bool {
match self {
#(Self::#struct_names(rule) => rule.should_run(ctx)),*
}