diff --git a/crates/oxc_linter/src/fixer/fix.rs b/crates/oxc_linter/src/fixer/fix.rs index 8d982eb62d05f..8a795db0b8739 100644 --- a/crates/oxc_linter/src/fixer/fix.rs +++ b/crates/oxc_linter/src/fixer/fix.rs @@ -1,6 +1,7 @@ use std::{ borrow::Cow, fmt::{self, Display}, + iter, ops::Deref, }; @@ -388,6 +389,33 @@ impl PossibleFixes { } } +impl From> for PossibleFixes { + /// Create a new [`PossibleFixes`] from an `Option`. + fn from(fix: Option) -> Self { + match fix { + Some(fix) => PossibleFixes::Single(fix), + None => PossibleFixes::None, + } + } +} + +impl FromIterator for PossibleFixes { + /// Create a new [`PossibleFixes`] from an iterator of [`Fix`]es. + fn from_iter>(fixes: T) -> Self { + let mut fixes = fixes.into_iter(); + + if let Some(first_fix) = fixes.next() { + if let Some(second_fix) = fixes.next() { + PossibleFixes::Multiple(iter::chain([first_fix, second_fix], fixes).collect()) + } else { + PossibleFixes::Single(first_fix) + } + } else { + PossibleFixes::None + } + } +} + // NOTE (@DonIsaac): having these variants is effectively the same as interning // single or 0-element Vecs. I experimented with using smallvec here, but the // resulting struct size was larger (40 bytes vs 32). So, we're sticking with diff --git a/crates/oxc_linter/src/lib.rs b/crates/oxc_linter/src/lib.rs index b0ec186b741b9..8de2cd1c108d6 100644 --- a/crates/oxc_linter/src/lib.rs +++ b/crates/oxc_linter/src/lib.rs @@ -7,7 +7,7 @@ #![expect(clippy::missing_errors_doc)] use std::{ - mem, + iter, mem, path::Path, ptr::{self, NonNull}, rc::Rc, @@ -672,29 +672,20 @@ impl Linter { let possible_fixes = if let Some(suggestions) = diagnostic.suggestions && ctx_host.fix.can_apply(FixKind::Suggestion) { - let mut fixes = - Vec::with_capacity(usize::from(fix.is_some()) + suggestions.len()); - if let Some(fix) = fix { - fixes.push(fix); - } + debug_assert!( + !suggestions.is_empty(), + "`diagnostic.suggestions` should be `None` if there are no suggestions" + ); - for suggestion in suggestions { - if let Some(fix) = create_fix(suggestion.fixes, FixKind::Suggestion) { - fixes.push(fix.with_message(suggestion.message)); - } - } + let suggestions = suggestions.into_iter().filter_map(|suggestion| { + create_fix(suggestion.fixes, FixKind::Suggestion) + .map(|fix| fix.with_message(suggestion.message)) + }); - if fixes.is_empty() { - PossibleFixes::None - } else if fixes.len() == 1 { - PossibleFixes::Single(fixes.into_iter().next().unwrap()) - } else { - PossibleFixes::Multiple(fixes) - } - } else if let Some(fix) = fix { - PossibleFixes::Single(fix) + #[expect(clippy::from_iter_instead_of_collect)] + PossibleFixes::from_iter(iter::chain(fix, suggestions)) } else { - PossibleFixes::None + PossibleFixes::from(fix) }; ctx_host.push_diagnostic(Message::new(