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