diff --git a/crates/oxc_linter/src/utils/comment.rs b/crates/oxc_linter/src/utils/comment.rs index 14403950a8594..0ac749830715e 100644 --- a/crates/oxc_linter/src/utils/comment.rs +++ b/crates/oxc_linter/src/utils/comment.rs @@ -23,6 +23,92 @@ pub fn count_comment_lines(comment: &Comment, source_text: &str) -> usize { if line_has_just_comment(comment_end_line, "*/") { end_line += 1; } - end_line - start_line + end_line.saturating_sub(start_line) + } +} + +#[cfg(test)] +mod test { + use super::*; + use oxc_allocator::Allocator; + use oxc_parser::Parser; + use oxc_semantic::SemanticBuilder; + use oxc_span::SourceType; + + #[test] + fn test_count_comment_lines() { + let cases: Vec<(&str, SourceType, Vec)> = vec![ + // Line comments - standalone + ("// This is a comment\nlet x = 1;", SourceType::default(), vec![1]), + (" // This is a comment\nlet x = 1;", SourceType::default(), vec![1]), + // Line comments - with code on same line + ("let x = 1; // comment", SourceType::default(), vec![0]), + // Multiple line comments + ( + "// Comment 1\n// Comment 2\n// Comment 3\nlet x = 1;", + SourceType::default(), + vec![1, 1, 1], + ), + // Block comments - single line standalone + ("/* comment */\nlet x = 1;", SourceType::default(), vec![1]), + // Block comments - single line with code + ("let x = /* comment */ 1;", SourceType::default(), vec![0]), + // Block comments - empty + ("/**/\nlet x = 1;", SourceType::default(), vec![1]), + // Block comments - multiline with delimiters on own lines + ( + "/*\n * This is a\n * multi-line comment\n */\nlet x = 1;", + SourceType::default(), + vec![4], + ), + // Block comments - multiline with leading whitespace + (" /*\n * Comment\n */\nlet x = 1;", SourceType::default(), vec![3]), + // Block comments - multiline with start delimiter sharing line with code + ("let y = 2; /*\n * Comment\n */\nlet x = 1;", SourceType::default(), vec![2]), + // Block comments - multiline with end delimiter sharing line with code + ("/*\n * Comment\n */ let x = 1;", SourceType::default(), vec![2]), + // Block comments - multiline with both delimiters sharing lines with code + ("let y = 2; /*\n * Comment\n */ let x = 1;", SourceType::default(), vec![1]), + // JSX comments + ( + r"export function Component() { + // hello + /* + cons + */ + return ( +
+ {/* hello */} + content +
+ ); +}", + SourceType::tsx(), + vec![1, 3, 0], + ), + ]; + + for (source, source_type, expected_counts) in cases { + let allocator = Allocator::default(); + let ret = Parser::new(&allocator, source, source_type).parse(); + let semantic = SemanticBuilder::new().build(&ret.program).semantic; + let comments = semantic.comments(); + + assert_eq!( + comments.len(), + expected_counts.len(), + "Expected {} comments in source: {:?}", + expected_counts.len(), + source + ); + + for (i, expected_count) in expected_counts.iter().enumerate() { + let actual_count = count_comment_lines(&comments[i], source); + assert_eq!( + actual_count, *expected_count, + "Comment {i} in source {source:?} expected {expected_count} lines but got {actual_count}", + ); + } + } } }