Skip to content

Commit 5ae3f36

Browse files
committedSep 24, 2024·
perf(linter): no-fallthrough: Use string matching instead of Regex for default comment pattern (#6008)
Profiling has shown this rule to be a consistent slow point, and in particular, the Regex construction is slow. <img width="1323" alt="Screenshot 2024-09-23 at 7 12 58 PM" src="https://github.com/user-attachments/assets/1d9b367d-eeda-4b19-b0a3-463e54ac4334"> This PR improves the situation in two ways: 1. A `Regex` is only constructed when there is a custom comment pattern (which is likely the minority of cases) 2. For the default comment pattern (when no custom pattern is passed), we now use a simple string matcher function, instead of a full-blown regex matcher. I believe this should be faster since it involves much less work, though can still allocate a `String`.
1 parent 5a0d17c commit 5ae3f36

File tree

1 file changed

+25
-9
lines changed

1 file changed

+25
-9
lines changed
 

‎crates/oxc_linter/src/rules/eslint/no_fallthrough.rs

+25-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::ops::Range;
22

3+
use cow_utils::CowUtils;
34
use itertools::Itertools;
45
use oxc_ast::{
56
ast::{Statement, SwitchCase, SwitchStatement},
@@ -35,11 +36,12 @@ fn no_unused_fallthrough_diagnostic(span: Span) -> OxcDiagnostic {
3536
.with_label(span)
3637
}
3738

38-
const DEFAULT_FALLTHROUGH_COMMENT_PATTERN: &str = r"falls?\s?through";
39-
4039
#[derive(Debug, Clone)]
4140
struct Config {
42-
comment_pattern: Regex,
41+
/// The custom comment pattern to match against. If set to None, the rule
42+
/// will use the default pattern. Otherwise, if this is Some, the rule will
43+
/// use the provided pattern.
44+
comment_pattern: Option<Regex>,
4345
allow_empty_case: bool,
4446
report_unused_fallthrough_comment: bool,
4547
}
@@ -53,9 +55,9 @@ impl NoFallthrough {
5355
allow_empty_case: Option<bool>,
5456
report_unused_fallthrough_comment: Option<bool>,
5557
) -> Self {
56-
let comment_pattern = comment_pattern.unwrap_or(DEFAULT_FALLTHROUGH_COMMENT_PATTERN);
5758
Self(Box::new(Config {
58-
comment_pattern: Regex::new(format!("(?iu){comment_pattern}").as_str()).unwrap(),
59+
comment_pattern: comment_pattern
60+
.map(|pattern| Regex::new(format!("(?iu){pattern}").as_str()).unwrap()),
5961
allow_empty_case: allow_empty_case.unwrap_or(false),
6062
report_unused_fallthrough_comment: report_unused_fallthrough_comment.unwrap_or(false),
6163
}))
@@ -387,10 +389,7 @@ impl NoFallthrough {
387389
.last()
388390
.map(str::trim);
389391

390-
comment.is_some_and(|comment| {
391-
(!comment.starts_with("oxlint-") && !comment.starts_with("eslint-"))
392-
&& self.0.comment_pattern.is_match(comment)
393-
})
392+
comment.is_some_and(|comment| self.is_comment_fall_through(comment))
394393
};
395394

396395
let (start, end) = possible_fallthrough_comment_span(case);
@@ -409,6 +408,23 @@ impl NoFallthrough {
409408
None
410409
}
411410
}
411+
412+
fn is_comment_fall_through(&self, comment: &str) -> bool {
413+
if comment.starts_with("oxlint-") || comment.starts_with("eslint-") {
414+
return false;
415+
}
416+
if let Some(custom_pattern) = &self.0.comment_pattern {
417+
custom_pattern.is_match(comment)
418+
} else {
419+
// We are doing a quick check here to see if it starts with the expected "falls" comment,
420+
// so that we don't need to initialize the pattern matcher if we don't need it.
421+
let comment = comment.trim().cow_to_ascii_lowercase();
422+
comment == "falls through"
423+
|| comment == "fall through"
424+
|| comment == "fallsthrough"
425+
|| comment == "fallthrough"
426+
}
427+
}
412428
}
413429

414430
/// Get semantic information about a switch cases and its exit point.

0 commit comments

Comments
 (0)
Please sign in to comment.