1use ruff_python_ast as ast;
12
13use crate::error::{write_caret_line, ExpressionError, ExpressionErrorKind};
14use crate::path_mapping::PathFormat;
15use crate::symbol_table::SymbolTable;
16use crate::value::{ExprValue, Float64};
17
18fn append_sub_error(msg: &mut String, err: &ExpressionError, is_last: bool) {
25 let Some(src) = err.expr() else {
26 return;
27 };
28 msg.push_str(" ");
29 msg.push_str(src);
30 msg.push('\n');
31 if let (Some(col), Some(end)) = (err.col_offset(), err.end_col_offset()) {
32 msg.push_str(" ");
33 let caret_off = err.caret_offset().unwrap_or(0);
34 let _ = write_caret_line(msg, col, end, caret_off);
35 if !is_last {
36 msg.push('\n');
37 }
38 }
39}
40
41pub const DEFAULT_MEMORY_LIMIT: usize = 100_000_000; pub const DEFAULT_OPERATION_LIMIT: usize = 10_000_000;
46
47#[derive(Debug)]
49pub struct EvalResult {
50 pub value: ExprValue,
51 pub peak_memory: usize,
52 pub operation_count: usize,
53}
54
55pub struct Evaluator<'a> {
83 symtabs: &'a [&'a SymbolTable],
84 path_format: PathFormat,
85 expr_source: Option<&'a str>,
86 memory_limit: usize,
87 operation_limit: usize,
88 current_memory: usize,
89 peak_memory: usize,
90 operation_count: usize,
91 recursion_depth: usize,
96 keyword_renames: &'a std::collections::HashMap<String, String>,
97 library: &'a crate::function_library::FunctionLibrary,
98 target_type: Option<crate::types::ExprType>,
99 regex_cache: std::collections::HashMap<String, regex::Regex>,
100}
101
102static EMPTY_KEYWORD_RENAMES: std::sync::LazyLock<std::collections::HashMap<String, String>> =
103 std::sync::LazyLock::new(std::collections::HashMap::new);
104
105impl<'a> Evaluator<'a> {
106 pub fn new(symtabs: &'a [&'a SymbolTable]) -> Self {
112 Self {
113 symtabs,
114 path_format: PathFormat::host(),
115 expr_source: None,
116 memory_limit: DEFAULT_MEMORY_LIMIT,
117 operation_limit: DEFAULT_OPERATION_LIMIT,
118 current_memory: 0,
119 peak_memory: 0,
120 operation_count: 0,
121 recursion_depth: 0,
122 keyword_renames: &EMPTY_KEYWORD_RENAMES,
123 library: &crate::default_library::DEFAULT_LIBRARY,
130 target_type: None,
131 regex_cache: std::collections::HashMap::new(),
132 }
133 }
134
135 #[must_use]
142 pub fn with_library(mut self, library: &'a crate::function_library::FunctionLibrary) -> Self {
143 self.library = library;
144 self
145 }
146
147 #[must_use]
153 pub fn with_memory_limit(mut self, limit: usize) -> Self {
154 self.memory_limit = limit;
155 self
156 }
157
158 #[must_use]
165 pub fn with_operation_limit(mut self, limit: usize) -> Self {
166 self.operation_limit = limit;
167 self
168 }
169
170 #[must_use]
177 pub fn with_path_format(mut self, format: PathFormat) -> Self {
178 self.path_format = format;
179 self
180 }
181
182 #[must_use]
188 pub fn with_target_type(mut self, t: &crate::types::ExprType) -> Self {
189 self.target_type = Some(t.clone());
190 self
191 }
192
193 #[must_use]
198 pub fn with_expr_source(mut self, source: &'a str) -> Self {
199 self.expr_source = Some(source);
200 self
201 }
202
203 #[must_use]
209 pub fn with_keyword_renames(
210 mut self,
211 renames: &'a std::collections::HashMap<String, String>,
212 ) -> Self {
213 self.keyword_renames = renames;
214 self
215 }
216
217 pub fn peak_memory(&self) -> usize {
218 self.peak_memory
219 }
220 pub fn operation_count(&self) -> usize {
221 self.operation_count
222 }
223
224 pub fn evaluate(&mut self, node: &ast::Expr) -> Result<ExprValue, ExpressionError> {
226 self.recursion_depth += 1;
235 if self.recursion_depth > super::parse::MAX_EXPRESSION_DEPTH {
236 self.recursion_depth -= 1;
237 let err = ExpressionError::expression_too_deep(
238 self.recursion_depth + 1,
239 super::parse::MAX_EXPRESSION_DEPTH,
240 );
241 return Err(match self.expr_source {
242 Some(src) => err.with_node(src, node),
243 None => err,
244 });
245 }
246 let result = self.evaluate_inner(node);
247 self.recursion_depth -= 1;
248 match result {
250 Err(e) if e.expr().is_none() => {
251 if let Some(src) = &self.expr_source {
252 Err(e.with_node(src, node))
253 } else {
254 Err(e)
255 }
256 }
257 Ok(val) => {
258 if let Some(ref tt) = self.target_type {
259 val.coerce(tt, self.path_format).map_err(|msg| {
260 let e = ExpressionError::new(msg);
261 if let Some(src) = &self.expr_source {
262 e.with_node(src, node)
263 } else {
264 e
265 }
266 })
267 } else {
268 Ok(val)
269 }
270 }
271 other => other,
272 }
273 }
274
275 fn evaluate_inner(&mut self, node: &ast::Expr) -> Result<ExprValue, ExpressionError> {
276 match node {
277 ast::Expr::NumberLiteral(n) => self.eval_number(n),
278 ast::Expr::StringLiteral(s) => self.eval_string(s),
279 ast::Expr::BooleanLiteral(b) => self.track(ExprValue::Bool(b.value)),
280 ast::Expr::NoneLiteral(_) => self.track(ExprValue::Null),
281 ast::Expr::Name(n) => self.eval_name(n),
282 ast::Expr::Attribute(a) => self.eval_attribute(a),
283 ast::Expr::BinOp(b) => self.eval_binop(b),
284 ast::Expr::UnaryOp(u) => self.eval_unaryop(u),
285 ast::Expr::BoolOp(b) => self.eval_boolop(b),
286 ast::Expr::Compare(c) => self.eval_compare(c),
287 ast::Expr::If(i) => self.eval_ifexp(i),
288 ast::Expr::Call(c) => self.eval_call(c),
289 ast::Expr::List(l) => self.eval_list(l),
290 ast::Expr::Subscript(s) => self.eval_subscript(s),
291 ast::Expr::ListComp(lc) => self.eval_listcomp(lc),
292 ast::Expr::Slice(s) => self.eval_slice(s),
293 ast::Expr::Starred(_) => Err(ExpressionError::unsupported(
294 "Star unpacking is not supported",
295 )),
296 ast::Expr::Lambda(_) => Err(ExpressionError::unsupported(
297 "Lambda expressions are not supported",
298 )),
299 ast::Expr::Dict(_) => Err(ExpressionError::unsupported(
300 "Dict literals are not supported",
301 )),
302 ast::Expr::Set(_) => Err(ExpressionError::unsupported(
303 "Set literals are not supported",
304 )),
305 ast::Expr::DictComp(_) => Err(ExpressionError::unsupported(
306 "Dict comprehensions are not supported",
307 )),
308 ast::Expr::SetComp(_) => Err(ExpressionError::unsupported(
309 "Set comprehensions are not supported",
310 )),
311 ast::Expr::Generator(_) => Err(ExpressionError::unsupported(
312 "Generator expressions are not supported",
313 )),
314 ast::Expr::Await(_) => Err(ExpressionError::unsupported(
315 "Await expressions are not supported",
316 )),
317 ast::Expr::FString(_) => Err(ExpressionError::unsupported(
318 "f-strings are not supported; use string concatenation",
319 )),
320 ast::Expr::BytesLiteral(_) => Err(ExpressionError::unsupported(
321 "Byte strings are not supported",
322 )),
323 ast::Expr::EllipsisLiteral(_) => {
324 Err(ExpressionError::unsupported("Ellipsis is not supported"))
325 }
326 ast::Expr::Tuple(_) => Err(ExpressionError::unsupported(
327 "Tuple literals are not supported",
328 )),
329 ast::Expr::Named(_) => Err(ExpressionError::unsupported(
330 "Walrus operator (:=) is not supported",
331 )),
332 _ => Err(ExpressionError::unsupported("Unsupported expression type")),
333 }
334 }
335
336 fn count_op(&mut self) -> Result<(), ExpressionError> {
337 self.operation_count = self.operation_count.saturating_add(1);
338 if self.operation_count > self.operation_limit {
339 Err(ExpressionError::from_kind(
340 ExpressionErrorKind::OperationLimitExceeded {
341 count: self.operation_count,
342 limit: self.operation_limit,
343 },
344 ))
345 } else {
346 Ok(())
347 }
348 }
349
350 fn collect_symbol_names(&self) -> Vec<String> {
352 let mut names = Vec::new();
353 for symtab in self.symtabs {
354 names.extend(symtab.all_paths(""));
355 }
356 names.sort();
357 names.dedup();
358 names
359 }
360
361 fn track(&mut self, value: ExprValue) -> Result<ExprValue, ExpressionError> {
362 if let ExprValue::Float(f) = &value {
364 if f.value().is_infinite() {
365 return Err(ExpressionError::float_error(
366 "Float operation produced infinity",
367 ));
368 }
369 if f.value().is_nan() {
370 return Err(ExpressionError::float_error("Float operation produced NaN"));
371 }
372 }
373 self.current_memory += value.memory_size();
374 if self.current_memory > self.peak_memory {
375 self.peak_memory = self.current_memory;
376 }
377 if self.current_memory > self.memory_limit {
378 Err(ExpressionError::from_kind(
379 ExpressionErrorKind::MemoryLimitExceeded {
380 used: self.current_memory,
381 limit: self.memory_limit,
382 },
383 ))
384 } else {
385 Ok(value)
386 }
387 }
388
389 fn release(&mut self, value: &ExprValue) {
390 let size = value.memory_size();
391 self.current_memory = self.current_memory.saturating_sub(size);
392 }
393
394 fn dispatch_with_node(
395 &mut self,
396 name: &str,
397 args: Vec<ExprValue>,
398 node: Option<&ast::Expr>,
399 ) -> Result<ExprValue, ExpressionError> {
400 self.count_op()?;
401 let input_size: usize = args.iter().map(|a| a.memory_size()).sum();
402 let lib = self.library;
403 let result = lib.call(name, &args, self).map_err(|e| {
404 if let (Some(src), Some(n)) = (self.expr_source, node) {
405 e.with_node(src, n)
406 } else {
407 e
408 }
409 })?;
410 self.current_memory = self.current_memory.saturating_sub(input_size);
411 self.track(result)
412 }
413
414 fn dispatch_with_span(
417 &mut self,
418 name: &str,
419 args: Vec<ExprValue>,
420 range: ruff_text_size::TextRange,
421 ) -> Result<ExprValue, ExpressionError> {
422 self.count_op()?;
423 let input_size: usize = args.iter().map(|a| a.memory_size()).sum();
424 let lib = self.library;
425 let result = lib.call(name, &args, self).map_err(|e| {
426 if let Some(src) = self.expr_source {
427 e.with_span(src, range.start().to_usize(), range.end().to_usize())
428 } else {
429 e
430 }
431 })?;
432 self.current_memory = self.current_memory.saturating_sub(input_size);
433 self.track(result)
434 }
435
436 fn eval_number(&mut self, n: &ast::ExprNumberLiteral) -> Result<ExprValue, ExpressionError> {
437 match &n.value {
438 ast::Number::Int(i) => {
439 let val: i64 = i.as_i64().ok_or_else(ExpressionError::integer_overflow)?;
440 self.track(ExprValue::Int(val))
441 }
442 ast::Number::Float(f) => {
443 if f.is_infinite() {
444 return Err(ExpressionError::float_error(
445 "Float operation produced infinity",
446 ));
447 }
448 if f.is_nan() {
449 return Err(ExpressionError::float_error("Float operation produced NaN"));
450 }
451 let original = self.expr_source.as_ref().and_then(|src| {
453 let start = n.range.start().to_usize();
454 let end = n.range.end().to_usize();
455 if end <= src.len() {
456 let s = &src[start..end];
457 if s.ends_with('.') || s.starts_with('.') || s.contains('_') {
459 None
460 } else {
461 Some(s.to_string())
462 }
463 } else {
464 None
465 }
466 });
467 self.track(ExprValue::Float(if let Some(s) = original {
468 Float64::with_str(*f, s)?
469 } else {
470 Float64::new(*f)?
471 }))
472 }
473 ast::Number::Complex { .. } => Err(ExpressionError::unsupported(
474 "Complex numbers are not supported",
475 )),
476 }
477 }
478
479 fn eval_string(&mut self, s: &ast::ExprStringLiteral) -> Result<ExprValue, ExpressionError> {
480 for part in &s.value {
482 if matches!(
483 part.flags.prefix(),
484 ast::str_prefix::StringLiteralPrefix::Unicode
485 ) {
486 return Err(ExpressionError::new(
487 "Unicode string prefix u'...' is not supported. Use '...' or \"...\" instead.",
488 ));
489 }
490 }
491 self.track(ExprValue::String(s.value.to_string()))
492 }
493
494 fn check_path_format(&self, value: &ExprValue, name: &str) -> Result<(), ExpressionError> {
495 match value {
496 ExprValue::Path { format, .. } if *format != self.path_format => {
497 Err(ExpressionError::new(format!(
498 "Path format mismatch for '{name}': value has {format:?} but evaluator uses {:?}",
499 self.path_format
500 )))
501 }
502 ExprValue::ListPath(items, fmt, _) if !items.is_empty() && *fmt != self.path_format => {
503 Err(ExpressionError::new(format!(
504 "Path format mismatch for '{name}': value has {fmt:?} but evaluator uses {:?}",
505 self.path_format
506 )))
507 }
508 _ => Ok(()),
509 }
510 }
511
512 fn eval_name(&mut self, n: &ast::ExprName) -> Result<ExprValue, ExpressionError> {
513 let name = n.id.as_str();
514 match name {
515 "True" | "true" => return self.track(ExprValue::Bool(true)),
516 "False" | "false" => return self.track(ExprValue::Bool(false)),
517 "None" | "null" => return self.track(ExprValue::Null),
518 _ => {}
519 }
520 for symtab in self.symtabs.iter().rev() {
521 if let Some(val) = symtab.get_value(name) {
522 self.check_path_format(val, name)?;
523 return self.track(val.clone());
524 }
525 }
526 let available = self.collect_symbol_names();
527 let suggestion = crate::edit_distance::suggest_closest(
528 name,
529 &available.iter().map(|s| s.as_str()).collect::<Vec<_>>(),
530 );
531 Err(ExpressionError::from_kind(
532 ExpressionErrorKind::UndefinedVariable {
533 name: name.to_string(),
534 suggestion,
535 },
536 ))
537 }
538
539 fn eval_attribute(&mut self, a: &ast::ExprAttribute) -> Result<ExprValue, ExpressionError> {
540 let dotted_path = build_dotted_name_from_attr(a);
542 if let Some(ref path) = dotted_path {
543 let resolved = resolve_keyword_renames(path, self.keyword_renames);
544 for symtab in self.symtabs.iter().rev() {
545 if let Some(val) = symtab.get_value(&resolved) {
546 self.count_op()?;
547 self.check_path_format(val, &resolved)?;
548 return self.track(val.clone());
549 }
550 }
551 }
552 let value = match self.evaluate(&a.value) {
556 Ok(v) => v,
557 Err(_) if dotted_path.is_some() => {
558 let path = dotted_path.as_ref().unwrap();
559 let available = self.collect_symbol_names();
560 let suggestion = crate::edit_distance::suggest_closest(
561 path,
562 &available.iter().map(|s| s.as_str()).collect::<Vec<_>>(),
563 );
564 let src = self.expr_source.unwrap_or("");
565 let attr_node = ast::Expr::Attribute(a.clone());
566 return Err(
567 ExpressionError::from_kind(ExpressionErrorKind::UndefinedVariable {
568 name: path.clone(),
569 suggestion,
570 })
571 .with_node(src, &attr_node),
572 );
573 }
574 Err(e) => return Err(e),
575 };
576 let attr = a.attr.as_str();
577 let prop_name = format!("__property_{attr}__");
578 let attr_node = ast::Expr::Attribute(a.clone());
579 match self.dispatch_with_node(&prop_name, vec![value.clone()], Some(&attr_node)) {
580 Ok(v) => Ok(v),
581 Err(_) => {
582 let src = self.expr_source.unwrap_or("");
583 let val_type = value.expr_type();
584 let val_type_str = if val_type.code() == crate::types::TypeCode::Unresolved
585 && !val_type.params().is_empty()
586 {
587 val_type.params()[0].to_string()
588 } else {
589 val_type.to_string()
590 };
591
592 {
594 let lib = self.library;
595 if !lib.get_signatures(attr).is_empty() {
596 return Err(ExpressionError::new(format!(
597 "'{attr}' is a method, not a property. Did you mean {attr}()?"
598 ))
599 .with_node(src, &attr_node));
600 }
601 }
602
603 {
605 let lib = self.library;
606 let valid_types: Vec<String> = lib
607 .get_signatures(&prop_name)
608 .iter()
609 .filter_map(|e| e.signature.sig_params().first())
610 .filter(|t: &&crate::types::ExprType| !t.is_symbolic())
611 .map(|t: &crate::types::ExprType| t.to_string())
612 .collect::<std::collections::BTreeSet<_>>()
613 .into_iter()
614 .collect();
615 if !valid_types.is_empty() {
616 return Err(ExpressionError::new(format!(
617 "'{attr}' property is not available for {val_type_str}. Available for: {}",
618 valid_types.join(", ")
619 )).with_node(src, &attr_node));
620 }
621 }
622
623 Err(ExpressionError::new(format!(
624 "Cannot access attribute '{attr}' on {val_type_str}"
625 ))
626 .with_node(src, &attr_node))
627 }
628 }
629 }
630
631 fn eval_binop(&mut self, b: &ast::ExprBinOp) -> Result<ExprValue, ExpressionError> {
632 let op_name = match b.op {
634 ast::Operator::Add => "__add__",
635 ast::Operator::Sub => "__sub__",
636 ast::Operator::Mult => "__mul__",
637 ast::Operator::Div => "__truediv__",
638 ast::Operator::FloorDiv => "__floordiv__",
639 ast::Operator::Mod => "__mod__",
640 ast::Operator::Pow => "__pow__",
641 ast::Operator::BitAnd => {
642 return Err(ExpressionError::unsupported(
643 "Bitwise AND (&) is not supported",
644 ))
645 }
646 ast::Operator::BitOr => {
647 return Err(ExpressionError::unsupported(
648 "Bitwise OR (|) is not supported",
649 ))
650 }
651 ast::Operator::BitXor => {
652 return Err(ExpressionError::unsupported(
653 "Bitwise XOR (^) is not supported",
654 ))
655 }
656 ast::Operator::LShift => {
657 return Err(ExpressionError::unsupported(
658 "Left shift (<<) is not supported",
659 ))
660 }
661 ast::Operator::RShift => {
662 return Err(ExpressionError::unsupported(
663 "Right shift (>>) is not supported",
664 ))
665 }
666 ast::Operator::MatMult => {
667 return Err(ExpressionError::unsupported(
668 "Matrix multiply (@) is not supported",
669 ))
670 }
671 };
672
673 let left = self.evaluate(&b.left)?;
674 let right = self.evaluate(&b.right)?;
675 self.dispatch_with_node(
676 op_name,
677 vec![left, right],
678 Some(&ast::Expr::BinOp(b.clone())),
679 )
680 }
681
682 fn eval_unaryop(&mut self, u: &ast::ExprUnaryOp) -> Result<ExprValue, ExpressionError> {
683 self.count_op()?;
684 if u.op == ast::UnaryOp::Invert {
685 return Err(ExpressionError::unsupported(
686 "Bitwise NOT (~) is not supported",
687 ));
688 }
689 if matches!(u.op, ast::UnaryOp::USub) {
692 if let ast::Expr::NumberLiteral(n) = &*u.operand {
693 if let ast::Number::Int(i) = &n.value {
694 if let Some(pos) = i.as_i64() {
696 return self.track(ExprValue::Int(
697 pos.checked_neg()
698 .ok_or_else(ExpressionError::integer_overflow)?,
699 ));
700 }
701 if let Some(pos) = i.as_u64() {
703 if pos == 9223372036854775808u64 {
704 return self.track(ExprValue::Int(i64::MIN));
705 }
706 }
707 return Err(ExpressionError::integer_overflow());
708 }
709 }
710 }
711 let operand = self.evaluate(&u.operand)?;
712 let op_name = match u.op {
713 ast::UnaryOp::USub => "__neg__",
714 ast::UnaryOp::UAdd => "__pos__",
715 ast::UnaryOp::Not => "__not__",
716 ast::UnaryOp::Invert => unreachable!(),
717 };
718 self.dispatch_with_node(op_name, vec![operand], Some(&ast::Expr::UnaryOp(u.clone())))
719 }
720
721 fn eval_boolop(&mut self, b: &ast::ExprBoolOp) -> Result<ExprValue, ExpressionError> {
722 self.count_op()?;
723 let mut last = ExprValue::Bool(match b.op {
724 ast::BoolOp::And => true,
725 ast::BoolOp::Or => false,
726 });
727 let mut seen_unresolved = false;
728 for node in &b.values {
729 if seen_unresolved {
730 match self.evaluate(node) {
734 Ok(val) => match b.op {
735 ast::BoolOp::And => {
736 if matches!(&val, ExprValue::Null | ExprValue::Bool(false)) {
737 return Ok(val);
738 }
739 }
740 ast::BoolOp::Or => {
741 if !matches!(&val, ExprValue::Null | ExprValue::Bool(false)) {
742 return Ok(val);
743 }
744 }
745 },
746 Err(_) => { }
747 }
748 continue;
749 }
750 last = self.evaluate(node)?;
751 if last.is_unresolved() {
752 seen_unresolved = true;
753 continue;
754 }
755 match b.op {
756 ast::BoolOp::And => {
758 if matches!(&last, ExprValue::Null | ExprValue::Bool(false)) {
759 return Ok(last);
760 }
761 }
762 ast::BoolOp::Or => {
763 if !matches!(&last, ExprValue::Null | ExprValue::Bool(false)) {
764 return Ok(last);
765 }
766 }
767 }
768 }
769 if seen_unresolved {
770 return self.track(ExprValue::unresolved(ExprType::BOOL));
771 }
772 Ok(last)
773 }
774
775 fn eval_compare(&mut self, c: &ast::ExprCompare) -> Result<ExprValue, ExpressionError> {
776 self.count_op()?;
777 for op in &c.ops {
779 match op {
780 ast::CmpOp::Is => {
781 return Err(ExpressionError::unsupported(
782 "'is' operator is not supported; use '=='",
783 ))
784 }
785 ast::CmpOp::IsNot => {
786 return Err(ExpressionError::unsupported(
787 "'is not' operator is not supported; use '!='",
788 ))
789 }
790 _ => {}
791 }
792 }
793 let mut left = self.evaluate(&c.left)?;
794 for (op, right_node) in c.ops.iter().zip(c.comparators.iter()) {
795 let right = self.evaluate(right_node)?;
796 if left.is_unresolved() || right.is_unresolved() {
797 self.release(&left);
798 self.release(&right);
799 return self.track(ExprValue::unresolved(ExprType::BOOL));
800 }
801 let (op_name, args) = match op {
802 ast::CmpOp::Eq => ("__eq__", vec![left.clone(), right.clone()]),
803 ast::CmpOp::NotEq => ("__ne__", vec![left.clone(), right.clone()]),
804 ast::CmpOp::Lt => ("__lt__", vec![left.clone(), right.clone()]),
805 ast::CmpOp::LtE => ("__le__", vec![left.clone(), right.clone()]),
806 ast::CmpOp::Gt => ("__gt__", vec![left.clone(), right.clone()]),
807 ast::CmpOp::GtE => ("__ge__", vec![left.clone(), right.clone()]),
808 ast::CmpOp::In => ("__contains__", vec![right.clone(), left.clone()]),
810 ast::CmpOp::NotIn => ("__not_contains__", vec![right.clone(), left.clone()]),
811 ast::CmpOp::Is => {
812 return Err(ExpressionError::unsupported(
813 "'is' operator is not supported; use '=='",
814 ))
815 }
816 ast::CmpOp::IsNot => {
817 return Err(ExpressionError::unsupported(
818 "'is not' operator is not supported; use '!='",
819 ))
820 }
821 };
822
823 use ruff_text_size::Ranged;
826 let result_val = self.dispatch_with_span(op_name, args, c.range())?;
827 let result = match &result_val {
828 ExprValue::Bool(b) => *b,
829 _ => true,
830 };
831 self.release(&result_val);
832
833 if !result {
834 self.release(&left);
835 self.release(&right);
836 return self.track(ExprValue::Bool(false));
837 }
838 self.release(&left);
839 left = right;
840 }
841 self.release(&left);
842 self.track(ExprValue::Bool(true))
843 }
844
845 fn eval_ifexp(&mut self, i: &ast::ExprIf) -> Result<ExprValue, ExpressionError> {
846 self.count_op()?;
847 let test = self.evaluate(&i.test)?;
848 if test.is_unresolved() {
849 let inner = unwrap_unresolved(&test.expr_type());
851 let is_bool_compatible = inner == ExprType::BOOL
852 || inner.code() == crate::types::TypeCode::Unresolved
853 || inner.code() == crate::types::TypeCode::Any
854 || (inner.code() == crate::types::TypeCode::Union
855 && inner.params().contains(&ExprType::BOOL));
856 if !is_bool_compatible {
857 let err =
858 ExpressionError::new(format!("Condition must be a boolean, got {}", inner));
859 self.release(&test);
860 return Err(if let Some(src) = self.expr_source {
861 err.with_node(src, &i.test)
862 } else {
863 err
864 });
865 }
866 self.release(&test);
867 let body = self.evaluate(&i.body);
869 let orelse = self.evaluate(&i.orelse);
870 match (body, orelse) {
871 (Err(be), Err(oe)) => {
872 let mut msg = format!(
873 "Both branches fail in the if/else:\n if-branch: {}\n",
874 be.message()
875 );
876 append_sub_error(&mut msg, &be, false);
877 msg.push_str(&format!(" else-branch: {}\n", oe.message()));
878 append_sub_error(&mut msg, &oe, true);
879 let mut err = ExpressionError::new(msg).with_sub_errors(vec![be, oe]);
880 if let Some(src) = self.expr_source {
881 use ruff_text_size::Ranged;
882 let start = i.range().start().to_usize();
883 let end = i.range().end().to_usize();
884 err.set_source_span(src, start, end, 0);
885 }
886 Err(err)
887 }
888 (Ok(b), Err(_)) => {
889 let t = unwrap_unresolved(&b.expr_type());
890 self.track(ExprValue::unresolved(t))
891 }
892 (Err(_), Ok(o)) => {
893 let t = unwrap_unresolved(&o.expr_type());
894 self.track(ExprValue::unresolved(t))
895 }
896 (Ok(b), Ok(o)) => {
897 let bt = unwrap_unresolved(&b.expr_type());
898 let ot = unwrap_unresolved(&o.expr_type());
899 if bt == ot {
900 self.track(ExprValue::unresolved(bt))
901 } else {
902 self.track(ExprValue::unresolved(ExprType::union(vec![bt, ot])))
903 }
904 }
905 }
906 } else {
907 if !matches!(&test, ExprValue::Bool(_)) {
909 let err = ExpressionError::new(format!(
910 "Condition must be a boolean, got {}",
911 test.expr_type()
912 ));
913 return Err(if let Some(src) = self.expr_source {
914 err.with_node(src, &i.test)
915 } else {
916 err
917 });
918 }
919 if matches!(test, ExprValue::Bool(true)) {
921 self.release(&test);
922 self.evaluate(&i.body)
923 } else {
924 self.release(&test);
925 self.evaluate(&i.orelse)
926 }
927 }
928 }
929
930 fn eval_call(&mut self, c: &ast::ExprCall) -> Result<ExprValue, ExpressionError> {
931 self.count_op()?;
932 if !c.arguments.keywords.is_empty() {
934 return Err(ExpressionError::unsupported(
935 "Keyword arguments are not supported",
936 ));
937 }
938 let mut receiver_value: Option<ExprValue> = None;
940 let is_method_call;
941 let func_name = match &*c.func {
942 ast::Expr::Name(n) => {
943 is_method_call = false;
944 Some(n.id.to_string())
945 }
946 ast::Expr::Attribute(a) => {
947 let receiver = self.evaluate(&a.value)?;
948 receiver_value = Some(receiver);
949 is_method_call = true;
950 Some(a.attr.to_string())
951 }
952 _ => {
953 is_method_call = false;
954 None
955 }
956 };
957 let mut args = Vec::new();
959 for arg in &c.arguments.args {
960 args.push(self.evaluate(arg)?);
961 }
962
963 if let Some(recv) = receiver_value {
967 args.insert(0, recv);
968 }
969
970 let _any_unresolved = args.iter().any(|a| a.is_unresolved());
971 let args_size: usize = args.iter().map(|a| a.memory_size()).sum();
974 self.current_memory = self.current_memory.saturating_sub(args_size);
975
976 if let Some(name) = &func_name {
978 if name.starts_with("__") && name.ends_with("__") {
979 return Err(ExpressionError::new(format!(
980 "Cannot call '{}' directly",
981 name
982 )));
983 }
984 {
985 let lib = self.library;
986 let result = if is_method_call {
987 lib.call_method(name, &args, self)
988 } else {
989 lib.call(name, &args, self)
990 };
991 let result = result.map_err(|e| {
992 let src = self.expr_source.unwrap_or("");
993 let call_node = ast::Expr::Call(c.clone());
994 if is_method_call
995 && !lib
996 .get_signatures(&format!("__property_{name}__"))
997 .is_empty()
998 {
999 return ExpressionError::new(format!(
1000 "'{name}' is a property, not a method. Use .{name} instead of .{name}()"
1001 ))
1002 .with_node(src, &call_node);
1003 }
1004 e.with_node(src, &call_node)
1005 })?;
1006 self.track(result)
1007 }
1008 } else {
1009 Err(ExpressionError::new("Cannot call non-function expression"))
1010 }
1011 }
1012
1013 fn eval_list(&mut self, l: &ast::ExprList) -> Result<ExprValue, ExpressionError> {
1014 let list_elem_target = self.target_type.as_ref().and_then(|tt| {
1016 if tt.code() == crate::types::TypeCode::List && tt.params().len() == 1 {
1017 Some(tt.params()[0].clone())
1018 } else {
1019 None
1020 }
1021 });
1022
1023 let saved_target = self.target_type.take();
1025 if let Some(ref elem_t) = list_elem_target {
1026 self.target_type = Some(elem_t.clone());
1027 }
1028
1029 let mut elements = Vec::new();
1030 for elt in &l.elts {
1031 let val = self.evaluate(elt)?;
1032 if matches!(&val, ExprValue::Null) {
1033 self.target_type = saved_target;
1034 return Err(ExpressionError::new("null is not allowed in list literals"));
1035 }
1036 elements.push(val);
1037 }
1038
1039 self.target_type = saved_target;
1041
1042 for e in &elements {
1044 let t = e.expr_type();
1045 if let Some(inner) = t.list_element_type() {
1046 if inner.list_element_type().is_some() {
1047 return Err(ExpressionError::new(
1048 "Lists may be nested at most 2 levels deep",
1049 ));
1050 }
1051 }
1052 }
1053 if elements.iter().any(|e| e.is_unresolved()) {
1054 if !elements.is_empty() {
1056 let first_type = unwrap_unresolved(&elements[0].expr_type());
1057 for (_i, e) in elements.iter().enumerate().skip(1) {
1058 let t = unwrap_unresolved(&e.expr_type());
1059 if (first_type == ExprType::INT && t == ExprType::FLOAT)
1061 || (first_type == ExprType::FLOAT && t == ExprType::INT)
1062 || (first_type == ExprType::PATH && t == ExprType::STRING)
1063 || (first_type == ExprType::STRING && t == ExprType::PATH)
1064 {
1065 continue;
1066 }
1067 if t.code() == crate::types::TypeCode::Unresolved
1068 || first_type.code() == crate::types::TypeCode::Unresolved
1069 {
1070 continue;
1071 }
1072 if t != first_type {
1073 return Err(ExpressionError::new(format!(
1074 "List literal contains incompatible types: {first_type}, {t}"
1075 )));
1076 }
1077 }
1078 }
1079 let elem_type = if elements.is_empty() {
1080 ExprType::NULLTYPE
1081 } else {
1082 let mut result = unwrap_unresolved(&elements[0].expr_type());
1084 for e in elements.iter().skip(1) {
1085 let t = unwrap_unresolved(&e.expr_type());
1086 if t.code() == crate::types::TypeCode::Unresolved {
1087 continue;
1088 }
1089 if result.code() == crate::types::TypeCode::Unresolved {
1090 result = t;
1091 continue;
1092 }
1093 if (result == ExprType::INT && t == ExprType::FLOAT)
1094 || (result == ExprType::FLOAT && t == ExprType::INT)
1095 {
1096 result = ExprType::FLOAT;
1097 } else if (result == ExprType::PATH && t == ExprType::STRING)
1098 || (result == ExprType::STRING && t == ExprType::PATH)
1099 {
1100 result = ExprType::STRING;
1101 }
1102 }
1103 result
1104 };
1105 return self.track(ExprValue::unresolved(ExprType::list(elem_type)));
1106 }
1107 if let Some(ref elem_t) = list_elem_target {
1109 let coerced: Result<Vec<ExprValue>, _> = elements
1110 .into_iter()
1111 .map(|e| {
1112 e.coerce(elem_t, self.path_format)
1113 .map_err(ExpressionError::new)
1114 })
1115 .collect();
1116 let list = ExprValue::make_list_checked(self, coerced?, elem_t.clone())?;
1117 return self.track(list);
1118 }
1119 if !elements.is_empty() {
1121 let mut seen_types: Vec<ExprType> = Vec::new();
1122 for e in elements.iter() {
1123 let t = e.expr_type();
1124 if !seen_types.contains(&t) {
1125 seen_types.push(t);
1126 }
1127 }
1128 let dominated: Vec<&ExprType> = seen_types
1130 .iter()
1131 .filter(|t| {
1132 t.code() == crate::types::TypeCode::NullType ||
1134 (**t == ExprType::INT && seen_types.contains(&ExprType::FLOAT)) ||
1136 (**t == ExprType::FLOAT && seen_types.contains(&ExprType::INT)) ||
1138 (**t == ExprType::PATH && seen_types.contains(&ExprType::STRING)) ||
1140 (**t == ExprType::STRING && seen_types.contains(&ExprType::PATH))
1142 })
1143 .collect();
1144 let compatible = dominated.len() == seen_types.len() ||
1146 seen_types.len() == 1 ||
1147 seen_types.iter().all(|t| t.code() == crate::types::TypeCode::List || t.code() == crate::types::TypeCode::NullType);
1149 if !compatible {
1150 let type_strs: Vec<String> = seen_types.iter().map(|t| t.to_string()).collect();
1151 let msg = if type_strs.len() == 2 {
1152 format!(
1153 "List literal contains incompatible types: {} and {}",
1154 type_strs[0], type_strs[1]
1155 )
1156 } else {
1157 let last = type_strs.last().unwrap();
1158 let rest = type_strs[..type_strs.len() - 1].join(", ");
1159 format!("List literal contains incompatible types: {rest}, and {last}")
1160 };
1161 return Err(ExpressionError::new(msg));
1162 }
1163 }
1164 let elem_type = if elements.is_empty() {
1165 ExprType::NULLTYPE
1166 } else {
1167 elements[0].expr_type()
1168 };
1169 let list = ExprValue::make_list_checked(self, elements, elem_type)?;
1170 self.track(list)
1171 }
1172
1173 fn eval_subscript(&mut self, s: &ast::ExprSubscript) -> Result<ExprValue, ExpressionError> {
1174 self.count_op()?;
1175 let value = self.evaluate(&s.value)?;
1176
1177 if matches!(&value, ExprValue::Path { .. }) {
1179 return Err(ExpressionError::new(
1180 "Cannot subscript type path".to_string(),
1181 ));
1182 }
1183
1184 if let ast::Expr::Slice(sl) = &*s.slice {
1186 let start = match sl.lower.as_ref().map(|e| self.evaluate(e)).transpose()? {
1187 Some(v) => v,
1188 None => ExprValue::Null,
1189 };
1190 let stop = match sl.upper.as_ref().map(|e| self.evaluate(e)).transpose()? {
1191 Some(v) => v,
1192 None => ExprValue::Null,
1193 };
1194 let step = match sl.step.as_ref().map(|e| self.evaluate(e)).transpose()? {
1195 Some(v) => v,
1196 None => ExprValue::Null,
1197 };
1198
1199 if let ExprValue::Int(0) = &step {
1200 return Err(ExpressionError::new("Slice step cannot be zero"));
1201 }
1202
1203 if value.is_unresolved() {
1204 let inner = unwrap_unresolved(&value.expr_type());
1205 if let Some(elem) = inner.list_element_type() {
1206 return self.track(ExprValue::unresolved(ExprType::list(elem.clone())));
1207 }
1208 return self.track(ExprValue::unresolved(inner));
1209 }
1210
1211 let any_bound_unresolved =
1213 start.is_unresolved() || stop.is_unresolved() || step.is_unresolved();
1214 if any_bound_unresolved {
1215 if value.is_list() {
1216 let elem_type = value.list_elem_type().unwrap();
1217 return self.track(ExprValue::unresolved(ExprType::list(elem_type.clone())));
1218 } else if matches!(&value, ExprValue::String(_)) {
1219 return self.track(ExprValue::unresolved(ExprType::STRING));
1220 }
1221 }
1222
1223 let node = ast::Expr::Subscript(s.clone());
1225 return self.dispatch_with_node(
1226 "__getitem__",
1227 vec![value, start, stop, step],
1228 Some(&node),
1229 );
1230 }
1231
1232 let slice = self.evaluate(&s.slice)?;
1233
1234 if slice.is_unresolved() {
1236 let inner = unwrap_unresolved(&slice.expr_type());
1237 if inner != ExprType::INT
1238 && inner.code() != crate::types::TypeCode::Unresolved
1239 && inner.code() != crate::types::TypeCode::Any
1240 {
1241 let mut err = ExpressionError::new("Index must be an integer");
1242 if let Some(src) = self.expr_source {
1243 use ruff_text_size::Ranged;
1244 let start = s.value.range().start().to_usize();
1245 let end = s.range().end().to_usize();
1246 let left_end = s.value.range().end().to_usize();
1247 err.set_source_span(src, start, end, left_end - start);
1248 }
1249 return Err(err);
1250 }
1251 }
1252
1253 self.dispatch_with_node(
1255 "__getitem__",
1256 vec![value, slice],
1257 Some(&ast::Expr::Subscript(s.clone())),
1258 )
1259 }
1260
1261 fn child_evaluator<'b>(&self, symtabs: &'b [&'b SymbolTable]) -> Evaluator<'b>
1265 where
1266 'a: 'b,
1267 {
1268 Evaluator {
1269 symtabs,
1270 path_format: self.path_format,
1271 expr_source: self.expr_source,
1272 memory_limit: self.memory_limit,
1273 operation_limit: self.operation_limit,
1274 current_memory: self.current_memory,
1275 peak_memory: self.peak_memory,
1276 operation_count: self.operation_count,
1277 recursion_depth: self.recursion_depth,
1278 keyword_renames: self.keyword_renames,
1279 library: self.library,
1280 target_type: None,
1281 regex_cache: std::collections::HashMap::new(),
1282 }
1283 }
1284
1285 fn absorb_counters(&mut self, child: &Evaluator) {
1287 self.current_memory = child.current_memory;
1288 self.peak_memory = child.peak_memory;
1289 self.operation_count = child.operation_count;
1290 }
1291
1292 fn eval_listcomp(&mut self, lc: &ast::ExprListComp) -> Result<ExprValue, ExpressionError> {
1293 if lc.generators.len() != 1 {
1295 return Err(ExpressionError::unsupported(
1296 "Multiple 'for' clauses in list comprehensions are not supported",
1297 ));
1298 }
1299 let gen = &lc.generators[0];
1300 if gen.ifs.len() > 1 {
1301 return Err(ExpressionError::unsupported(
1302 "Multiple 'if' clauses in a list comprehension are not supported; combine with 'and'",
1303 ));
1304 }
1305 if !matches!(&gen.target, ast::Expr::Name(_)) {
1306 return Err(ExpressionError::unsupported(
1307 "Tuple unpacking in list comprehension is not supported",
1308 ));
1309 }
1310 if let ast::Expr::Name(n) = &gen.target {
1311 let var_name = n.id.as_str();
1312 if !var_name.is_empty() && !var_name.starts_with(|c: char| c.is_lowercase() || c == '_')
1313 {
1314 return Err(ExpressionError::new(format!(
1315 "Loop variable '{var_name}' must start with a lowercase letter or underscore"
1316 )));
1317 }
1318 }
1319
1320 let iterable = self.evaluate(&gen.iter)?;
1321 let var_name = match &gen.target {
1322 ast::Expr::Name(n) => n.id.to_string(),
1323 _ => unreachable!(),
1324 };
1325
1326 if iterable.is_unresolved() {
1328 let inner = unwrap_unresolved(&iterable.expr_type());
1329 let elem_type = inner.list_element_type().cloned().unwrap_or(ExprType::INT);
1330 let mut tmp = crate::symbol_table::SymbolTable::new();
1331 tmp.set(&var_name, ExprValue::unresolved(elem_type))
1332 .map_err(|e| ExpressionError::new(e.to_string()))?;
1333 let mut combined: Vec<&SymbolTable> = self.symtabs.to_vec();
1334 combined.push(&tmp);
1335 let mut child = self.child_evaluator(&combined);
1336 if let Some(if_clause) = gen.ifs.first() {
1338 let cond = child.evaluate(if_clause)?;
1339 let cond_inner = unwrap_unresolved(&cond.expr_type());
1340 let is_bool_compatible = cond_inner == ExprType::BOOL
1341 || cond_inner.code() == crate::types::TypeCode::Unresolved
1342 || cond_inner.code() == crate::types::TypeCode::Any
1343 || (cond_inner.code() == crate::types::TypeCode::Union
1344 && cond_inner.params().contains(&ExprType::BOOL));
1345 if !is_bool_compatible {
1346 let err = ExpressionError::new(format!(
1347 "List comprehension filter must be a boolean, got {}",
1348 cond_inner
1349 ));
1350 return Err(if let Some(src) = self.expr_source {
1351 err.with_node(src, if_clause)
1352 } else {
1353 err
1354 });
1355 }
1356 }
1357 let body_val = child.evaluate(&lc.elt)?;
1358 self.absorb_counters(&child);
1359 let body_type = unwrap_unresolved(&body_val.expr_type());
1360 return self.track(ExprValue::unresolved(ExprType::list(body_type)));
1361 }
1362
1363 let items: Vec<ExprValue> = if let Some(iter) = iterable.list_iter() {
1365 iter.collect()
1366 } else if let ExprValue::RangeExpr(r) = &iterable {
1367 r.iter().map(ExprValue::Int).collect()
1368 } else {
1369 return Err(ExpressionError::type_error(format!(
1370 "Cannot iterate over {}",
1371 iterable.expr_type()
1372 )));
1373 };
1374 self.release(&iterable);
1375
1376 let mut result = Vec::new();
1378 let base_symtabs: Vec<&SymbolTable> = self.symtabs.to_vec();
1379 for item in &items {
1380 self.count_op()?;
1381 let mut tmp = crate::symbol_table::SymbolTable::new();
1382 tmp.set(&var_name, item.clone())
1383 .map_err(|e| ExpressionError::new(e.to_string()))?;
1384 let mut combined = base_symtabs.clone();
1385 combined.push(&tmp);
1386 let mut child = self.child_evaluator(&combined);
1387 child.regex_cache = std::mem::take(&mut self.regex_cache);
1388 let mut include = true;
1389 if let Some(if_clause) = gen.ifs.first() {
1390 let cond = child.evaluate(if_clause)?;
1391 if let ExprValue::Bool(b) = cond {
1392 include = b;
1393 } else {
1394 let err = ExpressionError::new(format!(
1395 "List comprehension filter must be a boolean, got {}",
1396 cond.expr_type()
1397 ));
1398 return Err(if let Some(src) = self.expr_source {
1399 err.with_node(src, if_clause)
1400 } else {
1401 err
1402 });
1403 }
1404 }
1405 if include {
1406 result.push(child.evaluate(&lc.elt)?);
1407 }
1408 self.absorb_counters(&child);
1409 self.regex_cache = child.regex_cache;
1410 self.current_memory = self.current_memory.saturating_sub(item.memory_size());
1411 }
1412
1413 for e in &result {
1415 let t = e.expr_type();
1416 if let Some(inner) = t.list_element_type() {
1417 if inner.list_element_type().is_some() {
1418 return Err(ExpressionError::new(
1419 "Lists may be nested at most 2 levels deep",
1420 ));
1421 }
1422 }
1423 }
1424 let elem_type = if result.is_empty() {
1425 ExprType::NULLTYPE
1426 } else {
1427 result[0].expr_type()
1428 };
1429 let list = ExprValue::make_list_checked(self, result, elem_type)?;
1430 self.track(list)
1431 }
1432
1433 fn eval_slice(&mut self, s: &ast::ExprSlice) -> Result<ExprValue, ExpressionError> {
1434 if let Some(step) = &s.step {
1435 let step_val = self.evaluate(step)?;
1436 if let ExprValue::Int(0) = step_val {
1437 return Err(ExpressionError::new("Slice step cannot be zero"));
1438 }
1439 }
1440 self.track(ExprValue::unresolved(ExprType::INT))
1441 }
1442}
1443
1444impl<'a> crate::function_library::EvalContext for Evaluator<'a> {
1447 fn path_format(&self) -> PathFormat {
1448 self.path_format
1449 }
1450 fn count_op(&mut self) -> Result<(), ExpressionError> {
1451 self.operation_count = self.operation_count.saturating_add(1);
1452 if self.operation_count > self.operation_limit {
1453 Err(ExpressionError::from_kind(
1454 ExpressionErrorKind::OperationLimitExceeded {
1455 count: self.operation_count,
1456 limit: self.operation_limit,
1457 },
1458 ))
1459 } else {
1460 Ok(())
1461 }
1462 }
1463 fn count_ops(&mut self, n: usize) -> Result<(), ExpressionError> {
1464 self.operation_count = self.operation_count.saturating_add(n);
1465 if self.operation_count > self.operation_limit {
1466 Err(ExpressionError::from_kind(
1467 ExpressionErrorKind::OperationLimitExceeded {
1468 count: self.operation_count,
1469 limit: self.operation_limit,
1470 },
1471 ))
1472 } else {
1473 Ok(())
1474 }
1475 }
1476 fn count_string_ops(&mut self, len: usize) -> Result<(), ExpressionError> {
1477 let ops = len.div_ceil(256);
1478 self.operation_count = self.operation_count.saturating_add(ops);
1479 if self.operation_count > self.operation_limit {
1480 Err(ExpressionError::from_kind(
1481 ExpressionErrorKind::OperationLimitExceeded {
1482 count: self.operation_count,
1483 limit: self.operation_limit,
1484 },
1485 ))
1486 } else {
1487 Ok(())
1488 }
1489 }
1490 fn check_memory(&self, bytes: usize) -> Result<(), ExpressionError> {
1491 let projected = self.current_memory.saturating_add(bytes);
1492 if projected > self.memory_limit {
1493 Err(ExpressionError::from_kind(
1494 ExpressionErrorKind::MemoryLimitExceeded {
1495 used: projected,
1496 limit: self.memory_limit,
1497 },
1498 ))
1499 } else {
1500 Ok(())
1501 }
1502 }
1503 fn get_or_compile_regex(&mut self, pattern: &str) -> Result<regex::Regex, ExpressionError> {
1504 if let Some(re) = self.regex_cache.get(pattern) {
1505 return Ok(re.clone());
1506 }
1507 let re = regex::RegexBuilder::new(pattern)
1508 .size_limit(1 << 20)
1509 .build()
1510 .map_err(|e| ExpressionError::new(format!("Invalid regex: {e}")))?;
1511 self.regex_cache.insert(pattern.to_string(), re.clone());
1512 Ok(re)
1513 }
1514}
1515
1516fn unwrap_unresolved(t: &ExprType) -> ExprType {
1518 if t.code() == crate::types::TypeCode::Unresolved && !t.params().is_empty() {
1519 t.params()[0].clone()
1520 } else {
1521 t.clone()
1522 }
1523}
1524
1525fn build_dotted_name_from_attr(a: &ast::ExprAttribute) -> Option<String> {
1529 let mut parts: Vec<&str> = vec![a.attr.as_str()];
1530 let mut current: &ast::Expr = &a.value;
1531 loop {
1532 match current {
1533 ast::Expr::Name(n) => {
1534 parts.push(n.id.as_str());
1535 break;
1536 }
1537 ast::Expr::Attribute(attr) => {
1538 parts.push(attr.attr.as_str());
1539 current = &attr.value;
1540 }
1541 _ => return None,
1542 }
1543 }
1544 parts.reverse();
1545 Some(parts.join("."))
1546}
1547
1548fn resolve_keyword_renames(
1549 name: &str,
1550 renames: &std::collections::HashMap<String, String>,
1551) -> String {
1552 if renames.is_empty() {
1553 return name.to_string();
1554 }
1555 let mut result = name.to_string();
1556 for (replacement, original) in renames {
1557 let from = format!(".{replacement}");
1559 let to = format!(".{original}");
1560 result = result.replace(&from, &to);
1561 }
1562 result
1563}
1564
1565use crate::types::ExprType;