1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use crate::group;
use once_cell::sync::Lazy;
use rslint_errors::Span;
use rslint_lexer::SyntaxKind;
use rslint_parser::{
ast::{ArgList, Literal, LiteralKind},
AstNode, SyntaxNode, SyntaxNodeExt,
};
use rslint_regex::{validate_flags, EcmaVersion, Flags, Parser, Regex};
use std::sync::Mutex;
use std::{collections::HashMap, ops::Range};
type RegexResult = Result<(Regex, Range<usize>), (Range<usize>, String)>;
pub(crate) static REGEX_MAP: Lazy<Mutex<HashMap<Range<usize>, RegexResult>>> =
Lazy::new(|| Mutex::new(HashMap::new()));
group! {
regex,
no_invalid_regexp::NoInvalidRegexp,
simplify_regex::SimplifyRegex
}
pub(crate) fn maybe_parse_and_store_regex(
node: &SyntaxNode,
file_id: usize,
) -> Option<RegexResult> {
let mut map_handle = REGEX_MAP.lock().unwrap();
if let Some(r) = map_handle.get(&node.as_range()) {
return Some(r.to_owned());
}
let r = collect_regex_from_node(node, file_id)?;
map_handle.insert(node.as_range(), r.clone());
Some(r)
}
fn collect_regex_from_node(node: &SyntaxNode, file_id: usize) -> Option<RegexResult> {
match node.kind() {
SyntaxKind::NEW_EXPR | SyntaxKind::CALL_EXPR => {
let name = node.child_with_kind(SyntaxKind::NAME_REF);
if name.map_or(false, |x| x.text() == "RegExp") {
let mut args = node
.child_with_ast::<ArgList>()
.map(|x| x.args())
.into_iter()
.flatten();
let pat = args.next().and_then(|x| {
Some((
x.syntax().try_to::<Literal>()?.inner_string_text()?,
x.range(),
))
});
let flags = args.next().and_then(|x| {
Some((
x.syntax().try_to::<Literal>()?.inner_string_text()?,
x.range(),
))
});
if let Some((pat, range)) = pat {
let range = range.as_range();
let new_range = range.start + 1..range.end - 1;
let flags = if let Some((flags, flag_range)) = flags {
match validate_flags(&flags.to_string(), EcmaVersion::ES2021) {
Ok(f) => f,
Err(err) => {
return Some(Err((flag_range.as_range(), err)));
}
}
} else {
Flags::empty()
};
let pattern = &pat.to_string();
let parser = Parser::new_from_pattern_and_flags(
pattern,
file_id,
range.as_range().start + 1,
EcmaVersion::ES2021,
false,
flags,
);
Some(match parser.parse() {
Ok(r) => Ok((r, new_range)),
Err(err) => Err((err.span.as_range(), err.message)),
})
} else {
None
}
} else {
None
}
}
SyntaxKind::LITERAL if node.to::<Literal>().kind() == LiteralKind::Regex => {
let pattern = &node.text().to_string();
let parser = Parser::new(
pattern,
file_id,
node.as_range().start,
EcmaVersion::ES2021,
false,
);
let range = node.as_range();
let new_range = range.start + 1..range.end - 1;
let res = match parser {
Ok(p) => p.parse(),
Err(err) => {
return Some(Err((err.span.as_range(), err.message)));
}
};
Some(match res {
Ok(r) => Ok((r, new_range)),
Err(err) => Err((err.span.as_range(), err.message)),
})
}
_ => None,
}
}