1use oxc_ast::ast::*;
2
3use crate::GlobalContext;
4
5pub trait IsLiteralValue<'a, 'b> {
20 fn is_literal_value(&self, include_functions: bool, ctx: &impl GlobalContext<'a>) -> bool;
21}
22
23impl<'a> IsLiteralValue<'a, '_> for Expression<'a> {
24 fn is_literal_value(&self, include_functions: bool, ctx: &impl GlobalContext<'a>) -> bool {
25 match self {
26 Self::BooleanLiteral(_)
27 | Self::NullLiteral(_)
28 | Self::NumericLiteral(_)
29 | Self::BigIntLiteral(_)
30 | Self::RegExpLiteral(_)
31 | Self::StringLiteral(_) => true,
32 Self::TemplateLiteral(lit) => lit.is_literal_value(include_functions, ctx),
33 Self::Identifier(ident) => {
34 matches!(ident.name.as_str(), "undefined" | "Infinity" | "NaN")
35 && ctx.is_global_reference(ident)
36 }
37 Self::ArrayExpression(expr) => expr.is_literal_value(include_functions, ctx),
38 Self::ObjectExpression(expr) => expr.is_literal_value(include_functions, ctx),
39 Self::FunctionExpression(_) | Self::ArrowFunctionExpression(_) => include_functions,
40 Self::UnaryExpression(e) => e.is_literal_value(include_functions, ctx),
41 Self::BinaryExpression(e) => e.is_literal_value(include_functions, ctx),
42 Self::LogicalExpression(e) => {
43 e.left.is_literal_value(include_functions, ctx)
44 && e.right.is_literal_value(include_functions, ctx)
45 }
46 Self::ConditionalExpression(e) => {
47 e.test.is_literal_value(include_functions, ctx)
48 && e.consequent.is_literal_value(include_functions, ctx)
49 && e.alternate.is_literal_value(include_functions, ctx)
50 }
51 Self::ParenthesizedExpression(e) => {
52 e.expression.is_literal_value(include_functions, ctx)
53 }
54 Self::SequenceExpression(e) => {
55 e.expressions.iter().all(|expr| expr.is_literal_value(include_functions, ctx))
56 }
57 _ => false,
58 }
59 }
60}
61
62impl<'a> IsLiteralValue<'a, '_> for TemplateLiteral<'a> {
63 fn is_literal_value(&self, _include_functions: bool, _ctx: &impl GlobalContext<'a>) -> bool {
64 self.is_no_substitution_template()
65 }
66}
67
68impl<'a> IsLiteralValue<'a, '_> for ArrayExpression<'a> {
69 fn is_literal_value(&self, include_functions: bool, ctx: &impl GlobalContext<'a>) -> bool {
70 self.elements.iter().all(|element| element.is_literal_value(include_functions, ctx))
71 }
72}
73
74impl<'a> IsLiteralValue<'a, '_> for ObjectExpression<'a> {
75 fn is_literal_value(&self, include_functions: bool, ctx: &impl GlobalContext<'a>) -> bool {
76 self.properties.iter().all(|property| property.is_literal_value(include_functions, ctx))
77 }
78}
79
80impl<'a> IsLiteralValue<'a, '_> for UnaryExpression<'a> {
81 fn is_literal_value(&self, include_functions: bool, ctx: &impl GlobalContext<'a>) -> bool {
82 match self.operator {
83 UnaryOperator::Void | UnaryOperator::LogicalNot | UnaryOperator::Typeof => {
84 self.argument.is_literal_value(include_functions, ctx)
85 }
86 UnaryOperator::UnaryPlus => {
87 can_convert_to_number_transparently(&self.argument, include_functions, ctx)
88 }
89 UnaryOperator::UnaryNegation | UnaryOperator::BitwiseNot => {
90 can_convert_to_number_transparently(&self.argument, include_functions, ctx)
91 || matches!(self.argument, Expression::BigIntLiteral(_))
92 }
93 UnaryOperator::Delete => false,
94 }
95 }
96}
97
98impl<'a> IsLiteralValue<'a, '_> for BinaryExpression<'a> {
99 fn is_literal_value(&self, include_functions: bool, ctx: &impl GlobalContext<'a>) -> bool {
100 match self.operator {
101 BinaryOperator::StrictEquality | BinaryOperator::StrictInequality => {
102 self.left.is_literal_value(include_functions, ctx)
103 && self.right.is_literal_value(include_functions, ctx)
104 }
105 BinaryOperator::Addition => {
106 if (is_immutable_string(&self.left, include_functions, ctx)
107 && can_convert_to_string_transparently(&self.right, include_functions, ctx))
108 || (is_immutable_string(&self.right, include_functions, ctx)
109 && can_convert_to_string_transparently(&self.left, include_functions, ctx))
110 {
111 return true;
112 }
113 (matches!(&self.left, Expression::NumericLiteral(_))
114 && matches!(&self.right, Expression::NumericLiteral(_)))
115 | (matches!(&self.left, Expression::BigIntLiteral(_))
116 && matches!(&self.right, Expression::BigIntLiteral(_)))
117 }
118 BinaryOperator::Subtraction
119 | BinaryOperator::Multiplication
120 | BinaryOperator::Division
121 | BinaryOperator::Remainder
122 | BinaryOperator::Exponential
123 | BinaryOperator::ShiftLeft
124 | BinaryOperator::ShiftRight
125 | BinaryOperator::ShiftRightZeroFill
126 | BinaryOperator::BitwiseOR
127 | BinaryOperator::BitwiseXOR
128 | BinaryOperator::BitwiseAnd => {
129 if (matches!(&self.left, Expression::NumericLiteral(_))
130 && can_convert_to_number_transparently(&self.right, include_functions, ctx))
131 || (matches!(&self.right, Expression::NumericLiteral(_))
132 && can_convert_to_number_transparently(&self.left, include_functions, ctx))
133 {
134 return true;
135 }
136 let (Expression::BigIntLiteral(_), Expression::BigIntLiteral(right)) =
137 (&self.left, &self.right)
138 else {
139 return false;
140 };
141 match self.operator {
143 BinaryOperator::ShiftRightZeroFill => false,
144 BinaryOperator::Exponential => !right.is_negative(),
145 BinaryOperator::Division | BinaryOperator::Remainder => !right.is_zero(),
146 _ => true,
147 }
148 }
149 BinaryOperator::LessThan
150 | BinaryOperator::LessEqualThan
151 | BinaryOperator::GreaterThan
152 | BinaryOperator::GreaterEqualThan
153 | BinaryOperator::Equality
154 | BinaryOperator::Inequality
155 | BinaryOperator::In
156 | BinaryOperator::Instanceof => false,
157 }
158 }
159}
160
161impl<'a> IsLiteralValue<'a, '_> for ArrayExpressionElement<'a> {
162 fn is_literal_value(&self, include_functions: bool, ctx: &impl GlobalContext<'a>) -> bool {
163 match self {
164 Self::SpreadElement(_) => false,
166 Self::Elision(_) => true,
167 match_expression!(Self) => {
168 self.to_expression().is_literal_value(include_functions, ctx)
169 }
170 }
171 }
172}
173
174impl<'a> IsLiteralValue<'a, '_> for ObjectPropertyKind<'a> {
175 fn is_literal_value(&self, include_functions: bool, ctx: &impl GlobalContext<'a>) -> bool {
176 match self {
177 Self::ObjectProperty(property) => property.is_literal_value(include_functions, ctx),
178 Self::SpreadProperty(property) => match &property.argument {
179 Expression::ArrayExpression(expr) => expr.is_literal_value(include_functions, ctx),
180 Expression::StringLiteral(_) => true,
181 Expression::TemplateLiteral(lit) => lit.is_literal_value(include_functions, ctx),
182 Expression::ObjectExpression(expr) => expr.is_literal_value(include_functions, ctx),
183 _ => false,
184 },
185 }
186 }
187}
188
189impl<'a> IsLiteralValue<'a, '_> for ObjectProperty<'a> {
190 fn is_literal_value(&self, include_functions: bool, ctx: &impl GlobalContext<'a>) -> bool {
191 self.key.is_literal_value(include_functions, ctx)
192 && self.value.is_literal_value(include_functions, ctx)
193 }
194}
195
196impl<'a> IsLiteralValue<'a, '_> for PropertyKey<'a> {
197 fn is_literal_value(&self, include_functions: bool, ctx: &impl GlobalContext<'a>) -> bool {
198 match self {
199 Self::StaticIdentifier(_) => true,
200 Self::PrivateIdentifier(_) => false,
201 match_expression!(Self) => {
202 can_convert_to_string_transparently(self.to_expression(), include_functions, ctx)
203 }
204 }
205 }
206}
207
208fn can_convert_to_number_transparently<'a>(
209 expr: &Expression<'a>,
210 include_functions: bool,
211 ctx: &impl GlobalContext<'a>,
212) -> bool {
213 match expr {
214 Expression::NumericLiteral(_)
215 | Expression::NullLiteral(_)
216 | Expression::BooleanLiteral(_)
217 | Expression::StringLiteral(_) => true,
218 Expression::TemplateLiteral(lit) => lit.is_literal_value(include_functions, ctx),
219 Expression::Identifier(ident) => {
220 matches!(ident.name.as_str(), "undefined" | "Infinity" | "NaN")
221 && ctx.is_global_reference(ident)
222 }
223 Expression::ArrowFunctionExpression(_) | Expression::FunctionExpression(_) => {
224 include_functions
225 }
226 Expression::UnaryExpression(e) => match e.operator {
227 UnaryOperator::Void | UnaryOperator::LogicalNot | UnaryOperator::Typeof => {
228 e.argument.is_literal_value(include_functions, ctx)
229 }
230 UnaryOperator::UnaryPlus | UnaryOperator::UnaryNegation | UnaryOperator::BitwiseNot => {
231 can_convert_to_number_transparently(&e.argument, include_functions, ctx)
232 }
233 UnaryOperator::Delete => false,
234 },
235 Expression::BinaryExpression(e) => match e.operator {
236 BinaryOperator::StrictEquality | BinaryOperator::StrictInequality => {
237 e.left.is_literal_value(include_functions, ctx)
238 && e.right.is_literal_value(include_functions, ctx)
239 }
240 BinaryOperator::Addition => {
241 if (is_immutable_string(&e.left, include_functions, ctx)
242 && can_convert_to_string_transparently(&e.right, include_functions, ctx))
243 || (is_immutable_string(&e.right, include_functions, ctx)
244 && can_convert_to_string_transparently(&e.left, include_functions, ctx))
245 {
246 return true;
247 }
248 (matches!(&e.left, Expression::NumericLiteral(_))
249 && matches!(&e.right, Expression::NumericLiteral(_)))
250 | (matches!(&e.left, Expression::BigIntLiteral(_))
251 && matches!(&e.right, Expression::BigIntLiteral(_)))
252 }
253 BinaryOperator::Subtraction
254 | BinaryOperator::Multiplication
255 | BinaryOperator::Division
256 | BinaryOperator::Remainder
257 | BinaryOperator::Exponential
258 | BinaryOperator::ShiftLeft
259 | BinaryOperator::ShiftRight
260 | BinaryOperator::ShiftRightZeroFill
261 | BinaryOperator::BitwiseOR
262 | BinaryOperator::BitwiseXOR
263 | BinaryOperator::BitwiseAnd => {
264 if (matches!(&e.left, Expression::NumericLiteral(_))
265 && can_convert_to_number_transparently(&e.right, include_functions, ctx))
266 || (matches!(&e.right, Expression::NumericLiteral(_))
267 && can_convert_to_number_transparently(&e.left, include_functions, ctx))
268 {
269 return true;
270 }
271 false
272 }
273 BinaryOperator::LessThan
274 | BinaryOperator::LessEqualThan
275 | BinaryOperator::GreaterThan
276 | BinaryOperator::GreaterEqualThan
277 | BinaryOperator::Equality
278 | BinaryOperator::Inequality
279 | BinaryOperator::In
280 | BinaryOperator::Instanceof => false,
281 },
282 Expression::LogicalExpression(e) => {
283 can_convert_to_number_transparently(&e.left, include_functions, ctx)
284 && can_convert_to_number_transparently(&e.right, include_functions, ctx)
285 }
286 Expression::ConditionalExpression(e) => {
287 e.test.is_literal_value(include_functions, ctx)
288 && can_convert_to_number_transparently(&e.consequent, include_functions, ctx)
289 && can_convert_to_number_transparently(&e.alternate, include_functions, ctx)
290 }
291 Expression::ParenthesizedExpression(e) => {
292 can_convert_to_number_transparently(&e.expression, include_functions, ctx)
293 }
294 Expression::SequenceExpression(e) => {
295 can_convert_to_number_transparently(
296 e.expressions.last().expect("should have at least one element"),
297 include_functions,
298 ctx,
299 ) && e
300 .expressions
301 .iter()
302 .rev()
303 .skip(1)
304 .all(|expr| expr.is_literal_value(include_functions, ctx))
305 }
306 _ => false,
307 }
308}
309
310fn can_convert_to_string_transparently<'a>(
311 expr: &Expression<'a>,
312 include_functions: bool,
313 ctx: &impl GlobalContext<'a>,
314) -> bool {
315 match expr {
316 Expression::NumericLiteral(_)
317 | Expression::StringLiteral(_)
318 | Expression::NullLiteral(_)
319 | Expression::BooleanLiteral(_)
320 | Expression::BigIntLiteral(_) => true,
321 Expression::TemplateLiteral(lit) => lit.is_literal_value(include_functions, ctx),
322 Expression::Identifier(ident) => {
323 matches!(ident.name.as_str(), "undefined" | "Infinity" | "NaN")
324 && ctx.is_global_reference(ident)
325 }
326 Expression::ArrowFunctionExpression(_) | Expression::FunctionExpression(_) => {
327 include_functions
328 }
329 Expression::UnaryExpression(e) => e.is_literal_value(include_functions, ctx),
330 Expression::BinaryExpression(e) => e.is_literal_value(include_functions, ctx),
331 Expression::LogicalExpression(e) => {
332 e.left.is_literal_value(include_functions, ctx)
333 && e.right.is_literal_value(include_functions, ctx)
334 }
335 Expression::ConditionalExpression(e) => {
336 e.test.is_literal_value(include_functions, ctx)
337 && can_convert_to_string_transparently(&e.consequent, include_functions, ctx)
338 && can_convert_to_string_transparently(&e.alternate, include_functions, ctx)
339 }
340 Expression::ParenthesizedExpression(e) => {
341 can_convert_to_string_transparently(&e.expression, include_functions, ctx)
342 }
343 Expression::SequenceExpression(e) => {
344 can_convert_to_string_transparently(
345 e.expressions.last().expect("should have at least one element"),
346 include_functions,
347 ctx,
348 ) && e
349 .expressions
350 .iter()
351 .rev()
352 .skip(1)
353 .all(|expr| expr.is_literal_value(include_functions, ctx))
354 }
355 _ => false,
356 }
357}
358
359fn is_immutable_string<'a>(
360 expr: &Expression<'a>,
361 include_functions: bool,
362 ctx: &impl GlobalContext<'a>,
363) -> bool {
364 match expr {
365 Expression::StringLiteral(_) => true,
366 Expression::TemplateLiteral(lit) => lit.is_literal_value(include_functions, ctx),
367 _ => false,
368 }
369}