Skip to content
Merged
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
73 changes: 59 additions & 14 deletions crates/oxc_linter/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::{
AllowWarnDeny, FrameworkFlags, ModuleRecord, OxlintEnv, OxlintGlobals, OxlintSettings,
config::GlobalValue,
disable_directives::DisableDirectives,
fixer::{FixKind, Message, PossibleFixes, RuleFix, RuleFixer},
fixer::{Fix, FixKind, Message, PossibleFixes, RuleFix, RuleFixer},
};

mod host;
Expand Down Expand Up @@ -325,7 +325,6 @@ impl<'a> LintContext<'a> {
/// returns something that can turn into a [`RuleFix`].
///
/// [closure]: <https://doc.rust-lang.org/book/ch13-01-closures.html>
#[cfg_attr(debug_assertions, expect(clippy::missing_panics_doc))] // Only panics in debug mode
pub fn diagnostic_with_fix_of_kind<C, F>(
&self,
diagnostic: OxcDiagnostic,
Expand All @@ -334,29 +333,74 @@ impl<'a> LintContext<'a> {
) where
C: Into<RuleFix<'a>>,
F: FnOnce(RuleFixer<'_, 'a>) -> C,
{
let (diagnostic, fix) = self.create_fix(fix_kind, fix, diagnostic);
if let Some(fix) = fix {
self.add_diagnostic(Message::new(diagnostic, PossibleFixes::Single(fix)));
} else {
self.diagnostic(diagnostic);
}
}

/// Report a lint rule violation and provide an automatic fix of a specific kind.
/// This method is used when the rule can provide multiple fixes for the same diagnostic.
pub fn diagnostics_with_multiple_fixes<C, F1, F2>(
&self,
diagnostic: OxcDiagnostic,
fix_one: (FixKind, F1),
fix_two: (FixKind, F2),
) where
C: Into<RuleFix<'a>>,
F1: FnOnce(RuleFixer<'_, 'a>) -> C,
F2: FnOnce(RuleFixer<'_, 'a>) -> C,
{
let fixes_result: Vec<Fix<'a>> = vec![
self.create_fix(fix_one.0, fix_one.1, diagnostic.clone()).1,
self.create_fix(fix_two.0, fix_two.1, diagnostic.clone()).1,
]
.into_iter()
.flatten()
.collect();

if fixes_result.is_empty() {
self.diagnostic(diagnostic);
} else {
self.add_diagnostic(Message::new(diagnostic, PossibleFixes::Multiple(fixes_result)));
}
}

fn create_fix<C, F>(
&self,
fix_kind: FixKind,
fix: F,
diagnostic: OxcDiagnostic,
) -> (OxcDiagnostic, Option<Fix<'a>>)
where
C: Into<RuleFix<'a>>,
F: FnOnce(RuleFixer<'_, 'a>) -> C,
{
let fixer = RuleFixer::new(fix_kind, self);
let rule_fix: RuleFix<'a> = fix(fixer).into();
#[cfg(debug_assertions)]
{
assert!(
self.current_rule_fix_capabilities.supports_fix(fix_kind),
"Rule `{}` does not support safe fixes. Did you forget to update fix capabilities in declare_oxc_lint?.\n\tSupported fix kinds: {:?}\n\tAttempted fix kind: {:?}",
self.current_rule_name,
FixKind::from(self.current_rule_fix_capabilities),
rule_fix.kind()
);
}
debug_assert!(
self.current_rule_fix_capabilities.supports_fix(fix_kind),
"Rule `{}` does not support safe fixes. Did you forget to update fix capabilities in declare_oxc_lint?.\n\tSupported fix kinds: {:?}\n\tAttempted fix kind: {:?}",
self.current_rule_name,
FixKind::from(self.current_rule_fix_capabilities),
rule_fix.kind()
);

let diagnostic = match (rule_fix.message(), &diagnostic.help) {
(Some(message), None) => diagnostic.with_help(message.to_owned()),
_ => diagnostic,
};

if self.parent.fix.can_apply(rule_fix.kind()) && !rule_fix.is_empty() {
let fix = rule_fix.into_fix(self.source_text());
#[cfg(debug_assertions)]
{
if fix.span.size() > 1 {
assert!(
debug_assert!(
fix.message.as_ref().is_some_and(|msg| !msg.is_empty()),
"Rule `{}/{}` fix should have a message for a complex fix. Did you forget to add a message?\n Source text: {:?}\n Fixed text: {:?}\nhelp: You can add a message to a fix with `RuleFix.with_message()`",
self.current_plugin_name,
Expand All @@ -366,9 +410,10 @@ impl<'a> LintContext<'a> {
);
}
}
self.add_diagnostic(Message::new(diagnostic, PossibleFixes::Single(fix)));

(diagnostic, Some(fix))
} else {
self.diagnostic(diagnostic);
(diagnostic, None)
}
}

Expand Down
Loading