1use crate::RuleId;
4
5#[derive(Clone, Copy, Debug, Eq, PartialEq)]
7#[non_exhaustive]
8pub struct CallRecognition {
9 pub receiver: Option<&'static str>,
11 pub member: &'static str,
13 pub rule: RuleId,
15}
16
17#[derive(Clone, Copy, Debug, Eq, PartialEq)]
19#[non_exhaustive]
20pub enum TypeScriptReceiverKind {
21 Map,
23 Set,
25}
26
27#[derive(Clone, Copy, Debug, Eq, PartialEq)]
29#[non_exhaustive]
30pub struct MethodRecognition {
31 pub receiver: TypeScriptReceiverKind,
33 pub member: &'static str,
35 pub rule: RuleId,
37}
38
39pub const TYPESCRIPT_CALLS: &[CallRecognition] = &[
44 free("fetch", RuleId::TsFetch),
45 free("structuredClone", RuleId::TsStructuredClone),
46 free("String", RuleId::TsPrimitiveCast),
47 free("Number", RuleId::TsPrimitiveCast),
48 free("Boolean", RuleId::TsPrimitiveCast),
49 free("BigInt", RuleId::TsPrimitiveCast),
50 free("parseFloat", RuleId::TsPrimitiveCast),
51 free("parseInt", RuleId::TsPrimitiveCast),
52 free("isNaN", RuleId::TsNumberPredicate),
53 free("Symbol", RuleId::TsSymbol),
54 static_call("JSON", "stringify", RuleId::TsJsonStringify),
55 static_call("JSON", "parse", RuleId::TsJsonParse),
56 static_call("Date", "now", RuleId::TsDateNow),
57 static_call("Math", "random", RuleId::TsMathRandom),
58 static_call("Math", "abs", RuleId::TsMathNumeric),
59 static_call("Math", "floor", RuleId::TsMathNumeric),
60 static_call("Math", "ceil", RuleId::TsMathNumeric),
61 static_call("Math", "round", RuleId::TsMathNumeric),
62 static_call("Math", "trunc", RuleId::TsMathNumeric),
63 static_call("Math", "max", RuleId::TsMathNumeric),
64 static_call("Math", "min", RuleId::TsMathNumeric),
65 static_call("Math", "hypot", RuleId::TsMathNumeric),
66 static_call("Math", "sqrt", RuleId::TsMathNumeric),
67 static_call("Math", "cbrt", RuleId::TsMathNumeric),
68 static_call("Math", "sign", RuleId::TsMathNumeric),
69 static_call("Math", "sin", RuleId::TsMathNumeric),
70 static_call("Math", "cos", RuleId::TsMathNumeric),
71 static_call("Math", "tan", RuleId::TsMathNumeric),
72 static_call("Math", "asin", RuleId::TsMathNumeric),
73 static_call("Math", "acos", RuleId::TsMathNumeric),
74 static_call("Math", "atan", RuleId::TsMathNumeric),
75 static_call("Math", "log", RuleId::TsMathNumeric),
76 static_call("Math", "log10", RuleId::TsMathNumeric),
77 static_call("Math", "log2", RuleId::TsMathNumeric),
78 static_call("Math", "exp", RuleId::TsMathNumeric),
79 static_call("Math", "pow", RuleId::TsMathNumeric),
80 static_call("Math", "atan2", RuleId::TsMathNumeric),
81 static_call("Number", "isFinite", RuleId::TsNumberPredicate),
82 static_call("Number", "isInteger", RuleId::TsNumberPredicate),
83 static_call("Number", "isNaN", RuleId::TsNumberPredicate),
84 static_call("Number", "parseFloat", RuleId::TsNumberParseFloat),
85 static_call("Number", "parseInt", RuleId::TsNumberParseInt),
86 static_call("Promise", "resolve", RuleId::TsPromiseStatic),
87 static_call("Promise", "all", RuleId::TsPromiseStatic),
88 static_call("Promise", "race", RuleId::TsPromiseStatic),
89 static_call("Promise", "allSettled", RuleId::TsPromiseStatic),
90 static_call("Symbol", "for", RuleId::TsSymbol),
91 static_call("Object", "is", RuleId::TsObjectStatic),
92 static_call("Object", "fromEntries", RuleId::TsObjectStatic),
93 static_call("Object", "create", RuleId::TsObjectStatic),
94 static_call("Object", "getPrototypeOf", RuleId::TsObjectStatic),
95 static_call("Array", "isArray", RuleId::TsArrayStatic),
96 static_call("Array", "from", RuleId::TsArrayStatic),
97 static_call("ArrayBuffer", "isView", RuleId::TsArrayStatic),
98 static_call("Buffer", "from", RuleId::TsBufferStatic),
99 static_call("Buffer", "alloc", RuleId::TsBufferStatic),
100 static_call("Buffer", "concat", RuleId::TsBufferStatic),
101 static_call("Buffer", "isBuffer", RuleId::TsBufferStatic),
102];
103
104pub const TYPESCRIPT_METHODS: &[MethodRecognition] = &[
106 method(TypeScriptReceiverKind::Map, "has", RuleId::TsMapHas),
107 method(TypeScriptReceiverKind::Map, "get", RuleId::TsMapGet),
108 method(TypeScriptReceiverKind::Map, "set", RuleId::TsMapMutation),
109 method(TypeScriptReceiverKind::Map, "delete", RuleId::TsMapMutation),
110 method(TypeScriptReceiverKind::Map, "clear", RuleId::TsMapMutation),
111 method(TypeScriptReceiverKind::Map, "keys", RuleId::TsMapProjection),
112 method(
113 TypeScriptReceiverKind::Map,
114 "values",
115 RuleId::TsMapProjection,
116 ),
117 method(
118 TypeScriptReceiverKind::Map,
119 "entries",
120 RuleId::TsMapProjection,
121 ),
122 method(TypeScriptReceiverKind::Set, "has", RuleId::TsSetHas),
123 method(TypeScriptReceiverKind::Set, "add", RuleId::TsSetMutation),
124 method(TypeScriptReceiverKind::Set, "delete", RuleId::TsSetMutation),
125 method(TypeScriptReceiverKind::Set, "clear", RuleId::TsSetMutation),
126 method(TypeScriptReceiverKind::Set, "keys", RuleId::TsSetProjection),
127 method(
128 TypeScriptReceiverKind::Set,
129 "values",
130 RuleId::TsSetProjection,
131 ),
132 method(
133 TypeScriptReceiverKind::Set,
134 "entries",
135 RuleId::TsSetProjection,
136 ),
137];
138
139#[must_use]
141pub fn typescript_call_rule(receiver: Option<&str>, member: &str) -> Option<RuleId> {
142 TYPESCRIPT_CALLS
143 .iter()
144 .find(|entry| entry.receiver == receiver && entry.member == member)
145 .map(|entry| entry.rule)
146}
147
148#[must_use]
150pub fn typescript_method_rule(receiver: TypeScriptReceiverKind, member: &str) -> Option<RuleId> {
151 TYPESCRIPT_METHODS
152 .iter()
153 .find(|entry| entry.receiver == receiver && entry.member == member)
154 .map(|entry| entry.rule)
155}
156
157const fn free(member: &'static str, rule: RuleId) -> CallRecognition {
159 CallRecognition {
160 receiver: None,
161 member,
162 rule,
163 }
164}
165
166const fn static_call(
168 receiver: &'static str,
169 member: &'static str,
170 rule: RuleId,
171) -> CallRecognition {
172 CallRecognition {
173 receiver: Some(receiver),
174 member,
175 rule,
176 }
177}
178
179const fn method(
181 receiver: TypeScriptReceiverKind,
182 member: &'static str,
183 rule: RuleId,
184) -> MethodRecognition {
185 MethodRecognition {
186 receiver,
187 member,
188 rule,
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195
196 #[test]
198 fn recognizes_exact_typescript_calls() {
199 let cases = [
200 ((None, "structuredClone"), RuleId::TsStructuredClone),
201 ((Some("JSON"), "parse"), RuleId::TsJsonParse),
202 ((Some("Math"), "floor"), RuleId::TsMathNumeric),
203 ((Some("Number"), "isNaN"), RuleId::TsNumberPredicate),
204 ((Some("Promise"), "allSettled"), RuleId::TsPromiseStatic),
205 ((Some("Object"), "fromEntries"), RuleId::TsObjectStatic),
206 ((Some("Object"), "create"), RuleId::TsObjectStatic),
207 ((Some("Array"), "isArray"), RuleId::TsArrayStatic),
208 ];
209
210 for ((receiver, member), expected) in cases {
211 assert_eq!(typescript_call_rule(receiver, member), Some(expected));
212 }
213 }
214
215 #[test]
217 fn rejects_similar_unsupported_typescript_calls() {
218 let cases = [
219 (None, "fetcher"),
220 (Some("json"), "parse"),
221 (Some("Math"), "randomBytes"),
222 (Some("Number"), "parseDouble"),
223 (Some("Promise"), "any"),
224 (Some("Object"), "entries"),
225 (Some("Array"), "of"),
226 (Some("value"), "map"),
227 ];
228
229 for (receiver, member) in cases {
230 assert_eq!(typescript_call_rule(receiver, member), None);
231 }
232 }
233
234 #[test]
236 fn recognizes_typescript_receiver_methods() {
237 let cases = [
238 (TypeScriptReceiverKind::Map, "has", Some(RuleId::TsMapHas)),
239 (TypeScriptReceiverKind::Map, "get", Some(RuleId::TsMapGet)),
240 (
241 TypeScriptReceiverKind::Map,
242 "set",
243 Some(RuleId::TsMapMutation),
244 ),
245 (
246 TypeScriptReceiverKind::Map,
247 "entries",
248 Some(RuleId::TsMapProjection),
249 ),
250 (TypeScriptReceiverKind::Set, "has", Some(RuleId::TsSetHas)),
251 (
252 TypeScriptReceiverKind::Set,
253 "add",
254 Some(RuleId::TsSetMutation),
255 ),
256 (
257 TypeScriptReceiverKind::Set,
258 "values",
259 Some(RuleId::TsSetProjection),
260 ),
261 (TypeScriptReceiverKind::Set, "get", None),
262 (TypeScriptReceiverKind::Map, "add", None),
263 ];
264
265 for (receiver, member, expected) in cases {
266 assert_eq!(typescript_method_rule(receiver, member), expected);
267 }
268 }
269}