Skip to content

Commit

Permalink
refactor(restricted-regex): accept non-capturing groups with flags
Browse files Browse the repository at this point in the history
  • Loading branch information
Conaclos committed Oct 19, 2024
1 parent 8081745 commit 0dc4588
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 5 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b

Contributed by @Conaclos

- [useFilenamingConvention](https://biomejs.dev/linter/rules/use-filenaming-convention) and [useNamingConvention](https://biomejs.dev/linter/rules/use-naming-convention) `match` options now accept case-insensitive and case-sensitive groups.

By default, the regular expression in `match` is case-sensitive.
You can now make it case-insensitive by using a case-insensitive group `(?i:)`.
For example, the regular expression `(?i:a)` matches `a` and `A`.

Contributed by @Conaclos

### Parser

#### New features
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ declare_lint_rule! {
/// - Alternations `|`
/// - Capturing groups `()`
/// - Non-capturing groups `(?:)`
/// - Case-insensitive groups `(?i:)` and case-sensitive groups `(?-i:)`
/// - A limited set of escaped characters including all special characters
/// and regular string escape characters `\f`, `\n`, `\r`, `\t`, `\v`
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,7 @@ declare_lint_rule! {
/// - Alternations `|`
/// - Capturing groups `()`
/// - Non-capturing groups `(?:)`
/// - Case-insensitive groups `(?i:)` and case-sensitive groups `(?-i:)`
/// - A limited set of escaped characters including all special characters
/// and regular string escape characters `\f`, `\n`, `\r`, `\t`, `\v`
///
Expand Down
38 changes: 33 additions & 5 deletions crates/biome_js_analyze/src/utils/restricted_regex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ use biome_deserialize_macros::Deserializable;
/// - Alternations `|`
/// - Capturing groups `()`
/// - Non-capturing groups `(?:)`
/// - Non-capturing groups with flags `(?flags:)` and negated flags `(?-flags:)`
/// Supported flags:
/// - `i`: ignore case
/// - `m`: multiline mode
/// - `s`: single line mode (`.` matches also `\n`)
/// - A limited set of escaped characters including all regex special characters
/// and regular string escape characters `\f`, `\n`, `\r`, `\t`, `\v`
///
Expand Down Expand Up @@ -173,10 +178,31 @@ fn is_restricted_regex(pattern: &str) -> Result<(), regex::Error> {
};
}
Some(b':') => {}
_ => {
return Err(regex::Error::Syntax(
"Group flags `(?flags:)` are not supported.".to_string(),
));
c => {
let mut current = c;
while matches!(current, Some(b'i' | b'm' | b's' | b'-')) {
current = it.next()
}
match current {
Some(b':') => {}
Some(b')') => {
return Err(regex::Error::Syntax(
"Group modifiers `(?flags)` are not supported.".to_string(),
));
}
Some(c) if c.is_ascii() => {
// SAFETY: `c` is ASCII according to the guard
let c = c as char;
return Err(regex::Error::Syntax(format!(
"Group flags `(?{c}:)` are not supported."
)));
}
_ => {
return Err(regex::Error::Syntax(
"Unterminated non-capturing group.".to_string(),
));
}
}
}
},
_ => {}
Expand All @@ -197,7 +223,6 @@ mod tests {
assert!(is_restricted_regex("a$").is_err());
assert!(is_restricted_regex(r"\").is_err());
assert!(is_restricted_regex(r"\p{L}").is_err());
assert!(is_restricted_regex(r"(?i:)").is_err());
assert!(is_restricted_regex(r"(?=a)").is_err());
assert!(is_restricted_regex(r"(?!a)").is_err());
assert!(is_restricted_regex(r"(?<NAME>:a)").is_err());
Expand All @@ -210,6 +235,9 @@ mod tests {
assert!(is_restricted_regex("").is_ok());
assert!(is_restricted_regex("abc").is_ok());
assert!(is_restricted_regex("(?:a)(.+)z").is_ok());
assert!(is_restricted_regex("(?ims:a)(.+)z").is_ok());
assert!(is_restricted_regex("(?-ims:a)(.+)z").is_ok());
assert!(is_restricted_regex("(?i-ms:a)(.+)z").is_ok());
assert!(is_restricted_regex("[A-Z][^a-z]").is_ok());
assert!(is_restricted_regex(r"\n\t\v\f").is_ok());
assert!(is_restricted_regex("([^_])").is_ok());
Expand Down

0 comments on commit 0dc4588

Please sign in to comment.