diff --git a/crates/oxc_ast/src/trivia.rs b/crates/oxc_ast/src/trivia.rs index 18529150f42e7..1f0a82e31d0f5 100644 --- a/crates/oxc_ast/src/trivia.rs +++ b/crates/oxc_ast/src/trivia.rs @@ -12,13 +12,15 @@ use oxc_span::Span; #[derive(Debug, Clone, Copy)] pub struct Comment { pub kind: CommentKind, - pub end: u32, + /// The span of the comment text (without leading/trailing delimiters). + pub span: Span, } impl Comment { #[inline] - pub fn new(end: u32, kind: CommentKind) -> Self { - Self { kind, end } + pub fn new(start: u32, end: u32, kind: CommentKind) -> Self { + let span = Span::new(start, end); + Self { kind, span } } } @@ -41,7 +43,7 @@ impl CommentKind { } /// Sorted set of unique trivia comments, in ascending order by starting position. -pub type SortedComments = Box<[(u32, Comment)]>; +pub type SortedComments = Box<[Comment]>; #[derive(Debug, Clone, Default)] pub struct Trivias(Arc); @@ -51,7 +53,7 @@ pub struct TriviasImpl { /// Unique comments, ordered by increasing span-start. comments: SortedComments, - irregular_whitespaces: Vec, + irregular_whitespaces: Box<[Span]>, } impl Deref for Trivias { @@ -65,11 +67,14 @@ impl Deref for Trivias { impl Trivias { pub fn new(comments: SortedComments, irregular_whitespaces: Vec) -> Trivias { - Self(Arc::new(TriviasImpl { comments, irregular_whitespaces })) + Self(Arc::new(TriviasImpl { + comments, + irregular_whitespaces: irregular_whitespaces.into_boxed_slice(), + })) } - pub fn comments(&self) -> impl Iterator + '_ { - self.comments.iter().map(|(start, comment)| (comment.kind, Span::new(*start, comment.end))) + pub fn comments(&self) -> impl Iterator { + self.comments.iter() } pub fn comments_range(&self, range: R) -> CommentsRange<'_> @@ -83,21 +88,21 @@ impl Trivias { self.comments_range(span.start..span.end).count() > 0 } - pub fn irregular_whitespaces(&self) -> &Vec { + pub fn irregular_whitespaces(&self) -> &[Span] { &self.irregular_whitespaces } } /// Double-ended iterator over a range of comments, by starting position. pub struct CommentsRange<'a> { - comments: &'a [(u32, Comment)], + comments: &'a [Comment], range: (Bound, Bound), current_start: usize, current_end: usize, } impl<'a> CommentsRange<'a> { - fn new(comments: &'a [(u32, Comment)], start: Bound, end: Bound) -> Self { + fn new(comments: &'a [Comment], start: Bound, end: Bound) -> Self { // Directly skip all comments that are already known to start // outside the requested range. let partition_start = { @@ -106,7 +111,7 @@ impl<'a> CommentsRange<'a> { Bound::Included(x) => x, Bound::Excluded(x) => x.saturating_add(1), }; - comments.partition_point(|(start, _)| *start < range_start) + comments.partition_point(|comment| comment.span.start < range_start) }; let partition_end = { let range_end = match end { @@ -114,7 +119,7 @@ impl<'a> CommentsRange<'a> { Bound::Included(x) => x, Bound::Excluded(x) => x.saturating_sub(1), }; - comments.partition_point(|(start, _)| *start <= range_end) + comments.partition_point(|comment| comment.span.start <= range_end) }; Self { comments, @@ -126,14 +131,14 @@ impl<'a> CommentsRange<'a> { } impl<'c> Iterator for CommentsRange<'c> { - type Item = (&'c u32, &'c Comment); + type Item = &'c Comment; fn next(&mut self) -> Option { if self.current_start < self.current_end { - for (start, comment) in &self.comments[self.current_start..self.current_end] { + for comment in &self.comments[self.current_start..self.current_end] { self.current_start = self.current_start.saturating_add(1); - if self.range.contains(start) { - return Some((start, comment)); + if self.range.contains(&comment.span.start) { + return Some(comment); } } } @@ -149,11 +154,10 @@ impl<'c> Iterator for CommentsRange<'c> { impl<'c> DoubleEndedIterator for CommentsRange<'c> { fn next_back(&mut self) -> Option { if self.current_start < self.current_end { - for (start, comment) in self.comments[self.current_start..self.current_end].iter().rev() - { + for comment in self.comments[self.current_start..self.current_end].iter().rev() { self.current_end = self.current_end.saturating_sub(1); - if self.range.contains(start) { - return Some((start, comment)); + if self.range.contains(&comment.span.start) { + return Some(comment); } } } @@ -170,11 +174,11 @@ mod test { #[test] fn test_comments_range() { let comments: SortedComments = vec![ - (0, Comment { end: 4, kind: CommentKind::SingleLine }), - (5, Comment { end: 9, kind: CommentKind::SingleLine }), - (10, Comment { end: 13, kind: CommentKind::SingleLine }), - (14, Comment { end: 17, kind: CommentKind::SingleLine }), - (18, Comment { end: 23, kind: CommentKind::SingleLine }), + Comment { span: Span::new(0, 4), kind: CommentKind::SingleLine }, + Comment { span: Span::new(5, 9), kind: CommentKind::SingleLine }, + Comment { span: Span::new(10, 13), kind: CommentKind::SingleLine }, + Comment { span: Span::new(14, 17), kind: CommentKind::SingleLine }, + Comment { span: Span::new(18, 23), kind: CommentKind::SingleLine }, ] .into_boxed_slice(); let full_len = comments.len(); diff --git a/crates/oxc_codegen/src/annotation_comment.rs b/crates/oxc_codegen/src/annotation_comment.rs index 8ac820e9f4564..ede9470f53a3f 100644 --- a/crates/oxc_codegen/src/annotation_comment.rs +++ b/crates/oxc_codegen/src/annotation_comment.rs @@ -12,20 +12,20 @@ static MATCHER: Lazy> = Lazy::new(|| { pub fn get_leading_annotate_comment( node_start: u32, codegen: &mut Codegen<{ MINIFY }>, -) -> Option<(u32, Comment)> { +) -> Option { let maybe_leading_comment = codegen.try_get_leading_comment(node_start); - let (comment_start, comment) = maybe_leading_comment?; + let comment = maybe_leading_comment?; let real_end = match comment.kind { - CommentKind::SingleLine => comment.end, - CommentKind::MultiLine => comment.end + 2, + CommentKind::SingleLine => comment.span.end, + CommentKind::MultiLine => comment.span.end + 2, }; let source_code = codegen.source_text; let content_between = &source_code[real_end as usize..node_start as usize]; // Used for VariableDeclaration (Rollup only respects "const" and only for the first one) if content_between.chars().all(|ch| ch.is_ascii_whitespace()) { - let comment_content = &source_code[*comment_start as usize..comment.end as usize]; + let comment_content = &source_code[comment.span.start as usize..comment.span.end as usize]; if MATCHER.find_iter(&comment_content).next().is_some() { - return Some((*comment_start, *comment)); + return Some(*comment); } None } else { @@ -33,21 +33,17 @@ pub fn get_leading_annotate_comment( } } -pub fn print_comment( - comment_start: u32, - comment: Comment, - p: &mut Codegen<{ MINIFY }>, -) { +pub fn print_comment(comment: Comment, p: &mut Codegen<{ MINIFY }>) { match comment.kind { CommentKind::SingleLine => { p.print_str("//"); - p.print_range_of_source_code(comment_start as usize..comment.end as usize); + p.print_range_of_source_code(comment.span.start as usize..comment.span.end as usize); p.print_soft_newline(); p.print_indent(); } CommentKind::MultiLine => { p.print_str("/*"); - p.print_range_of_source_code(comment_start as usize..comment.end as usize); + p.print_range_of_source_code(comment.span.start as usize..comment.span.end as usize); p.print_str("*/"); p.print_soft_space(); } @@ -58,11 +54,11 @@ pub fn gen_comment(node_start: u32, codegen: &mut Codegen<{ if !codegen.comment_options.preserve_annotate_comments { return; } - if let Some((comment_start, comment)) = codegen.try_take_moved_comment(node_start) { - print_comment::(comment_start, comment, codegen); + if let Some(comment) = codegen.try_take_moved_comment(node_start) { + print_comment::(comment, codegen); } let maybe_leading_annotate_comment = get_leading_annotate_comment(node_start, codegen); - if let Some((comment_start, comment)) = maybe_leading_annotate_comment { - print_comment::(comment_start, comment, codegen); + if let Some(comment) = maybe_leading_annotate_comment { + print_comment::(comment, codegen); } } diff --git a/crates/oxc_codegen/src/lib.rs b/crates/oxc_codegen/src/lib.rs index c7e4e5df3bc30..1b62391198617 100644 --- a/crates/oxc_codegen/src/lib.rs +++ b/crates/oxc_codegen/src/lib.rs @@ -517,7 +517,7 @@ impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> { } } -pub(crate) type MoveCommentMap = FxHashMap; +pub(crate) type MoveCommentMap = FxHashMap; // Comment related impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> { @@ -541,15 +541,15 @@ impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> { /// /// }, b = 10000; /// ``` - fn move_comment(&mut self, position: u32, full_comment_info: (u32, Comment)) { + fn move_comment(&mut self, position: u32, full_comment_info: Comment) { self.move_comment_map.insert(position, full_comment_info); } - fn try_get_leading_comment(&self, start: u32) -> Option<(&u32, &Comment)> { + fn try_get_leading_comment(&self, start: u32) -> Option<&Comment> { self.trivias.comments_range(0..start).next_back() } - fn try_take_moved_comment(&mut self, node_start: u32) -> Option<(u32, Comment)> { + fn try_take_moved_comment(&mut self, node_start: u32) -> Option { self.move_comment_map.remove(&node_start) } } diff --git a/crates/oxc_linter/src/disable_directives.rs b/crates/oxc_linter/src/disable_directives.rs index d3ea039f813e9..c4b0fc32147b5 100644 --- a/crates/oxc_linter/src/disable_directives.rs +++ b/crates/oxc_linter/src/disable_directives.rs @@ -93,8 +93,8 @@ impl<'a> DisableDirectivesBuilder<'a> { // This algorithm iterates through the comments and builds all intervals // for matching disable and enable pairs. // Wrongly ordered matching pairs are not taken into consideration. - for (_, span) in self.trivias.clone().comments() { - let text = span.source_text(self.source_text); + for comment in self.trivias.clone().comments() { + let text = comment.span.source_text(self.source_text); let text = text.trim_start(); if let Some(text) = @@ -103,30 +103,35 @@ impl<'a> DisableDirectivesBuilder<'a> { // `eslint-disable` if text.trim().is_empty() { if self.disable_all_start.is_none() { - self.disable_all_start = Some(span.end); + self.disable_all_start = Some(comment.span.end); } - self.disable_all_comments.push(span); + self.disable_all_comments.push(comment.span); continue; } // `eslint-disable-next-line` if let Some(text) = text.strip_prefix("-next-line") { // Get the span up to the next new line - let stop = self.source_text[span.end as usize..] + let stop = self.source_text[comment.span.end as usize..] .lines() .take(2) - .fold(span.end, |acc, line| acc + line.len() as u32); + .fold(comment.span.end, |acc, line| acc + line.len() as u32); if text.trim().is_empty() { - self.add_interval(span.end, stop, DisabledRule::All); - self.disable_all_comments.push(span); + self.add_interval(comment.span.end, stop, DisabledRule::All); + self.disable_all_comments.push(comment.span); } else { // `eslint-disable-next-line rule_name1, rule_name2` let mut rules = vec![]; Self::get_rule_names(text, |rule_name| { - self.add_interval(span.end, stop, DisabledRule::Single(rule_name)); + self.add_interval( + comment.span.end, + stop, + DisabledRule::Single(rule_name), + ); rules.push(rule_name); }); - self.disable_rule_comments.push(DisableRuleComment { span, rules }); + self.disable_rule_comments + .push(DisableRuleComment { span: comment.span, rules }); } continue; } @@ -134,16 +139,16 @@ impl<'a> DisableDirectivesBuilder<'a> { // `eslint-disable-line` if let Some(text) = text.strip_prefix("-line") { // Get the span between the preceding newline to this comment - let start = self.source_text[..=span.start as usize] + let start = self.source_text[..=comment.span.start as usize] .lines() .next_back() - .map_or(0, |line| span.start - (line.len() as u32 - 1)); - let stop = span.start; + .map_or(0, |line| comment.span.start - (line.len() as u32 - 1)); + let stop = comment.span.start; // `eslint-disable-line` if text.trim().is_empty() { self.add_interval(start, stop, DisabledRule::All); - self.disable_all_comments.push(span); + self.disable_all_comments.push(comment.span); } else { // `eslint-disable-line rule-name1, rule-name2` let mut rules = vec![]; @@ -151,7 +156,8 @@ impl<'a> DisableDirectivesBuilder<'a> { self.add_interval(start, stop, DisabledRule::Single(rule_name)); rules.push(rule_name); }); - self.disable_rule_comments.push(DisableRuleComment { span, rules }); + self.disable_rule_comments + .push(DisableRuleComment { span: comment.span, rules }); } continue; } @@ -159,10 +165,10 @@ impl<'a> DisableDirectivesBuilder<'a> { // `eslint-disable rule-name1, rule-name2` let mut rules = vec![]; Self::get_rule_names(text, |rule_name| { - self.disable_start_map.entry(rule_name).or_insert(span.end); + self.disable_start_map.entry(rule_name).or_insert(comment.span.end); rules.push(rule_name); }); - self.disable_rule_comments.push(DisableRuleComment { span, rules }); + self.disable_rule_comments.push(DisableRuleComment { span: comment.span, rules }); continue; } @@ -173,13 +179,17 @@ impl<'a> DisableDirectivesBuilder<'a> { // `eslint-enable` if text.trim().is_empty() { if let Some(start) = self.disable_all_start.take() { - self.add_interval(start, span.start, DisabledRule::All); + self.add_interval(start, comment.span.start, DisabledRule::All); } } else { // `eslint-enable rule-name1, rule-name2` Self::get_rule_names(text, |rule_name| { if let Some(start) = self.disable_start_map.remove(rule_name) { - self.add_interval(start, span.start, DisabledRule::Single(rule_name)); + self.add_interval( + start, + comment.span.start, + DisabledRule::Single(rule_name), + ); } }); } diff --git a/crates/oxc_linter/src/rules/eslint/default_case.rs b/crates/oxc_linter/src/rules/eslint/default_case.rs index 2e657e8d8461f..32f1d15a7a257 100644 --- a/crates/oxc_linter/src/rules/eslint/default_case.rs +++ b/crates/oxc_linter/src/rules/eslint/default_case.rs @@ -72,10 +72,8 @@ impl Rule for DefaultCase { .trivias() .comments_range(last_case.span.start..switch.span.end) .last() - .is_some_and(|(start, comment)| { - let raw = Span::new(*start, comment.end) - .source_text(ctx.semantic().source_text()) - .trim(); + .is_some_and(|comment| { + let raw = comment.span.source_text(ctx.semantic().source_text()).trim(); match &self.comment_pattern { Some(comment_pattern) => comment_pattern.is_match(raw), None => raw.eq_ignore_ascii_case("no default"), diff --git a/crates/oxc_linter/src/rules/eslint/max_lines.rs b/crates/oxc_linter/src/rules/eslint/max_lines.rs index 2b00cc0f5b664..28363beb633c0 100644 --- a/crates/oxc_linter/src/rules/eslint/max_lines.rs +++ b/crates/oxc_linter/src/rules/eslint/max_lines.rs @@ -82,23 +82,29 @@ impl Rule for MaxLines { fn run_once(&self, ctx: &LintContext) { let comment_lines = if self.skip_comments { let mut comment_lines: usize = 0; - for (kind, span) in ctx.semantic().trivias().comments() { - if kind.is_single_line() { - let comment_line = - ctx.source_text()[..span.start as usize].lines().next_back().unwrap_or(""); + for comment in ctx.semantic().trivias().comments() { + if comment.kind.is_single_line() { + let comment_line = ctx.source_text()[..comment.span.start as usize] + .lines() + .next_back() + .unwrap_or(""); if line_has_just_comment(comment_line, "//") { comment_lines += 1; } } else { - let mut start_line = ctx.source_text()[..span.start as usize].lines().count(); - let comment_start_line = - ctx.source_text()[..span.start as usize].lines().next_back().unwrap_or(""); + let mut start_line = + ctx.source_text()[..comment.span.start as usize].lines().count(); + let comment_start_line = ctx.source_text()[..comment.span.start as usize] + .lines() + .next_back() + .unwrap_or(""); if !line_has_just_comment(comment_start_line, "/*") { start_line += 1; } - let mut end_line = ctx.source_text()[..=span.end as usize].lines().count(); + let mut end_line = + ctx.source_text()[..=comment.span.end as usize].lines().count(); let comment_end_line = - ctx.source_text()[span.end as usize..].lines().next().unwrap_or(""); + ctx.source_text()[comment.span.end as usize..].lines().next().unwrap_or(""); if line_has_just_comment(comment_end_line, "*/") { end_line += 1; } diff --git a/crates/oxc_linter/src/rules/eslint/no_fallthrough.rs b/crates/oxc_linter/src/rules/eslint/no_fallthrough.rs index bdf32261051ee..fa6c8225032c7 100644 --- a/crates/oxc_linter/src/rules/eslint/no_fallthrough.rs +++ b/crates/oxc_linter/src/rules/eslint/no_fallthrough.rs @@ -212,8 +212,8 @@ impl NoFallthrough { let comment = semantic .trivias() .comments_range(range) - .map(|(start, comment)| { - &semantic.source_text()[*start as usize..comment.end as usize] + .map(|comment| { + &semantic.source_text()[comment.span.start as usize..comment.span.end as usize] }) .last() .map(str::trim); diff --git a/crates/oxc_linter/src/rules/jest/no_commented_out_tests.rs b/crates/oxc_linter/src/rules/jest/no_commented_out_tests.rs index febcf9a055c9d..6be16fafeff45 100644 --- a/crates/oxc_linter/src/rules/jest/no_commented_out_tests.rs +++ b/crates/oxc_linter/src/rules/jest/no_commented_out_tests.rs @@ -52,10 +52,10 @@ impl Rule for NoCommentedOutTests { } let comments = ctx.semantic().trivias().comments(); let source_text = ctx.semantic().source_text(); - let commented_tests = comments.filter_map(|(_, span)| { - let text = span.source_text(source_text); + let commented_tests = comments.filter_map(|comment| { + let text = comment.span.source_text(source_text); if RE.is_match(text) { - Some(span) + Some(comment.span) } else { None } diff --git a/crates/oxc_linter/src/rules/typescript/ban_ts_comment.rs b/crates/oxc_linter/src/rules/typescript/ban_ts_comment.rs index e516c06d83263..591e8f1def287 100644 --- a/crates/oxc_linter/src/rules/typescript/ban_ts_comment.rs +++ b/crates/oxc_linter/src/rules/typescript/ban_ts_comment.rs @@ -141,12 +141,12 @@ impl Rule for BanTsComment { fn run_once(&self, ctx: &LintContext) { let comments = ctx.semantic().trivias().comments(); - for (kind, span) in comments { - let raw = ctx.source_range(span); - if let Some(captures) = find_ts_comment_directive(raw, kind.is_single_line()) { + for comm in comments { + let raw = ctx.source_range(comm.span); + if let Some(captures) = find_ts_comment_directive(raw, comm.kind.is_single_line()) { // safe to unwrap, if capture success, it can always capture one of the four directives let (directive, description) = (captures.0, captures.1); - if CommentKind::MultiLine == kind + if CommentKind::MultiLine == comm.kind && (directive == "check" || directive == "nocheck") { continue; @@ -163,16 +163,16 @@ impl Rule for BanTsComment { if *on { if directive == "ignore" { ctx.diagnostic_with_fix( - ignore_instead_of_expect_error(span), + ignore_instead_of_expect_error(comm.span), |fixer| { fixer.replace( - span, + comm.span, raw.replace("@ts-ignore", "@ts-expect-error"), ) }, ); } else { - ctx.diagnostic(comment(directive, span)); + ctx.diagnostic(comment(directive, comm.span)); } } } @@ -182,7 +182,7 @@ impl Rule for BanTsComment { ctx.diagnostic(comment_requires_description( directive, self.minimum_description_length, - span, + comm.span, )); } @@ -191,7 +191,7 @@ impl Rule for BanTsComment { ctx.diagnostic(comment_description_not_match_pattern( directive, &re.to_string(), - span, + comm.span, )); } } diff --git a/crates/oxc_linter/src/rules/typescript/ban_tslint_comment.rs b/crates/oxc_linter/src/rules/typescript/ban_tslint_comment.rs index cf233f5dc782d..f63b458665778 100644 --- a/crates/oxc_linter/src/rules/typescript/ban_tslint_comment.rs +++ b/crates/oxc_linter/src/rules/typescript/ban_tslint_comment.rs @@ -38,12 +38,16 @@ impl Rule for BanTslintComment { let comments = ctx.semantic().trivias().comments(); let source_text_len = ctx.semantic().source_text().len(); - for (kind, span) in comments { - let raw = span.source_text(ctx.semantic().source_text()); + for comment in comments { + let raw = comment.span.source_text(ctx.semantic().source_text()); if is_tslint_comment_directive(raw) { - let comment_span = - get_full_comment(source_text_len, span.start, span.end, kind.is_multi_line()); + let comment_span = get_full_comment( + source_text_len, + comment.span.start, + comment.span.end, + comment.kind.is_multi_line(), + ); ctx.diagnostic_with_fix( ban_tslint_comment_diagnostic(raw.trim(), comment_span), diff --git a/crates/oxc_linter/src/rules/typescript/prefer_function_type.rs b/crates/oxc_linter/src/rules/typescript/prefer_function_type.rs index e1caf9f6e28cd..8234dd36479c1 100644 --- a/crates/oxc_linter/src/rules/typescript/prefer_function_type.rs +++ b/crates/oxc_linter/src/rules/typescript/prefer_function_type.rs @@ -162,9 +162,7 @@ fn check_member(member: &TSSignature, node: &AstNode<'_>, ctx: &LintContext<'_>) .semantic() .trivias() .comments_range(node_start..node_end) - .map(|(start, comment)| { - (*comment, Span::new(*start, comment.end)) - }); + .map(|comment| (*comment, comment.span)); let comments_text = { let mut comments_vec: Vec = vec![]; diff --git a/crates/oxc_linter/src/rules/typescript/prefer_ts_expect_error.rs b/crates/oxc_linter/src/rules/typescript/prefer_ts_expect_error.rs index a66a8684eb87d..594bb1d13687c 100644 --- a/crates/oxc_linter/src/rules/typescript/prefer_ts_expect_error.rs +++ b/crates/oxc_linter/src/rules/typescript/prefer_ts_expect_error.rs @@ -45,15 +45,15 @@ impl Rule for PreferTsExpectError { fn run_once(&self, ctx: &LintContext) { let comments = ctx.semantic().trivias().comments(); - for (kind, span) in comments { - let raw = span.source_text(ctx.semantic().source_text()); + for comment in comments { + let raw = comment.span.source_text(ctx.semantic().source_text()); - if !is_valid_ts_ignore_present(kind, raw) { + if !is_valid_ts_ignore_present(comment.kind, raw) { continue; } - if kind.is_single_line() { - let comment_span = Span::new(span.start - 2, span.end); + if comment.kind.is_single_line() { + let comment_span = Span::new(comment.span.start - 2, comment.span.end); ctx.diagnostic_with_fix(prefer_ts_expect_error_diagnostic(comment_span), |fixer| { fixer.replace( comment_span, @@ -61,7 +61,7 @@ impl Rule for PreferTsExpectError { ) }); } else { - let comment_span = Span::new(span.start - 2, span.end + 2); + let comment_span = Span::new(comment.span.start - 2, comment.span.end + 2); ctx.diagnostic_with_fix(prefer_ts_expect_error_diagnostic(comment_span), |fixer| { fixer.replace( comment_span, diff --git a/crates/oxc_linter/src/rules/typescript/triple_slash_reference.rs b/crates/oxc_linter/src/rules/typescript/triple_slash_reference.rs index 34150479e2d84..6cc9db238d745 100644 --- a/crates/oxc_linter/src/rules/typescript/triple_slash_reference.rs +++ b/crates/oxc_linter/src/rules/typescript/triple_slash_reference.rs @@ -112,8 +112,9 @@ impl Rule for TripleSlashReference { let comments_range_end = program.body.first().map_or(program.span.end, |v| v.span().start); let mut refs_for_import = HashMap::new(); - for (start, comment) in ctx.semantic().trivias().comments_range(0..comments_range_end) { - let raw = &ctx.semantic().source_text()[*start as usize..comment.end as usize]; + for comment in ctx.semantic().trivias().comments_range(0..comments_range_end) { + let raw = &ctx.semantic().source_text() + [comment.span.start as usize..comment.span.end as usize]; if let Some((group1, group2)) = get_attr_key_and_value(raw) { if (group1 == "types" && self.types == TypesOption::Never) || (group1 == "path" && self.path == PathOption::Never) @@ -121,12 +122,13 @@ impl Rule for TripleSlashReference { { ctx.diagnostic(triple_slash_reference_diagnostic( &group2, - Span::new(*start - 2, comment.end), + Span::new(comment.span.start - 2, comment.span.end), )); } if group1 == "types" && self.types == TypesOption::PreferImport { - refs_for_import.insert(group2, Span::new(*start - 2, comment.end)); + refs_for_import + .insert(group2, Span::new(comment.span.start - 2, comment.span.end)); } } } diff --git a/crates/oxc_linter/src/rules/unicorn/no_empty_file.rs b/crates/oxc_linter/src/rules/unicorn/no_empty_file.rs index 820292f54e6a5..1205faeb14c47 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_empty_file.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_empty_file.rs @@ -69,11 +69,11 @@ impl Rule for NoEmptyFile { } fn has_triple_slash_directive(ctx: &LintContext<'_>) -> bool { - for (kind, span) in ctx.semantic().trivias().comments() { - if !kind.is_single_line() { + for comment in ctx.semantic().trivias().comments() { + if !comment.kind.is_single_line() { continue; } - let text = span.source_text(ctx.source_text()); + let text = comment.span.source_text(ctx.source_text()); if text.starts_with("///") { return true; } diff --git a/crates/oxc_linter/src/utils/tree_shaking.rs b/crates/oxc_linter/src/utils/tree_shaking.rs index 170c9230bbf0e..5fe45096eb7f2 100644 --- a/crates/oxc_linter/src/utils/tree_shaking.rs +++ b/crates/oxc_linter/src/utils/tree_shaking.rs @@ -217,12 +217,10 @@ fn flatten_member_expr_if_possible(function_name: &FunctionName) -> CompactStr { /// /// pub fn has_pure_notation(span: Span, ctx: &LintContext) -> bool { - let Some((start, comment)) = ctx.semantic().trivias().comments_range(..span.start).next_back() - else { + let Some(comment) = ctx.semantic().trivias().comments_range(..span.start).next_back() else { return false; }; - let span = Span::new(*start, comment.end); - let raw = span.source_text(ctx.semantic().source_text()); + let raw = comment.span.source_text(ctx.semantic().source_text()); raw.contains("@__PURE__") || raw.contains("#__PURE__") } @@ -259,12 +257,9 @@ pub fn has_comment_about_side_effect_check(span: Span, ctx: &LintContext) -> boo /// let e = 2 /// ``` pub fn get_leading_tree_shaking_comment<'a>(span: Span, ctx: &LintContext<'a>) -> Option<&'a str> { - let (start, comment) = ctx.semantic().trivias().comments_range(..span.start).next_back()?; + let comment = ctx.semantic().trivias().comments_range(..span.start).next_back()?; - let comment_text = { - let span = Span::new(*start, comment.end); - span.source_text(ctx.source_text()) - }; + let comment_text = comment.span.source_text(ctx.source_text()); if !is_tree_shaking_comment(comment_text) { return None; @@ -272,7 +267,7 @@ pub fn get_leading_tree_shaking_comment<'a>(span: Span, ctx: &LintContext<'a>) - // If there are non-whitespace characters between the `comment`` and the `span`, // we treat the `comment` not belongs to the `span`. - let only_whitespace = ctx.source_text()[comment.end as usize..span.start as usize] + let only_whitespace = ctx.source_text()[comment.span.end as usize..span.start as usize] .strip_prefix("*/") // for multi-line comment .is_some_and(|s| s.trim().is_empty()); @@ -293,9 +288,9 @@ pub fn get_leading_tree_shaking_comment<'a>(span: Span, ctx: &LintContext<'a>) - return None; }; - if comment.end < current_line_start { + if comment.span.end < current_line_start { let previous_line = - ctx.source_text()[..comment.end as usize].lines().next_back().unwrap_or(""); + ctx.source_text()[..comment.span.end as usize].lines().next_back().unwrap_or(""); let nothing_before_comment = previous_line .trim() .strip_prefix(if comment.kind == CommentKind::SingleLine { "//" } else { "/*" }) diff --git a/crates/oxc_parser/examples/parser.rs b/crates/oxc_parser/examples/parser.rs index 017986884084c..f47a4c898f83e 100644 --- a/crates/oxc_parser/examples/parser.rs +++ b/crates/oxc_parser/examples/parser.rs @@ -25,8 +25,11 @@ fn main() -> Result<(), String> { println!("{}", serde_json::to_string_pretty(&ret.program).unwrap()); println!("Comments:"); - let comments = - ret.trivias.comments().map(|(_, span)| span.source_text(&source_text)).collect::>(); + let comments = ret + .trivias + .comments() + .map(|comment| comment.span.source_text(&source_text)) + .collect::>(); println!("{comments:?}"); if ret.errors.is_empty() { diff --git a/crates/oxc_parser/src/lexer/trivia_builder.rs b/crates/oxc_parser/src/lexer/trivia_builder.rs index ea66aaa2c35aa..937ec21bd2eb1 100644 --- a/crates/oxc_parser/src/lexer/trivia_builder.rs +++ b/crates/oxc_parser/src/lexer/trivia_builder.rs @@ -6,7 +6,7 @@ pub struct TriviaBuilder { // NOTE(lucab): This is a set of unique comments. Duplicated // comments could be generated in case of rewind; they are // filtered out at insertion time. - comments: Vec<(u32, Comment)>, + comments: Vec, irregular_whitespaces: Vec, } @@ -17,23 +17,23 @@ impl TriviaBuilder { pub fn add_single_line_comment(&mut self, start: u32, end: u32) { // skip leading `//` - self.add_comment(start + 2, Comment::new(end, CommentKind::SingleLine)); + self.add_comment(Comment::new(start + 2, end, CommentKind::SingleLine)); } pub fn add_multi_line_comment(&mut self, start: u32, end: u32) { // skip leading `/*` and trailing `*/` - self.add_comment(start + 2, Comment::new(end - 2, CommentKind::MultiLine)); + self.add_comment(Comment::new(start + 2, end - 2, CommentKind::MultiLine)); } - fn add_comment(&mut self, start: u32, comment: Comment) { + fn add_comment(&mut self, comment: Comment) { // The comments array is an ordered vec, only add the comment if its not added before, // to avoid situations where the parser needs to rewind and tries to reinsert the comment. - if let Some((last_start, _)) = self.comments.last() { - if start <= *last_start { + if let Some(last_comment) = self.comments.last() { + if comment.span.start <= last_comment.span.start { return; } } - self.comments.push((start, comment)); + self.comments.push(comment); } pub fn add_irregular_whitespace(&mut self, start: u32, end: u32) { diff --git a/crates/oxc_parser/src/lib.rs b/crates/oxc_parser/src/lib.rs index 3c3184daeffc8..bb83000f3e8af 100644 --- a/crates/oxc_parser/src/lib.rs +++ b/crates/oxc_parser/src/lib.rs @@ -508,7 +508,7 @@ mod test { let ret = Parser::new(&allocator, source, source_type).parse(); let comments = ret.trivias.comments().collect::>(); assert_eq!(comments.len(), 1, "{source}"); - assert_eq!(comments.first().unwrap().0, kind, "{source}"); + assert_eq!(comments.first().unwrap().kind, kind, "{source}"); } } diff --git a/crates/oxc_prettier/src/comments/print.rs b/crates/oxc_prettier/src/comments/print.rs index fa4ba5fea6622..218bf3926bdb4 100644 --- a/crates/oxc_prettier/src/comments/print.rs +++ b/crates/oxc_prettier/src/comments/print.rs @@ -33,11 +33,11 @@ impl<'a> Prettier<'a> { pub(crate) fn has_comment(&mut self, range: Span, flags: CommentFlags) -> bool { let mut peekable_trivias = self.trivias.clone(); - while let Some((kind, span)) = peekable_trivias.peek().copied() { - let start = span.start; - let end = span.end; + while let Some(comment) = peekable_trivias.peek().copied() { + let start = comment.span.start; + let end = comment.span.end; let mut should_break = true; - let comment = Comment::new(start, end, kind); + let comment = Comment::new(start, end, comment.kind); if range.end < comment.start && self.source_text[range.end as usize..comment.start as usize] @@ -70,10 +70,10 @@ impl<'a> Prettier<'a> { #[must_use] pub(crate) fn print_leading_comments(&mut self, range: Span) -> Option> { let mut parts = self.vec(); - while let Some((kind, span)) = self.trivias.peek().copied() { - let start = span.start; - let end = span.end; - let comment = Comment::new(start, end, kind); + while let Some(comment) = self.trivias.peek().copied() { + let start = comment.span.start; + let end = comment.span.end; + let comment = Comment::new(start, end, comment.kind); // Comment before the span if end <= range.start { self.trivias.next(); @@ -119,10 +119,10 @@ impl<'a> Prettier<'a> { pub(crate) fn print_trailing_comments(&mut self, range: Span) -> Option> { let mut parts = self.vec(); let mut previous_comment: Option = None; - while let Some((kind, span)) = self.trivias.peek().copied() { - let start = span.start; - let end = span.end; - let comment = Comment::new(start, end, kind); + while let Some(comment) = self.trivias.peek().copied() { + let start = comment.span.start; + let end = comment.span.end; + let comment = Comment::new(start, end, comment.kind); // Trailing comment if there is nothing in between. if range.end < comment.start && self.source_text[range.end as usize..comment.start as usize] @@ -186,10 +186,10 @@ impl<'a> Prettier<'a> { #[must_use] pub(crate) fn print_inner_comment(&mut self, range: Span) -> Vec<'a, Doc<'a>> { let mut parts = self.vec(); - while let Some((kind, span)) = self.trivias.peek().copied() { - let start = span.start; - let end = span.end; - let comment = Comment::new(start, end, kind); + while let Some(comment) = self.trivias.peek().copied() { + let start = comment.span.start; + let end = comment.span.end; + let comment = Comment::new(start, end, comment.kind); // Comment within the span if comment.start >= range.start && comment.end <= range.end { self.trivias.next(); @@ -209,10 +209,10 @@ impl<'a> Prettier<'a> { dangling_options: Option, ) -> Option> { let mut parts = vec![]; - while let Some((kind, span)) = self.trivias.peek().copied() { - let start = span.start; - let end = span.end; - let comment = Comment::new(start, end, kind); + while let Some(comment) = self.trivias.peek().copied() { + let start = comment.span.start; + let end = comment.span.end; + let comment = Comment::new(start, end, comment.kind); // Comment within the span if comment.end <= range.end { parts.push(self.print_comment(comment)); diff --git a/crates/oxc_prettier/src/lib.rs b/crates/oxc_prettier/src/lib.rs index d373fc9b8e4fa..47dc938c2c5d8 100644 --- a/crates/oxc_prettier/src/lib.rs +++ b/crates/oxc_prettier/src/lib.rs @@ -17,7 +17,7 @@ mod utils; use std::{iter::Peekable, vec}; use oxc_allocator::Allocator; -use oxc_ast::{ast::Program, AstKind, CommentKind, Trivias}; +use oxc_ast::{ast::Program, AstKind, Comment, Trivias}; use oxc_span::Span; use oxc_syntax::identifier::is_line_terminator; @@ -55,7 +55,7 @@ pub struct Prettier<'a> { options: PrettierOptions, /// A stack of comments that will be carefully placed in the right places. - trivias: Peekable>, + trivias: Peekable>, /// The stack of AST Nodes /// See @@ -84,7 +84,7 @@ impl<'a> Prettier<'a> { allocator, source_text, options, - trivias: trivias.comments().collect::>().into_iter().peekable(), + trivias: trivias.comments().copied().collect::>().into_iter().peekable(), stack: vec![], group_id_builder: GroupIdBuilder::default(), args: PrettierArgs::default(), diff --git a/crates/oxc_semantic/src/jsdoc/builder.rs b/crates/oxc_semantic/src/jsdoc/builder.rs index b3f1fc791f2a8..f61a308c44e8a 100644 --- a/crates/oxc_semantic/src/jsdoc/builder.rs +++ b/crates/oxc_semantic/src/jsdoc/builder.rs @@ -28,8 +28,8 @@ impl<'a> JSDocBuilder<'a> { let not_attached_docs = self .trivias .comments() - .filter(|(_, span)| !self.leading_comments_seen.contains(&span.start)) - .filter_map(|(kind, span)| self.parse_if_jsdoc_comment(kind, span)) + .filter(|comment| !self.leading_comments_seen.contains(&comment.span.start)) + .filter_map(|comment| self.parse_if_jsdoc_comment(comment.kind, comment.span)) .collect::>(); JSDocFinder::new(self.attached_docs, not_attached_docs) @@ -119,20 +119,18 @@ impl<'a> JSDocBuilder<'a> { let mut leading_comments = vec![]; // May be better to set range start for perf? // But once I tried, coverage tests start failing... - for (start, comment) in self.trivias.comments_range(..span.start) { - if self.leading_comments_seen.contains(start) { + for comment in self.trivias.comments_range(..span.start) { + if self.leading_comments_seen.contains(&comment.span.start) { continue; } - leading_comments.push((start, comment)); - self.leading_comments_seen.insert(*start); + leading_comments.push(comment); + self.leading_comments_seen.insert(comment.span.start); } let leading_jsdoc_comments = leading_comments .into_iter() - .filter_map(|(start, comment)| { - self.parse_if_jsdoc_comment(comment.kind, Span::new(*start, comment.end)) - }) + .filter_map(|comment| self.parse_if_jsdoc_comment(comment.kind, comment.span)) .collect::>(); if !leading_jsdoc_comments.is_empty() { diff --git a/crates/oxc_transformer/src/react/options.rs b/crates/oxc_transformer/src/react/options.rs index 3f184dde311e2..ab38da9647dde 100644 --- a/crates/oxc_transformer/src/react/options.rs +++ b/crates/oxc_transformer/src/react/options.rs @@ -144,8 +144,8 @@ impl ReactOptions { /// /// This behavior is aligned with babel. pub(crate) fn update_with_comments(&mut self, ctx: &TransformCtx) { - for (_, span) in ctx.trivias.comments() { - let mut comment = span.source_text(ctx.source_text).trim_start(); + for comment in ctx.trivias.comments() { + let mut comment = comment.span.source_text(ctx.source_text).trim_start(); // strip leading jsdoc comment `*` and then whitespaces while let Some(cur_comment) = comment.strip_prefix('*') { comment = cur_comment.trim_start(); diff --git a/crates/oxc_transformer/src/typescript/options.rs b/crates/oxc_transformer/src/typescript/options.rs index 547440be4824f..1a46484d2503f 100644 --- a/crates/oxc_transformer/src/typescript/options.rs +++ b/crates/oxc_transformer/src/typescript/options.rs @@ -55,8 +55,8 @@ impl TypeScriptOptions { /// /// This behavior is aligned with babel. pub(crate) fn update_with_comments(mut self, ctx: &TransformCtx) -> Self { - for (_, span) in ctx.trivias.comments() { - let mut comment = span.source_text(ctx.source_text).trim_start(); + for comment in ctx.trivias.comments() { + let mut comment = comment.span.source_text(ctx.source_text).trim_start(); // strip leading jsdoc comment `*` and then whitespaces while let Some(cur_comment) = comment.strip_prefix('*') { comment = cur_comment.trim_start(); diff --git a/crates/oxc_wasm/src/lib.rs b/crates/oxc_wasm/src/lib.rs index 1065845d72d1c..e55d0edc043eb 100644 --- a/crates/oxc_wasm/src/lib.rs +++ b/crates/oxc_wasm/src/lib.rs @@ -340,14 +340,14 @@ impl Oxc { fn map_comments(&self, trivias: &Trivias) -> Vec { trivias .comments() - .map(|(kind, span)| Comment { - r#type: match kind { + .map(|comment| Comment { + r#type: match comment.kind { CommentKind::SingleLine => CommentType::Line, CommentKind::MultiLine => CommentType::Block, }, - value: span.source_text(&self.source_text).to_string(), - start: span.start, - end: span.end, + value: comment.span.source_text(&self.source_text).to_string(), + start: comment.span.start, + end: comment.span.end, }) .collect() } diff --git a/napi/parser/src/lib.rs b/napi/parser/src/lib.rs index d219918848ed2..b7e3e7927812e 100644 --- a/napi/parser/src/lib.rs +++ b/napi/parser/src/lib.rs @@ -104,14 +104,14 @@ fn parse_with_return<'a>(source_text: &'a str, options: &ParserOptions) -> Parse let comments = ret .trivias .comments() - .map(|(kind, span)| Comment { - r#type: match kind { + .map(|comment| Comment { + r#type: match comment.kind { CommentKind::SingleLine => "Line", CommentKind::MultiLine => "Block", }, - value: span.source_text(source_text).to_string(), - start: span.start, - end: span.end, + value: comment.span.source_text(source_text).to_string(), + start: comment.span.start, + end: comment.span.end, }) .collect::>(); diff --git a/tasks/coverage/src/suite.rs b/tasks/coverage/src/suite.rs index 3bca755075ce3..9956b9c3b3bf9 100644 --- a/tasks/coverage/src/suite.rs +++ b/tasks/coverage/src/suite.rs @@ -444,10 +444,10 @@ pub trait Case: Sized + Sync + Send + UnwindSafe { fn check_comments(&self, trivias: &Trivias) -> Option { let mut uniq: HashSet = HashSet::new(); - for (_, span) in trivias.comments() { - if !uniq.insert(span) { + for comment in trivias.comments() { + if !uniq.insert(comment.span) { return Some(TestResult::DuplicatedComments( - span.source_text(self.code()).to_string(), + comment.span.source_text(self.code()).to_string(), )); } }