From a969f6962740148331c3cb66986fbb9f026c1d85 Mon Sep 17 00:00:00 2001 From: Devin-Yeung <53309384+Devin-Yeung@users.noreply.github.com> Date: Sun, 3 Sep 2023 14:13:16 +0800 Subject: [PATCH] perf(linter): parse ts-directive manually (#845) --- .../src/rules/typescript/ban_ts_comment.rs | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) 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 ef061e04a..b8cd949fa 100644 --- a/crates/oxc_linter/src/rules/typescript/ban_ts_comment.rs +++ b/crates/oxc_linter/src/rules/typescript/ban_ts_comment.rs @@ -1,4 +1,3 @@ -use once_cell::sync::Lazy; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::{self, Error}, @@ -126,13 +125,11 @@ impl Rule for BanTsComment { fn run_once(&self, ctx: &LintContext) { let comments = ctx.semantic().trivias().comments(); for (start, comment) in comments { - let regex = if comment.is_single_line() { &SINGLELINE } else { &MULTILINE }; let raw = &ctx.semantic().source_text()[*start as usize..comment.end() as usize]; - if let Some(captures) = regex.captures(raw) { + if let Some(captures) = find_ts_comment_directive(raw, comment.is_single_line()) { // safe to unwrap, if capture success, it can always capture one of the four directives - let directive = captures.name("directive").unwrap().as_str(); - let description = captures.name("description").map_or("", |m| m.as_str().trim()); + let (directive, description) = (captures.0, captures.1.trim()); match self.option(directive) { DirectiveConfig::Boolean(on) => { @@ -170,19 +167,6 @@ impl Rule for BanTsComment { } } -// regex to parse single line comment -static SINGLELINE: Lazy = Lazy::new(|| { - Regex::new(r"^/*\s*@ts-(?Pexpect-error|ignore|check|nocheck)(?P.*)") - .unwrap() -}); -// regex to parse multiline comment -static MULTILINE: Lazy = Lazy::new(|| { - Regex::new( - r"^\s*(?:/|\*)*\s*@ts-(?Pexpect-error|ignore|check|nocheck)(?P.*)", - ) - .unwrap() -}); - impl BanTsComment { /// get the option for a given directive, caller should guarantee /// the directive should be one of the ignore/check/nocheck/expect-error @@ -197,6 +181,30 @@ impl BanTsComment { } } +pub fn find_ts_comment_directive(raw: &str, single_line: bool) -> Option<(&str, &str)> { + let prefix = "@ts-"; + // For multi-line comments, get the last line + let mut lines = raw.lines(); + let line = lines.next_back()?; + let index = line.find(prefix)?; + if !line[..index] + .chars() + .all(|c| c.is_whitespace() || if single_line { c == '/' } else { c == '*' || c == '/' }) + { + return None; + } + let multi_len = lines.map(|line| line.len() + 1).sum::(); + let start = index + prefix.len(); + for directive in ["expect-error", "ignore", "nocheck", "check"] { + if line.get(start..start + directive.len()) == Some(directive) { + let start = multi_len + index + prefix.len(); + let end = start + directive.len(); + return Some((&raw[start..end], &raw[end..])); + } + } + None +} + #[test] #[allow(clippy::too_many_lines)] fn test() {