1use crate as ast;
19use crate::{Expr, Number};
20use std::borrow::Cow;
21use std::hash::Hash;
22
23#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
24pub enum ComparableBoolOp {
25 And,
26 Or,
27}
28
29impl From<ast::BoolOp> for ComparableBoolOp {
30 fn from(op: ast::BoolOp) -> Self {
31 match op {
32 ast::BoolOp::And => Self::And,
33 ast::BoolOp::Or => Self::Or,
34 }
35 }
36}
37
38#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
39pub enum ComparableOperator {
40 Add,
41 Sub,
42 Mult,
43 MatMult,
44 Div,
45 Mod,
46 Pow,
47 LShift,
48 RShift,
49 BitOr,
50 BitXor,
51 BitAnd,
52 FloorDiv,
53}
54
55impl From<ast::Operator> for ComparableOperator {
56 fn from(op: ast::Operator) -> Self {
57 match op {
58 ast::Operator::Add => Self::Add,
59 ast::Operator::Sub => Self::Sub,
60 ast::Operator::Mult => Self::Mult,
61 ast::Operator::MatMult => Self::MatMult,
62 ast::Operator::Div => Self::Div,
63 ast::Operator::Mod => Self::Mod,
64 ast::Operator::Pow => Self::Pow,
65 ast::Operator::LShift => Self::LShift,
66 ast::Operator::RShift => Self::RShift,
67 ast::Operator::BitOr => Self::BitOr,
68 ast::Operator::BitXor => Self::BitXor,
69 ast::Operator::BitAnd => Self::BitAnd,
70 ast::Operator::FloorDiv => Self::FloorDiv,
71 }
72 }
73}
74
75#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
76pub enum ComparableUnaryOp {
77 Invert,
78 Not,
79 UAdd,
80 USub,
81}
82
83impl From<ast::UnaryOp> for ComparableUnaryOp {
84 fn from(op: ast::UnaryOp) -> Self {
85 match op {
86 ast::UnaryOp::Invert => Self::Invert,
87 ast::UnaryOp::Not => Self::Not,
88 ast::UnaryOp::UAdd => Self::UAdd,
89 ast::UnaryOp::USub => Self::USub,
90 }
91 }
92}
93
94#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
95pub enum ComparableCmpOp {
96 Eq,
97 NotEq,
98 Lt,
99 LtE,
100 Gt,
101 GtE,
102 Is,
103 IsNot,
104 In,
105 NotIn,
106}
107
108impl From<ast::CmpOp> for ComparableCmpOp {
109 fn from(op: ast::CmpOp) -> Self {
110 match op {
111 ast::CmpOp::Eq => Self::Eq,
112 ast::CmpOp::NotEq => Self::NotEq,
113 ast::CmpOp::Lt => Self::Lt,
114 ast::CmpOp::LtE => Self::LtE,
115 ast::CmpOp::Gt => Self::Gt,
116 ast::CmpOp::GtE => Self::GtE,
117 ast::CmpOp::Is => Self::Is,
118 ast::CmpOp::IsNot => Self::IsNot,
119 ast::CmpOp::In => Self::In,
120 ast::CmpOp::NotIn => Self::NotIn,
121 }
122 }
123}
124
125#[derive(Debug, PartialEq, Eq, Hash)]
126pub struct ComparableAlias<'a> {
127 name: &'a str,
128 asname: Option<&'a str>,
129}
130
131impl<'a> From<&'a ast::Alias> for ComparableAlias<'a> {
132 fn from(alias: &'a ast::Alias) -> Self {
133 Self {
134 name: alias.name.as_str(),
135 asname: alias.asname.as_deref(),
136 }
137 }
138}
139
140#[derive(Debug, PartialEq, Eq, Hash)]
141pub struct ComparableWithItem<'a> {
142 context_expr: ComparableExpr<'a>,
143 optional_vars: Option<ComparableExpr<'a>>,
144}
145
146impl<'a> From<&'a ast::WithItem> for ComparableWithItem<'a> {
147 fn from(with_item: &'a ast::WithItem) -> Self {
148 Self {
149 context_expr: (&with_item.context_expr).into(),
150 optional_vars: with_item.optional_vars.as_ref().map(Into::into),
151 }
152 }
153}
154
155#[derive(Debug, PartialEq, Eq, Hash)]
156pub struct ComparablePatternArguments<'a> {
157 patterns: Vec<ComparablePattern<'a>>,
158 keywords: Vec<ComparablePatternKeyword<'a>>,
159}
160
161impl<'a> From<&'a ast::PatternArguments> for ComparablePatternArguments<'a> {
162 fn from(parameters: &'a ast::PatternArguments) -> Self {
163 Self {
164 patterns: parameters.patterns.iter().map(Into::into).collect(),
165 keywords: parameters.keywords.iter().map(Into::into).collect(),
166 }
167 }
168}
169
170#[derive(Debug, PartialEq, Eq, Hash)]
171pub struct ComparablePatternKeyword<'a> {
172 attr: &'a str,
173 pattern: ComparablePattern<'a>,
174}
175
176impl<'a> From<&'a ast::PatternKeyword> for ComparablePatternKeyword<'a> {
177 fn from(keyword: &'a ast::PatternKeyword) -> Self {
178 Self {
179 attr: keyword.attr.as_str(),
180 pattern: (&keyword.pattern).into(),
181 }
182 }
183}
184
185#[derive(Debug, PartialEq, Eq, Hash)]
186pub struct PatternMatchValue<'a> {
187 value: ComparableExpr<'a>,
188}
189
190#[derive(Debug, PartialEq, Eq, Hash)]
191pub struct PatternMatchSingleton {
192 value: ComparableSingleton,
193}
194
195#[derive(Debug, PartialEq, Eq, Hash)]
196pub struct PatternMatchSequence<'a> {
197 patterns: Vec<ComparablePattern<'a>>,
198}
199
200#[derive(Debug, PartialEq, Eq, Hash)]
201pub struct PatternMatchMapping<'a> {
202 keys: Vec<ComparableExpr<'a>>,
203 patterns: Vec<ComparablePattern<'a>>,
204 rest: Option<&'a str>,
205}
206
207#[derive(Debug, PartialEq, Eq, Hash)]
208pub struct PatternMatchClass<'a> {
209 cls: ComparableExpr<'a>,
210 arguments: ComparablePatternArguments<'a>,
211}
212
213#[derive(Debug, PartialEq, Eq, Hash)]
214pub struct PatternMatchStar<'a> {
215 name: Option<&'a str>,
216}
217
218#[derive(Debug, PartialEq, Eq, Hash)]
219pub struct PatternMatchAs<'a> {
220 pattern: Option<Box<ComparablePattern<'a>>>,
221 name: Option<&'a str>,
222}
223
224#[derive(Debug, PartialEq, Eq, Hash)]
225pub struct PatternMatchOr<'a> {
226 patterns: Vec<ComparablePattern<'a>>,
227}
228
229#[derive(Debug, PartialEq, Eq, Hash)]
230pub enum ComparablePattern<'a> {
231 MatchValue(PatternMatchValue<'a>),
232 MatchSingleton(PatternMatchSingleton),
233 MatchSequence(PatternMatchSequence<'a>),
234 MatchMapping(PatternMatchMapping<'a>),
235 MatchClass(PatternMatchClass<'a>),
236 MatchStar(PatternMatchStar<'a>),
237 MatchAs(PatternMatchAs<'a>),
238 MatchOr(PatternMatchOr<'a>),
239}
240
241impl<'a> From<&'a ast::Pattern> for ComparablePattern<'a> {
242 fn from(pattern: &'a ast::Pattern) -> Self {
243 match pattern {
244 ast::Pattern::MatchValue(ast::PatternMatchValue { value, .. }) => {
245 Self::MatchValue(PatternMatchValue {
246 value: value.into(),
247 })
248 }
249 ast::Pattern::MatchSingleton(ast::PatternMatchSingleton { value, .. }) => {
250 Self::MatchSingleton(PatternMatchSingleton {
251 value: value.into(),
252 })
253 }
254 ast::Pattern::MatchSequence(ast::PatternMatchSequence { patterns, .. }) => {
255 Self::MatchSequence(PatternMatchSequence {
256 patterns: patterns.iter().map(Into::into).collect(),
257 })
258 }
259 ast::Pattern::MatchMapping(ast::PatternMatchMapping {
260 keys,
261 patterns,
262 rest,
263 ..
264 }) => Self::MatchMapping(PatternMatchMapping {
265 keys: keys.iter().map(Into::into).collect(),
266 patterns: patterns.iter().map(Into::into).collect(),
267 rest: rest.as_deref(),
268 }),
269 ast::Pattern::MatchClass(ast::PatternMatchClass { cls, arguments, .. }) => {
270 Self::MatchClass(PatternMatchClass {
271 cls: cls.into(),
272 arguments: arguments.into(),
273 })
274 }
275 ast::Pattern::MatchStar(ast::PatternMatchStar { name, .. }) => {
276 Self::MatchStar(PatternMatchStar {
277 name: name.as_deref(),
278 })
279 }
280 ast::Pattern::MatchAs(ast::PatternMatchAs { pattern, name, .. }) => {
281 Self::MatchAs(PatternMatchAs {
282 pattern: pattern.as_ref().map(Into::into),
283 name: name.as_deref(),
284 })
285 }
286 ast::Pattern::MatchOr(ast::PatternMatchOr { patterns, .. }) => {
287 Self::MatchOr(PatternMatchOr {
288 patterns: patterns.iter().map(Into::into).collect(),
289 })
290 }
291 }
292 }
293}
294
295impl<'a> From<&'a Box<ast::Pattern>> for Box<ComparablePattern<'a>> {
296 fn from(pattern: &'a Box<ast::Pattern>) -> Self {
297 Box::new((pattern.as_ref()).into())
298 }
299}
300
301#[derive(Debug, PartialEq, Eq, Hash)]
302pub struct ComparableMatchCase<'a> {
303 pattern: ComparablePattern<'a>,
304 guard: Option<ComparableExpr<'a>>,
305 body: Vec<ComparableStmt<'a>>,
306}
307
308impl<'a> From<&'a ast::MatchCase> for ComparableMatchCase<'a> {
309 fn from(match_case: &'a ast::MatchCase) -> Self {
310 Self {
311 pattern: (&match_case.pattern).into(),
312 guard: match_case.guard.as_ref().map(Into::into),
313 body: match_case.body.iter().map(Into::into).collect(),
314 }
315 }
316}
317
318#[derive(Debug, PartialEq, Eq, Hash)]
319pub struct ComparableDecorator<'a> {
320 expression: ComparableExpr<'a>,
321}
322
323impl<'a> From<&'a ast::Decorator> for ComparableDecorator<'a> {
324 fn from(decorator: &'a ast::Decorator) -> Self {
325 Self {
326 expression: (&decorator.expression).into(),
327 }
328 }
329}
330
331#[derive(Debug, PartialEq, Eq, Hash)]
332pub enum ComparableSingleton {
333 None,
334 True,
335 False,
336}
337
338impl From<&ast::Singleton> for ComparableSingleton {
339 fn from(singleton: &ast::Singleton) -> Self {
340 match singleton {
341 ast::Singleton::None => Self::None,
342 ast::Singleton::True => Self::True,
343 ast::Singleton::False => Self::False,
344 }
345 }
346}
347
348#[derive(Debug, PartialEq, Eq, Hash)]
349pub enum ComparableNumber<'a> {
350 Int(&'a ast::Int),
351 Float(u64),
352 Complex { real: u64, imag: u64 },
353}
354
355impl<'a> From<&'a ast::Number> for ComparableNumber<'a> {
356 fn from(number: &'a ast::Number) -> Self {
357 match number {
358 ast::Number::Int(value) => Self::Int(value),
359 ast::Number::Float(value) => Self::Float(value.to_bits()),
360 ast::Number::Complex { real, imag } => Self::Complex {
361 real: real.to_bits(),
362 imag: imag.to_bits(),
363 },
364 }
365 }
366}
367
368#[derive(Debug, Default, PartialEq, Eq, Hash)]
369pub struct ComparableArguments<'a> {
370 args: Vec<ComparableExpr<'a>>,
371 keywords: Vec<ComparableKeyword<'a>>,
372}
373
374impl<'a> From<&'a ast::Arguments> for ComparableArguments<'a> {
375 fn from(arguments: &'a ast::Arguments) -> Self {
376 Self {
377 args: arguments.args.iter().map(Into::into).collect(),
378 keywords: arguments.keywords.iter().map(Into::into).collect(),
379 }
380 }
381}
382
383impl<'a> From<&'a Box<ast::Arguments>> for ComparableArguments<'a> {
384 fn from(arguments: &'a Box<ast::Arguments>) -> Self {
385 (arguments.as_ref()).into()
386 }
387}
388
389#[derive(Debug, PartialEq, Eq, Hash)]
390pub struct ComparableParameters<'a> {
391 posonlyargs: Vec<ComparableParameterWithDefault<'a>>,
392 args: Vec<ComparableParameterWithDefault<'a>>,
393 vararg: Option<ComparableParameter<'a>>,
394 kwonlyargs: Vec<ComparableParameterWithDefault<'a>>,
395 kwarg: Option<ComparableParameter<'a>>,
396}
397
398impl<'a> From<&'a ast::Parameters> for ComparableParameters<'a> {
399 fn from(parameters: &'a ast::Parameters) -> Self {
400 Self {
401 posonlyargs: parameters.posonlyargs.iter().map(Into::into).collect(),
402 args: parameters.args.iter().map(Into::into).collect(),
403 vararg: parameters.vararg.as_ref().map(Into::into),
404 kwonlyargs: parameters.kwonlyargs.iter().map(Into::into).collect(),
405 kwarg: parameters.kwarg.as_ref().map(Into::into),
406 }
407 }
408}
409
410impl<'a> From<&'a Box<ast::Parameters>> for ComparableParameters<'a> {
411 fn from(parameters: &'a Box<ast::Parameters>) -> Self {
412 (parameters.as_ref()).into()
413 }
414}
415
416impl<'a> From<&'a Box<ast::Parameter>> for ComparableParameter<'a> {
417 fn from(arg: &'a Box<ast::Parameter>) -> Self {
418 (arg.as_ref()).into()
419 }
420}
421
422#[derive(Debug, PartialEq, Eq, Hash)]
423pub struct ComparableParameter<'a> {
424 arg: &'a str,
425 annotation: Option<Box<ComparableExpr<'a>>>,
426}
427
428impl<'a> From<&'a ast::Parameter> for ComparableParameter<'a> {
429 fn from(arg: &'a ast::Parameter) -> Self {
430 Self {
431 arg: arg.name.as_str(),
432 annotation: arg.annotation.as_ref().map(Into::into),
433 }
434 }
435}
436
437#[derive(Debug, PartialEq, Eq, Hash)]
438pub struct ComparableParameterWithDefault<'a> {
439 def: ComparableParameter<'a>,
440 default: Option<ComparableExpr<'a>>,
441}
442
443impl<'a> From<&'a ast::ParameterWithDefault> for ComparableParameterWithDefault<'a> {
444 fn from(arg: &'a ast::ParameterWithDefault) -> Self {
445 Self {
446 def: (&arg.parameter).into(),
447 default: arg.default.as_ref().map(Into::into),
448 }
449 }
450}
451
452#[derive(Debug, PartialEq, Eq, Hash)]
453pub struct ComparableKeyword<'a> {
454 arg: Option<&'a str>,
455 value: ComparableExpr<'a>,
456}
457
458impl<'a> From<&'a ast::Keyword> for ComparableKeyword<'a> {
459 fn from(keyword: &'a ast::Keyword) -> Self {
460 Self {
461 arg: keyword.arg.as_ref().map(ast::Identifier::as_str),
462 value: (&keyword.value).into(),
463 }
464 }
465}
466
467#[derive(Debug, PartialEq, Eq, Hash)]
468pub struct ComparableComprehension<'a> {
469 target: ComparableExpr<'a>,
470 iter: ComparableExpr<'a>,
471 ifs: Vec<ComparableExpr<'a>>,
472 is_async: bool,
473}
474
475impl<'a> From<&'a ast::Comprehension> for ComparableComprehension<'a> {
476 fn from(comprehension: &'a ast::Comprehension) -> Self {
477 Self {
478 target: (&comprehension.target).into(),
479 iter: (&comprehension.iter).into(),
480 ifs: comprehension.ifs.iter().map(Into::into).collect(),
481 is_async: comprehension.is_async,
482 }
483 }
484}
485
486#[derive(Debug, PartialEq, Eq, Hash)]
487pub struct ExceptHandlerExceptHandler<'a> {
488 type_: Option<Box<ComparableExpr<'a>>>,
489 name: Option<&'a str>,
490 body: Vec<ComparableStmt<'a>>,
491}
492
493#[derive(Debug, PartialEq, Eq, Hash)]
494pub enum ComparableExceptHandler<'a> {
495 ExceptHandler(ExceptHandlerExceptHandler<'a>),
496}
497
498impl<'a> From<&'a ast::ExceptHandler> for ComparableExceptHandler<'a> {
499 fn from(except_handler: &'a ast::ExceptHandler) -> Self {
500 let ast::ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler {
501 type_,
502 name,
503 body,
504 ..
505 }) = except_handler;
506 Self::ExceptHandler(ExceptHandlerExceptHandler {
507 type_: type_.as_ref().map(Into::into),
508 name: name.as_deref(),
509 body: body.iter().map(Into::into).collect(),
510 })
511 }
512}
513
514#[derive(Debug, PartialEq, Eq, Hash)]
515pub enum ComparableInterpolatedStringElement<'a> {
516 Literal(Cow<'a, str>),
517 InterpolatedElement(InterpolatedElement<'a>),
518}
519
520#[derive(Debug, PartialEq, Eq, Hash)]
527pub struct ComparableDebugText<'a> {
528 text: Cow<'a, str>,
529}
530
531impl<'a> From<&'a ast::DebugText> for ComparableDebugText<'a> {
532 fn from(debug_text: &'a ast::DebugText) -> Self {
533 Self {
536 text: normalize_newlines(debug_text.as_str()),
537 }
538 }
539}
540
541fn normalize_newlines(contents: &str) -> Cow<'_, str> {
542 if contents.contains('\r') {
543 Cow::Owned(contents.replace("\r\n", "\n").replace('\r', "\n"))
544 } else {
545 Cow::Borrowed(contents)
546 }
547}
548
549#[derive(Debug, PartialEq, Eq, Hash)]
550pub struct InterpolatedElement<'a> {
551 expression: ComparableExpr<'a>,
552 debug_text: Option<ComparableDebugText<'a>>,
553 conversion: ast::ConversionFlag,
554 format_spec: Option<Vec<ComparableInterpolatedStringElement<'a>>>,
555}
556
557impl<'a> From<&'a ast::InterpolatedStringElement> for ComparableInterpolatedStringElement<'a> {
558 fn from(interpolated_string_element: &'a ast::InterpolatedStringElement) -> Self {
559 match interpolated_string_element {
560 ast::InterpolatedStringElement::Literal(ast::InterpolatedStringLiteralElement {
561 value,
562 ..
563 }) => Self::Literal(value.as_ref().into()),
564 ast::InterpolatedStringElement::Interpolation(formatted_value) => {
565 formatted_value.into()
566 }
567 }
568 }
569}
570
571impl<'a> From<&'a ast::InterpolatedElement> for InterpolatedElement<'a> {
572 fn from(interpolated_element: &'a ast::InterpolatedElement) -> Self {
573 let ast::InterpolatedElement {
574 expression,
575 debug_text,
576 conversion,
577 format_spec,
578 range: _,
579 node_index: _,
580 } = interpolated_element;
581
582 Self {
583 expression: (expression).into(),
584 debug_text: debug_text.as_ref().map(Into::into),
585 conversion: *conversion,
586 format_spec: format_spec
587 .as_ref()
588 .map(|spec| spec.elements.iter().map(Into::into).collect()),
589 }
590 }
591}
592
593impl<'a> From<&'a ast::InterpolatedElement> for ComparableInterpolatedStringElement<'a> {
594 fn from(interpolated_element: &'a ast::InterpolatedElement) -> Self {
595 Self::InterpolatedElement(interpolated_element.into())
596 }
597}
598
599#[derive(Debug, PartialEq, Eq, Hash)]
600pub struct ComparableElifElseClause<'a> {
601 test: Option<ComparableExpr<'a>>,
602 body: Vec<ComparableStmt<'a>>,
603}
604
605impl<'a> From<&'a ast::ElifElseClause> for ComparableElifElseClause<'a> {
606 fn from(elif_else_clause: &'a ast::ElifElseClause) -> Self {
607 let ast::ElifElseClause {
608 range: _,
609 node_index: _,
610 test,
611 body,
612 } = elif_else_clause;
613 Self {
614 test: test.as_ref().map(Into::into),
615 body: body.iter().map(Into::into).collect(),
616 }
617 }
618}
619
620#[derive(Debug, PartialEq, Eq, Hash)]
621pub enum ComparableLiteral<'a> {
622 None,
623 Ellipsis,
624 Bool(&'a bool),
625 Str(Vec<ComparableStringLiteral<'a>>),
626 Bytes(Vec<ComparableBytesLiteral<'a>>),
627 Number(ComparableNumber<'a>),
628}
629
630impl<'a> From<ast::LiteralExpressionRef<'a>> for ComparableLiteral<'a> {
631 fn from(literal: ast::LiteralExpressionRef<'a>) -> Self {
632 match literal {
633 ast::LiteralExpressionRef::NoneLiteral(_) => Self::None,
634 ast::LiteralExpressionRef::EllipsisLiteral(_) => Self::Ellipsis,
635 ast::LiteralExpressionRef::BooleanLiteral(ast::ExprBooleanLiteral {
636 value, ..
637 }) => Self::Bool(value),
638 ast::LiteralExpressionRef::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
639 Self::Str(value.iter().map(Into::into).collect())
640 }
641 ast::LiteralExpressionRef::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => {
642 Self::Bytes(value.iter().map(Into::into).collect())
643 }
644 ast::LiteralExpressionRef::NumberLiteral(ast::ExprNumberLiteral { value, .. }) => {
645 Self::Number(value.into())
646 }
647 }
648 }
649}
650
651#[derive(Debug, PartialEq, Eq, Hash)]
652pub struct ComparableFString<'a> {
653 elements: Box<[ComparableInterpolatedStringElement<'a>]>,
654}
655
656impl<'a> From<&'a ast::FStringValue> for ComparableFString<'a> {
657 fn from(value: &'a ast::FStringValue) -> Self {
678 #[derive(Default)]
679 struct Collector<'a> {
680 elements: Vec<ComparableInterpolatedStringElement<'a>>,
681 }
682
683 impl<'a> Collector<'a> {
684 fn push_literal(&mut self, literal: &'a str) {
690 if let Some(ComparableInterpolatedStringElement::Literal(existing_literal)) =
691 self.elements.last_mut()
692 {
693 existing_literal.to_mut().push_str(literal);
694 } else {
695 self.elements
696 .push(ComparableInterpolatedStringElement::Literal(literal.into()));
697 }
698 }
699
700 fn push_expression(&mut self, expression: &'a ast::InterpolatedElement) {
701 self.elements.push(expression.into());
702 }
703 }
704
705 let mut collector = Collector::default();
706
707 for part in value {
708 match part {
709 ast::FStringPart::Literal(string_literal) => {
710 collector.push_literal(&string_literal.value);
711 }
712 ast::FStringPart::FString(fstring) => {
713 for element in &fstring.elements {
714 match element {
715 ast::InterpolatedStringElement::Literal(literal) => {
716 collector.push_literal(&literal.value);
717 }
718 ast::InterpolatedStringElement::Interpolation(expression) => {
719 collector.push_expression(expression);
720 }
721 }
722 }
723 }
724 }
725 }
726
727 Self {
728 elements: collector.elements.into_boxed_slice(),
729 }
730 }
731}
732
733#[derive(Debug, PartialEq, Eq, Hash)]
734pub struct ComparableTString<'a> {
735 strings: Box<[ComparableInterpolatedStringElement<'a>]>,
736 interpolations: Box<[InterpolatedElement<'a>]>,
737}
738
739impl<'a> From<&'a ast::TStringValue> for ComparableTString<'a> {
740 fn from(value: &'a ast::TStringValue) -> Self {
751 struct Collector<'a> {
752 strings: Vec<ComparableInterpolatedStringElement<'a>>,
753 interpolations: Vec<InterpolatedElement<'a>>,
754 }
755
756 impl Default for Collector<'_> {
757 fn default() -> Self {
758 Self {
759 strings: vec![ComparableInterpolatedStringElement::Literal("".into())],
760 interpolations: vec![],
761 }
762 }
763 }
764
765 impl<'a> Collector<'a> {
766 fn push_literal(&mut self, literal: &'a str) {
772 if let Some(ComparableInterpolatedStringElement::Literal(existing_literal)) =
773 self.strings.last_mut()
774 {
775 existing_literal.to_mut().push_str(literal);
776 } else {
777 self.strings
778 .push(ComparableInterpolatedStringElement::Literal(literal.into()));
779 }
780 }
781
782 fn start_new_literal(&mut self) {
783 self.strings
784 .push(ComparableInterpolatedStringElement::Literal("".into()));
785 }
786
787 fn push_tstring_interpolation(&mut self, expression: &'a ast::InterpolatedElement) {
788 self.interpolations.push(expression.into());
789 self.start_new_literal();
790 }
791 }
792
793 let mut collector = Collector::default();
794
795 for element in value.elements() {
796 match element {
797 ast::InterpolatedStringElement::Literal(literal) => {
798 collector.push_literal(&literal.value);
799 }
800 ast::InterpolatedStringElement::Interpolation(interpolation) => {
801 collector.push_tstring_interpolation(interpolation);
802 }
803 }
804 }
805
806 Self {
807 strings: collector.strings.into_boxed_slice(),
808 interpolations: collector.interpolations.into_boxed_slice(),
809 }
810 }
811}
812
813#[derive(Debug, PartialEq, Eq, Hash)]
814pub struct ComparableStringLiteral<'a> {
815 value: &'a str,
816}
817
818impl<'a> From<&'a ast::StringLiteral> for ComparableStringLiteral<'a> {
819 fn from(string_literal: &'a ast::StringLiteral) -> Self {
820 Self {
821 value: &string_literal.value,
822 }
823 }
824}
825
826#[derive(Debug, PartialEq, Eq, Hash)]
827pub struct ComparableBytesLiteral<'a> {
828 value: Cow<'a, [u8]>,
829}
830
831impl<'a> From<&'a ast::BytesLiteral> for ComparableBytesLiteral<'a> {
832 fn from(bytes_literal: &'a ast::BytesLiteral) -> Self {
833 Self {
834 value: Cow::Borrowed(&bytes_literal.value),
835 }
836 }
837}
838
839#[derive(Debug, PartialEq, Eq, Hash)]
840pub struct ExprBoolOp<'a> {
841 op: ComparableBoolOp,
842 values: Vec<ComparableExpr<'a>>,
843}
844
845#[derive(Debug, PartialEq, Eq, Hash)]
846pub struct ExprNamed<'a> {
847 target: Box<ComparableExpr<'a>>,
848 value: Box<ComparableExpr<'a>>,
849}
850
851#[derive(Debug, PartialEq, Eq, Hash)]
852pub struct ExprBinOp<'a> {
853 left: Box<ComparableExpr<'a>>,
854 op: ComparableOperator,
855 right: Box<ComparableExpr<'a>>,
856}
857
858#[derive(Debug, PartialEq, Eq, Hash)]
859pub struct ExprUnaryOp<'a> {
860 op: ComparableUnaryOp,
861 operand: Box<ComparableExpr<'a>>,
862}
863
864#[derive(Debug, PartialEq, Eq, Hash)]
865pub struct ExprLambda<'a> {
866 parameters: Option<ComparableParameters<'a>>,
867 body: Box<ComparableExpr<'a>>,
868}
869
870#[derive(Debug, PartialEq, Eq, Hash)]
871pub struct ExprIf<'a> {
872 test: Box<ComparableExpr<'a>>,
873 body: Box<ComparableExpr<'a>>,
874 orelse: Box<ComparableExpr<'a>>,
875}
876
877#[derive(Debug, PartialEq, Eq, Hash)]
878pub struct ComparableDictItem<'a> {
879 key: Option<ComparableExpr<'a>>,
880 value: ComparableExpr<'a>,
881}
882
883impl<'a> From<&'a ast::DictItem> for ComparableDictItem<'a> {
884 fn from(ast::DictItem { key, value }: &'a ast::DictItem) -> Self {
885 Self {
886 key: key.as_ref().map(ComparableExpr::from),
887 value: value.into(),
888 }
889 }
890}
891
892#[derive(Debug, PartialEq, Eq, Hash)]
893pub struct ExprDict<'a> {
894 items: Vec<ComparableDictItem<'a>>,
895}
896
897#[derive(Debug, PartialEq, Eq, Hash)]
898pub struct ExprSet<'a> {
899 elts: Vec<ComparableExpr<'a>>,
900}
901
902#[derive(Debug, PartialEq, Eq, Hash)]
903pub struct ExprListComp<'a> {
904 elt: Box<ComparableExpr<'a>>,
905 generators: Vec<ComparableComprehension<'a>>,
906}
907
908#[derive(Debug, PartialEq, Eq, Hash)]
909pub struct ExprSetComp<'a> {
910 elt: Box<ComparableExpr<'a>>,
911 generators: Vec<ComparableComprehension<'a>>,
912}
913
914#[derive(Debug, PartialEq, Eq, Hash)]
915pub struct ExprDictComp<'a> {
916 key: Option<Box<ComparableExpr<'a>>>,
917 value: Box<ComparableExpr<'a>>,
918 generators: Vec<ComparableComprehension<'a>>,
919}
920
921#[derive(Debug, PartialEq, Eq, Hash)]
922pub struct ExprGenerator<'a> {
923 elt: Box<ComparableExpr<'a>>,
924 generators: Vec<ComparableComprehension<'a>>,
925}
926
927#[derive(Debug, PartialEq, Eq, Hash)]
928pub struct ExprAwait<'a> {
929 value: Box<ComparableExpr<'a>>,
930}
931
932#[derive(Debug, PartialEq, Eq, Hash)]
933pub struct ExprYield<'a> {
934 value: Option<Box<ComparableExpr<'a>>>,
935}
936
937#[derive(Debug, PartialEq, Eq, Hash)]
938pub struct ExprYieldFrom<'a> {
939 value: Box<ComparableExpr<'a>>,
940}
941
942#[derive(Debug, PartialEq, Eq, Hash)]
943pub struct ExprCompare<'a> {
944 left: Box<ComparableExpr<'a>>,
945 ops: Vec<ComparableCmpOp>,
946 comparators: Vec<ComparableExpr<'a>>,
947}
948
949#[derive(Debug, PartialEq, Eq, Hash)]
950pub struct ExprCall<'a> {
951 func: Box<ComparableExpr<'a>>,
952 arguments: ComparableArguments<'a>,
953}
954
955#[derive(Debug, PartialEq, Eq, Hash)]
956pub struct ExprInterpolatedElement<'a> {
957 value: Box<ComparableExpr<'a>>,
958 debug_text: Option<ComparableDebugText<'a>>,
959 conversion: ast::ConversionFlag,
960 format_spec: Vec<ComparableInterpolatedStringElement<'a>>,
961}
962
963#[derive(Debug, PartialEq, Eq, Hash)]
964pub struct ExprFString<'a> {
965 value: ComparableFString<'a>,
966}
967
968#[derive(Debug, PartialEq, Eq, Hash)]
969pub struct ExprTString<'a> {
970 value: ComparableTString<'a>,
971}
972
973#[derive(Debug, PartialEq, Eq, Hash)]
974pub struct ExprStringLiteral<'a> {
975 value: ComparableStringLiteral<'a>,
976}
977
978#[derive(Debug, PartialEq, Eq, Hash)]
979pub struct ExprBytesLiteral<'a> {
980 value: ComparableBytesLiteral<'a>,
981}
982
983#[derive(Debug, PartialEq, Eq, Hash)]
984pub struct ExprNumberLiteral<'a> {
985 value: ComparableNumber<'a>,
986}
987
988#[derive(Debug, PartialEq, Eq, Hash)]
989pub struct ExprBoolLiteral {
990 value: bool,
991}
992
993#[derive(Debug, PartialEq, Eq, Hash)]
994pub struct ExprAttribute<'a> {
995 value: Box<ComparableExpr<'a>>,
996 attr: &'a str,
997}
998
999#[derive(Debug, PartialEq, Eq, Hash)]
1000pub struct ExprSubscript<'a> {
1001 value: Box<ComparableExpr<'a>>,
1002 slice: Box<ComparableExpr<'a>>,
1003}
1004
1005#[derive(Debug, PartialEq, Eq, Hash)]
1006pub struct ExprStarred<'a> {
1007 value: Box<ComparableExpr<'a>>,
1008}
1009
1010#[derive(Debug, PartialEq, Eq, Hash)]
1011pub struct ExprName<'a> {
1012 id: &'a str,
1013}
1014
1015#[derive(Debug, PartialEq, Eq, Hash)]
1016pub struct ExprList<'a> {
1017 elts: Vec<ComparableExpr<'a>>,
1018}
1019
1020#[derive(Debug, PartialEq, Eq, Hash)]
1021pub struct ExprTuple<'a> {
1022 elts: Vec<ComparableExpr<'a>>,
1023}
1024
1025#[derive(Debug, PartialEq, Eq, Hash)]
1026pub struct ExprSlice<'a> {
1027 lower: Option<Box<ComparableExpr<'a>>>,
1028 upper: Option<Box<ComparableExpr<'a>>>,
1029 step: Option<Box<ComparableExpr<'a>>>,
1030}
1031
1032#[derive(Debug, PartialEq, Eq, Hash)]
1033pub struct ExprIpyEscapeCommand<'a> {
1034 kind: ast::IpyEscapeKind,
1035 value: &'a str,
1036}
1037
1038#[derive(Debug, PartialEq, Eq, Hash)]
1039pub enum ComparableExpr<'a> {
1040 BoolOp(ExprBoolOp<'a>),
1041 NamedExpr(ExprNamed<'a>),
1042 BinOp(ExprBinOp<'a>),
1043 UnaryOp(ExprUnaryOp<'a>),
1044 Lambda(ExprLambda<'a>),
1045 IfExp(ExprIf<'a>),
1046 Dict(ExprDict<'a>),
1047 Set(ExprSet<'a>),
1048 ListComp(ExprListComp<'a>),
1049 SetComp(ExprSetComp<'a>),
1050 DictComp(ExprDictComp<'a>),
1051 GeneratorExp(ExprGenerator<'a>),
1052 Await(ExprAwait<'a>),
1053 Yield(ExprYield<'a>),
1054 YieldFrom(ExprYieldFrom<'a>),
1055 Compare(ExprCompare<'a>),
1056 Call(ExprCall<'a>),
1057 FStringExpressionElement(ExprInterpolatedElement<'a>),
1058 FString(ExprFString<'a>),
1059 TStringInterpolationElement(ExprInterpolatedElement<'a>),
1060 TString(ExprTString<'a>),
1061 StringLiteral(ExprStringLiteral<'a>),
1062 BytesLiteral(ExprBytesLiteral<'a>),
1063 NumberLiteral(ExprNumberLiteral<'a>),
1064 BoolLiteral(ExprBoolLiteral),
1065 NoneLiteral,
1066 EllipsisLiteral,
1067 Attribute(ExprAttribute<'a>),
1068 Subscript(ExprSubscript<'a>),
1069 Starred(ExprStarred<'a>),
1070 Name(ExprName<'a>),
1071 List(ExprList<'a>),
1072 Tuple(ExprTuple<'a>),
1073 Slice(ExprSlice<'a>),
1074 IpyEscapeCommand(ExprIpyEscapeCommand<'a>),
1075}
1076
1077impl<'a> From<&'a Box<ast::Expr>> for Box<ComparableExpr<'a>> {
1078 fn from(expr: &'a Box<ast::Expr>) -> Self {
1079 Box::new((expr.as_ref()).into())
1080 }
1081}
1082
1083impl<'a> From<&'a Box<ast::Expr>> for ComparableExpr<'a> {
1084 fn from(expr: &'a Box<ast::Expr>) -> Self {
1085 (expr.as_ref()).into()
1086 }
1087}
1088
1089impl<'a> From<&'a ast::Expr> for ComparableExpr<'a> {
1090 fn from(expr: &'a ast::Expr) -> Self {
1091 match expr {
1092 ast::Expr::BoolOp(ast::ExprBoolOp {
1093 op,
1094 values,
1095 range: _,
1096 node_index: _,
1097 }) => Self::BoolOp(ExprBoolOp {
1098 op: (*op).into(),
1099 values: values.iter().map(Into::into).collect(),
1100 }),
1101 ast::Expr::Named(ast::ExprNamed {
1102 target,
1103 value,
1104 range: _,
1105 node_index: _,
1106 }) => Self::NamedExpr(ExprNamed {
1107 target: target.into(),
1108 value: value.into(),
1109 }),
1110 ast::Expr::BinOp(ast::ExprBinOp {
1111 left,
1112 op,
1113 right,
1114 range: _,
1115 node_index: _,
1116 }) => Self::BinOp(ExprBinOp {
1117 left: left.into(),
1118 op: (*op).into(),
1119 right: right.into(),
1120 }),
1121 ast::Expr::UnaryOp(ast::ExprUnaryOp {
1122 op,
1123 operand,
1124 range: _,
1125 node_index: _,
1126 }) => Self::UnaryOp(ExprUnaryOp {
1127 op: (*op).into(),
1128 operand: operand.into(),
1129 }),
1130 ast::Expr::Lambda(ast::ExprLambda {
1131 parameters,
1132 body,
1133 range: _,
1134 node_index: _,
1135 }) => Self::Lambda(ExprLambda {
1136 parameters: parameters.as_ref().map(Into::into),
1137 body: body.into(),
1138 }),
1139 ast::Expr::If(ast::ExprIf {
1140 test,
1141 body,
1142 orelse,
1143 range: _,
1144 node_index: _,
1145 }) => Self::IfExp(ExprIf {
1146 test: test.into(),
1147 body: body.into(),
1148 orelse: orelse.into(),
1149 }),
1150 ast::Expr::Dict(ast::ExprDict {
1151 items,
1152 range: _,
1153 node_index: _,
1154 }) => Self::Dict(ExprDict {
1155 items: items.iter().map(ComparableDictItem::from).collect(),
1156 }),
1157 ast::Expr::Set(ast::ExprSet {
1158 elts,
1159 range: _,
1160 node_index: _,
1161 }) => Self::Set(ExprSet {
1162 elts: elts.iter().map(Into::into).collect(),
1163 }),
1164 ast::Expr::ListComp(ast::ExprListComp {
1165 elt,
1166 generators,
1167 range: _,
1168 node_index: _,
1169 }) => Self::ListComp(ExprListComp {
1170 elt: elt.into(),
1171 generators: generators.iter().map(Into::into).collect(),
1172 }),
1173 ast::Expr::SetComp(ast::ExprSetComp {
1174 elt,
1175 generators,
1176 range: _,
1177 node_index: _,
1178 }) => Self::SetComp(ExprSetComp {
1179 elt: elt.into(),
1180 generators: generators.iter().map(Into::into).collect(),
1181 }),
1182 ast::Expr::DictComp(ast::ExprDictComp {
1183 key,
1184 value,
1185 generators,
1186 range: _,
1187 node_index: _,
1188 }) => Self::DictComp(ExprDictComp {
1189 key: key.as_ref().map(Into::into),
1190 value: value.into(),
1191 generators: generators.iter().map(Into::into).collect(),
1192 }),
1193 ast::Expr::Generator(ast::ExprGenerator {
1194 elt,
1195 generators,
1196 range: _,
1197 node_index: _,
1198 parenthesized: _,
1199 }) => Self::GeneratorExp(ExprGenerator {
1200 elt: elt.into(),
1201 generators: generators.iter().map(Into::into).collect(),
1202 }),
1203 ast::Expr::Await(ast::ExprAwait {
1204 value,
1205 range: _,
1206 node_index: _,
1207 }) => Self::Await(ExprAwait {
1208 value: value.into(),
1209 }),
1210 ast::Expr::Yield(ast::ExprYield {
1211 value,
1212 range: _,
1213 node_index: _,
1214 }) => Self::Yield(ExprYield {
1215 value: value.as_ref().map(Into::into),
1216 }),
1217 ast::Expr::YieldFrom(ast::ExprYieldFrom {
1218 value,
1219 range: _,
1220 node_index: _,
1221 }) => Self::YieldFrom(ExprYieldFrom {
1222 value: value.into(),
1223 }),
1224 ast::Expr::Compare(ast::ExprCompare {
1225 left,
1226 ops,
1227 comparators,
1228 range: _,
1229 node_index: _,
1230 }) => Self::Compare(ExprCompare {
1231 left: left.into(),
1232 ops: ops.iter().copied().map(Into::into).collect(),
1233 comparators: comparators.iter().map(Into::into).collect(),
1234 }),
1235 ast::Expr::Call(ast::ExprCall {
1236 func,
1237 arguments,
1238 range_start: _,
1239 node_index: _,
1240 }) => Self::Call(ExprCall {
1241 func: func.into(),
1242 arguments: arguments.into(),
1243 }),
1244 ast::Expr::FString(ast::ExprFString {
1245 value,
1246 range: _,
1247 node_index: _,
1248 }) => Self::FString(ExprFString {
1249 value: value.into(),
1250 }),
1251 ast::Expr::TString(ast::ExprTString {
1252 value,
1253 range: _,
1254 node_index: _,
1255 }) => Self::TString(ExprTString {
1256 value: value.into(),
1257 }),
1258 ast::Expr::StringLiteral(ast::ExprStringLiteral {
1259 value,
1260 range: _,
1261 node_index: _,
1262 }) => Self::StringLiteral(ExprStringLiteral {
1263 value: ComparableStringLiteral {
1264 value: value.to_str(),
1265 },
1266 }),
1267 ast::Expr::BytesLiteral(ast::ExprBytesLiteral {
1268 value,
1269 range: _,
1270 node_index: _,
1271 }) => Self::BytesLiteral(ExprBytesLiteral {
1272 value: ComparableBytesLiteral {
1273 value: Cow::from(value),
1274 },
1275 }),
1276 ast::Expr::NumberLiteral(ast::ExprNumberLiteral {
1277 value,
1278 range: _,
1279 node_index: _,
1280 }) => Self::NumberLiteral(ExprNumberLiteral {
1281 value: value.into(),
1282 }),
1283 ast::Expr::BooleanLiteral(ast::ExprBooleanLiteral {
1284 value,
1285 range: _,
1286 node_index: _,
1287 }) => Self::BoolLiteral(ExprBoolLiteral { value: *value }),
1288 ast::Expr::NoneLiteral(_) => Self::NoneLiteral,
1289 ast::Expr::EllipsisLiteral(_) => Self::EllipsisLiteral,
1290 ast::Expr::Attribute(ast::ExprAttribute {
1291 value,
1292 attr,
1293 ctx: _,
1294 range: _,
1295 node_index: _,
1296 }) => Self::Attribute(ExprAttribute {
1297 value: value.into(),
1298 attr: attr.as_str(),
1299 }),
1300 ast::Expr::Subscript(ast::ExprSubscript {
1301 value,
1302 slice,
1303 ctx: _,
1304 range: _,
1305 node_index: _,
1306 }) => Self::Subscript(ExprSubscript {
1307 value: value.into(),
1308 slice: slice.into(),
1309 }),
1310 ast::Expr::Starred(ast::ExprStarred {
1311 value,
1312 ctx: _,
1313 range: _,
1314 node_index: _,
1315 }) => Self::Starred(ExprStarred {
1316 value: value.into(),
1317 }),
1318 ast::Expr::Name(name) => name.into(),
1319 ast::Expr::List(ast::ExprList {
1320 elts,
1321 ctx: _,
1322 range: _,
1323 node_index: _,
1324 }) => Self::List(ExprList {
1325 elts: elts.iter().map(Into::into).collect(),
1326 }),
1327 ast::Expr::Tuple(ast::ExprTuple {
1328 elts,
1329 ctx: _,
1330 range: _,
1331 node_index: _,
1332 parenthesized: _,
1333 }) => Self::Tuple(ExprTuple {
1334 elts: elts.iter().map(Into::into).collect(),
1335 }),
1336 ast::Expr::Slice(ast::ExprSlice {
1337 lower,
1338 upper,
1339 step,
1340 range: _,
1341 node_index: _,
1342 }) => Self::Slice(ExprSlice {
1343 lower: lower.as_ref().map(Into::into),
1344 upper: upper.as_ref().map(Into::into),
1345 step: step.as_ref().map(Into::into),
1346 }),
1347 ast::Expr::IpyEscapeCommand(ast::ExprIpyEscapeCommand {
1348 kind,
1349 value,
1350 range: _,
1351 node_index: _,
1352 }) => Self::IpyEscapeCommand(ExprIpyEscapeCommand { kind: *kind, value }),
1353 }
1354 }
1355}
1356
1357impl<'a> From<&'a ast::ExprName> for ComparableExpr<'a> {
1358 fn from(expr: &'a ast::ExprName) -> Self {
1359 Self::Name(ExprName {
1360 id: expr.id.as_str(),
1361 })
1362 }
1363}
1364
1365#[derive(Debug, PartialEq, Eq, Hash)]
1366pub struct StmtFunctionDef<'a> {
1367 is_async: bool,
1368 decorator_list: Vec<ComparableDecorator<'a>>,
1369 name: &'a str,
1370 type_params: Option<ComparableTypeParams<'a>>,
1371 parameters: ComparableParameters<'a>,
1372 returns: Option<ComparableExpr<'a>>,
1373 body: Vec<ComparableStmt<'a>>,
1374}
1375
1376#[derive(Debug, PartialEq, Eq, Hash)]
1377pub struct StmtClassDef<'a> {
1378 decorator_list: Vec<ComparableDecorator<'a>>,
1379 name: &'a str,
1380 type_params: Option<ComparableTypeParams<'a>>,
1381 arguments: ComparableArguments<'a>,
1382 body: Vec<ComparableStmt<'a>>,
1383}
1384
1385#[derive(Debug, PartialEq, Eq, Hash)]
1386pub struct StmtReturn<'a> {
1387 value: Option<ComparableExpr<'a>>,
1388}
1389
1390#[derive(Debug, PartialEq, Eq, Hash)]
1391pub struct StmtDelete<'a> {
1392 targets: Vec<ComparableExpr<'a>>,
1393}
1394
1395#[derive(Debug, PartialEq, Eq, Hash)]
1396pub struct StmtTypeAlias<'a> {
1397 pub name: Box<ComparableExpr<'a>>,
1398 pub type_params: Option<ComparableTypeParams<'a>>,
1399 pub value: Box<ComparableExpr<'a>>,
1400}
1401
1402#[derive(Debug, PartialEq, Eq, Hash)]
1403pub struct ComparableTypeParams<'a> {
1404 pub type_params: Vec<ComparableTypeParam<'a>>,
1405}
1406
1407impl<'a> From<&'a ast::TypeParams> for ComparableTypeParams<'a> {
1408 fn from(type_params: &'a ast::TypeParams) -> Self {
1409 Self {
1410 type_params: type_params.iter().map(Into::into).collect(),
1411 }
1412 }
1413}
1414
1415impl<'a> From<&'a Box<ast::TypeParams>> for ComparableTypeParams<'a> {
1416 fn from(type_params: &'a Box<ast::TypeParams>) -> Self {
1417 type_params.as_ref().into()
1418 }
1419}
1420
1421#[derive(Debug, PartialEq, Eq, Hash)]
1422pub enum ComparableTypeParam<'a> {
1423 TypeVar(TypeParamTypeVar<'a>),
1424 ParamSpec(TypeParamParamSpec<'a>),
1425 TypeVarTuple(TypeParamTypeVarTuple<'a>),
1426}
1427
1428impl<'a> From<&'a ast::TypeParam> for ComparableTypeParam<'a> {
1429 fn from(type_param: &'a ast::TypeParam) -> Self {
1430 match type_param {
1431 ast::TypeParam::TypeVar(ast::TypeParamTypeVar {
1432 name,
1433 bound,
1434 default,
1435 range: _,
1436 node_index: _,
1437 }) => Self::TypeVar(TypeParamTypeVar {
1438 name: name.as_str(),
1439 bound: bound.as_ref().map(Into::into),
1440 default: default.as_ref().map(Into::into),
1441 }),
1442 ast::TypeParam::TypeVarTuple(ast::TypeParamTypeVarTuple {
1443 name,
1444 default,
1445 range: _,
1446 node_index: _,
1447 }) => Self::TypeVarTuple(TypeParamTypeVarTuple {
1448 name: name.as_str(),
1449 default: default.as_ref().map(Into::into),
1450 }),
1451 ast::TypeParam::ParamSpec(ast::TypeParamParamSpec {
1452 name,
1453 default,
1454 range: _,
1455 node_index: _,
1456 }) => Self::ParamSpec(TypeParamParamSpec {
1457 name: name.as_str(),
1458 default: default.as_ref().map(Into::into),
1459 }),
1460 }
1461 }
1462}
1463
1464#[derive(Debug, PartialEq, Eq, Hash)]
1465pub struct TypeParamTypeVar<'a> {
1466 pub name: &'a str,
1467 pub bound: Option<Box<ComparableExpr<'a>>>,
1468 pub default: Option<Box<ComparableExpr<'a>>>,
1469}
1470
1471#[derive(Debug, PartialEq, Eq, Hash)]
1472pub struct TypeParamParamSpec<'a> {
1473 pub name: &'a str,
1474 pub default: Option<Box<ComparableExpr<'a>>>,
1475}
1476
1477#[derive(Debug, PartialEq, Eq, Hash)]
1478pub struct TypeParamTypeVarTuple<'a> {
1479 pub name: &'a str,
1480 pub default: Option<Box<ComparableExpr<'a>>>,
1481}
1482
1483#[derive(Debug, PartialEq, Eq, Hash)]
1484pub struct StmtAssign<'a> {
1485 targets: Vec<ComparableExpr<'a>>,
1486 value: ComparableExpr<'a>,
1487}
1488
1489#[derive(Debug, PartialEq, Eq, Hash)]
1490pub struct StmtAugAssign<'a> {
1491 target: ComparableExpr<'a>,
1492 op: ComparableOperator,
1493 value: ComparableExpr<'a>,
1494}
1495
1496#[derive(Debug, PartialEq, Eq, Hash)]
1497pub struct StmtAnnAssign<'a> {
1498 target: ComparableExpr<'a>,
1499 annotation: ComparableExpr<'a>,
1500 value: Option<ComparableExpr<'a>>,
1501 simple: bool,
1502}
1503
1504#[derive(Debug, PartialEq, Eq, Hash)]
1505pub struct StmtFor<'a> {
1506 is_async: bool,
1507 target: ComparableExpr<'a>,
1508 iter: ComparableExpr<'a>,
1509 body: Vec<ComparableStmt<'a>>,
1510 orelse: Vec<ComparableStmt<'a>>,
1511}
1512
1513#[derive(Debug, PartialEq, Eq, Hash)]
1514pub struct StmtWhile<'a> {
1515 test: ComparableExpr<'a>,
1516 body: Vec<ComparableStmt<'a>>,
1517 orelse: Vec<ComparableStmt<'a>>,
1518}
1519
1520#[derive(Debug, PartialEq, Eq, Hash)]
1521pub struct StmtIf<'a> {
1522 test: ComparableExpr<'a>,
1523 body: Vec<ComparableStmt<'a>>,
1524 elif_else_clauses: Vec<ComparableElifElseClause<'a>>,
1525}
1526
1527#[derive(Debug, PartialEq, Eq, Hash)]
1528pub struct StmtWith<'a> {
1529 is_async: bool,
1530 items: Vec<ComparableWithItem<'a>>,
1531 body: Vec<ComparableStmt<'a>>,
1532}
1533
1534#[derive(Debug, PartialEq, Eq, Hash)]
1535pub struct StmtMatch<'a> {
1536 subject: ComparableExpr<'a>,
1537 cases: Vec<ComparableMatchCase<'a>>,
1538}
1539
1540#[derive(Debug, PartialEq, Eq, Hash)]
1541pub struct StmtRaise<'a> {
1542 exc: Option<ComparableExpr<'a>>,
1543 cause: Option<ComparableExpr<'a>>,
1544}
1545
1546#[derive(Debug, PartialEq, Eq, Hash)]
1547pub struct StmtTry<'a> {
1548 body: Vec<ComparableStmt<'a>>,
1549 handlers: Vec<ComparableExceptHandler<'a>>,
1550 orelse: Vec<ComparableStmt<'a>>,
1551 finalbody: Vec<ComparableStmt<'a>>,
1552 is_star: bool,
1553}
1554
1555#[derive(Debug, PartialEq, Eq, Hash)]
1556pub struct StmtAssert<'a> {
1557 test: ComparableExpr<'a>,
1558 msg: Option<ComparableExpr<'a>>,
1559}
1560
1561#[derive(Debug, PartialEq, Eq, Hash)]
1562pub struct StmtImport<'a> {
1563 names: Vec<ComparableAlias<'a>>,
1564 is_lazy: bool,
1565}
1566
1567#[derive(Debug, PartialEq, Eq, Hash)]
1568pub struct StmtImportFrom<'a> {
1569 module: Option<&'a str>,
1570 names: Vec<ComparableAlias<'a>>,
1571 level: u32,
1572 is_lazy: bool,
1573}
1574
1575#[derive(Debug, PartialEq, Eq, Hash)]
1576pub struct StmtGlobal<'a> {
1577 names: Vec<&'a str>,
1578}
1579
1580#[derive(Debug, PartialEq, Eq, Hash)]
1581pub struct StmtNonlocal<'a> {
1582 names: Vec<&'a str>,
1583}
1584
1585#[derive(Debug, PartialEq, Eq, Hash)]
1586pub struct StmtExpr<'a> {
1587 value: ComparableExpr<'a>,
1588}
1589
1590#[derive(Debug, PartialEq, Eq, Hash)]
1591pub struct StmtIpyEscapeCommand<'a> {
1592 kind: ast::IpyEscapeKind,
1593 value: &'a str,
1594}
1595
1596#[derive(Debug, PartialEq, Eq, Hash)]
1597pub enum ComparableStmt<'a> {
1598 FunctionDef(StmtFunctionDef<'a>),
1599 ClassDef(StmtClassDef<'a>),
1600 Return(StmtReturn<'a>),
1601 Delete(StmtDelete<'a>),
1602 Assign(StmtAssign<'a>),
1603 AugAssign(StmtAugAssign<'a>),
1604 AnnAssign(StmtAnnAssign<'a>),
1605 For(StmtFor<'a>),
1606 While(StmtWhile<'a>),
1607 If(StmtIf<'a>),
1608 With(StmtWith<'a>),
1609 Match(StmtMatch<'a>),
1610 Raise(StmtRaise<'a>),
1611 Try(StmtTry<'a>),
1612 TypeAlias(StmtTypeAlias<'a>),
1613 Assert(StmtAssert<'a>),
1614 Import(StmtImport<'a>),
1615 ImportFrom(StmtImportFrom<'a>),
1616 Global(StmtGlobal<'a>),
1617 Nonlocal(StmtNonlocal<'a>),
1618 IpyEscapeCommand(StmtIpyEscapeCommand<'a>),
1619 Expr(StmtExpr<'a>),
1620 Pass,
1621 Break,
1622 Continue,
1623}
1624
1625impl<'a> From<&'a ast::Stmt> for ComparableStmt<'a> {
1626 fn from(stmt: &'a ast::Stmt) -> Self {
1627 match stmt {
1628 ast::Stmt::FunctionDef(ast::StmtFunctionDef {
1629 is_async,
1630 name,
1631 parameters,
1632 body,
1633 decorator_list,
1634 returns,
1635 type_params,
1636 range: _,
1637 node_index: _,
1638 }) => Self::FunctionDef(StmtFunctionDef {
1639 is_async: *is_async,
1640 name: name.as_str(),
1641 parameters: parameters.into(),
1642 body: body.iter().map(Into::into).collect(),
1643 decorator_list: decorator_list.iter().map(Into::into).collect(),
1644 returns: returns.as_ref().map(Into::into),
1645 type_params: type_params.as_ref().map(Into::into),
1646 }),
1647 ast::Stmt::ClassDef(ast::StmtClassDef {
1648 name,
1649 arguments,
1650 body,
1651 decorator_list,
1652 type_params,
1653 range: _,
1654 node_index: _,
1655 }) => Self::ClassDef(StmtClassDef {
1656 name: name.as_str(),
1657 arguments: arguments.as_ref().map(Into::into).unwrap_or_default(),
1658 body: body.iter().map(Into::into).collect(),
1659 decorator_list: decorator_list.iter().map(Into::into).collect(),
1660 type_params: type_params.as_ref().map(Into::into),
1661 }),
1662 ast::Stmt::Return(ast::StmtReturn {
1663 value,
1664 range: _,
1665 node_index: _,
1666 }) => Self::Return(StmtReturn {
1667 value: value.as_ref().map(Into::into),
1668 }),
1669 ast::Stmt::Delete(ast::StmtDelete {
1670 targets,
1671 range: _,
1672 node_index: _,
1673 }) => Self::Delete(StmtDelete {
1674 targets: targets.iter().map(Into::into).collect(),
1675 }),
1676 ast::Stmt::TypeAlias(ast::StmtTypeAlias {
1677 range: _,
1678 node_index: _,
1679 name,
1680 type_params,
1681 value,
1682 }) => Self::TypeAlias(StmtTypeAlias {
1683 name: name.into(),
1684 type_params: type_params.as_ref().map(Into::into),
1685 value: value.into(),
1686 }),
1687 ast::Stmt::Assign(ast::StmtAssign {
1688 targets,
1689 value,
1690 range: _,
1691 node_index: _,
1692 }) => Self::Assign(StmtAssign {
1693 targets: targets.iter().map(Into::into).collect(),
1694 value: value.into(),
1695 }),
1696 ast::Stmt::AugAssign(ast::StmtAugAssign {
1697 target,
1698 op,
1699 value,
1700 range: _,
1701 node_index: _,
1702 }) => Self::AugAssign(StmtAugAssign {
1703 target: target.into(),
1704 op: (*op).into(),
1705 value: value.into(),
1706 }),
1707 ast::Stmt::AnnAssign(ast::StmtAnnAssign {
1708 target,
1709 annotation,
1710 value,
1711 simple,
1712 range: _,
1713 node_index: _,
1714 }) => Self::AnnAssign(StmtAnnAssign {
1715 target: target.into(),
1716 annotation: annotation.into(),
1717 value: value.as_ref().map(Into::into),
1718 simple: *simple,
1719 }),
1720 ast::Stmt::For(ast::StmtFor {
1721 is_async,
1722 target,
1723 iter,
1724 body,
1725 orelse,
1726 range: _,
1727 node_index: _,
1728 }) => Self::For(StmtFor {
1729 is_async: *is_async,
1730 target: target.into(),
1731 iter: iter.into(),
1732 body: body.iter().map(Into::into).collect(),
1733 orelse: orelse.iter().map(Into::into).collect(),
1734 }),
1735 ast::Stmt::While(ast::StmtWhile {
1736 test,
1737 body,
1738 orelse,
1739 range: _,
1740 node_index: _,
1741 }) => Self::While(StmtWhile {
1742 test: test.into(),
1743 body: body.iter().map(Into::into).collect(),
1744 orelse: orelse.iter().map(Into::into).collect(),
1745 }),
1746 ast::Stmt::If(ast::StmtIf {
1747 test,
1748 body,
1749 elif_else_clauses,
1750 range: _,
1751 node_index: _,
1752 }) => Self::If(StmtIf {
1753 test: test.into(),
1754 body: body.iter().map(Into::into).collect(),
1755 elif_else_clauses: elif_else_clauses.iter().map(Into::into).collect(),
1756 }),
1757 ast::Stmt::With(ast::StmtWith {
1758 is_async,
1759 items,
1760 body,
1761 range: _,
1762 node_index: _,
1763 }) => Self::With(StmtWith {
1764 is_async: *is_async,
1765 items: items.iter().map(Into::into).collect(),
1766 body: body.iter().map(Into::into).collect(),
1767 }),
1768 ast::Stmt::Match(ast::StmtMatch {
1769 subject,
1770 cases,
1771 range: _,
1772 node_index: _,
1773 }) => Self::Match(StmtMatch {
1774 subject: subject.into(),
1775 cases: cases.iter().map(Into::into).collect(),
1776 }),
1777 ast::Stmt::Raise(ast::StmtRaise {
1778 exc,
1779 cause,
1780 range: _,
1781 node_index: _,
1782 }) => Self::Raise(StmtRaise {
1783 exc: exc.as_ref().map(Into::into),
1784 cause: cause.as_ref().map(Into::into),
1785 }),
1786 ast::Stmt::Try(ast::StmtTry {
1787 body,
1788 handlers,
1789 orelse,
1790 finalbody,
1791 is_star,
1792 range: _,
1793 node_index: _,
1794 }) => Self::Try(StmtTry {
1795 body: body.iter().map(Into::into).collect(),
1796 handlers: handlers.iter().map(Into::into).collect(),
1797 orelse: orelse.iter().map(Into::into).collect(),
1798 finalbody: finalbody.iter().map(Into::into).collect(),
1799 is_star: *is_star,
1800 }),
1801 ast::Stmt::Assert(ast::StmtAssert {
1802 test,
1803 msg,
1804 range: _,
1805 node_index: _,
1806 }) => Self::Assert(StmtAssert {
1807 test: test.into(),
1808 msg: msg.as_ref().map(Into::into),
1809 }),
1810 ast::Stmt::Import(ast::StmtImport {
1811 names,
1812 is_lazy,
1813 range: _,
1814 node_index: _,
1815 }) => Self::Import(StmtImport {
1816 names: names.iter().map(Into::into).collect(),
1817 is_lazy: *is_lazy,
1818 }),
1819 ast::Stmt::ImportFrom(ast::StmtImportFrom {
1820 module,
1821 names,
1822 level,
1823 is_lazy,
1824 range: _,
1825 node_index: _,
1826 }) => Self::ImportFrom(StmtImportFrom {
1827 module: module.as_deref(),
1828 names: names.iter().map(Into::into).collect(),
1829 level: *level,
1830 is_lazy: *is_lazy,
1831 }),
1832 ast::Stmt::Global(ast::StmtGlobal {
1833 names,
1834 range: _,
1835 node_index: _,
1836 }) => Self::Global(StmtGlobal {
1837 names: names.iter().map(ast::Identifier::as_str).collect(),
1838 }),
1839 ast::Stmt::Nonlocal(ast::StmtNonlocal {
1840 names,
1841 range: _,
1842 node_index: _,
1843 }) => Self::Nonlocal(StmtNonlocal {
1844 names: names.iter().map(ast::Identifier::as_str).collect(),
1845 }),
1846 ast::Stmt::IpyEscapeCommand(ast::StmtIpyEscapeCommand {
1847 kind,
1848 value,
1849 range: _,
1850 node_index: _,
1851 }) => Self::IpyEscapeCommand(StmtIpyEscapeCommand { kind: *kind, value }),
1852 ast::Stmt::Expr(ast::StmtExpr {
1853 value,
1854 range: _,
1855 node_index: _,
1856 }) => Self::Expr(StmtExpr {
1857 value: value.into(),
1858 }),
1859 ast::Stmt::Pass(_) => Self::Pass,
1860 ast::Stmt::Break(_) => Self::Break,
1861 ast::Stmt::Continue(_) => Self::Continue,
1862 }
1863 }
1864}
1865
1866#[derive(Debug, PartialEq, Eq, Hash)]
1867pub enum ComparableMod<'a> {
1868 Module(ComparableModModule<'a>),
1869 Expression(ComparableModExpression<'a>),
1870}
1871
1872#[derive(Debug, PartialEq, Eq, Hash)]
1873pub struct ComparableModModule<'a> {
1874 body: Vec<ComparableStmt<'a>>,
1875}
1876
1877#[derive(Debug, PartialEq, Eq, Hash)]
1878pub struct ComparableModExpression<'a> {
1879 body: Box<ComparableExpr<'a>>,
1880}
1881
1882impl<'a> From<&'a ast::Mod> for ComparableMod<'a> {
1883 fn from(mod_: &'a ast::Mod) -> Self {
1884 match mod_ {
1885 ast::Mod::Module(module) => Self::Module(module.into()),
1886 ast::Mod::Expression(expr) => Self::Expression(expr.into()),
1887 }
1888 }
1889}
1890
1891impl<'a> From<&'a ast::ModModule> for ComparableModModule<'a> {
1892 fn from(module: &'a ast::ModModule) -> Self {
1893 Self {
1894 body: module.body.iter().map(Into::into).collect(),
1895 }
1896 }
1897}
1898
1899impl<'a> From<&'a ast::ModExpression> for ComparableModExpression<'a> {
1900 fn from(expr: &'a ast::ModExpression) -> Self {
1901 Self {
1902 body: (&expr.body).into(),
1903 }
1904 }
1905}
1906
1907#[derive(Debug, PartialEq, Eq, Hash)]
1918pub struct HashableExpr<'a>(HashableExprKind<'a>);
1919
1920#[derive(Debug, PartialEq, Eq, Hash)]
1921enum HashableExprKind<'a> {
1922 Comparable(ComparableExpr<'a>),
1923 Number(HashableNumber),
1924 NamedExpr {
1925 target: ComparableExpr<'a>,
1926 value: Box<HashableExprKind<'a>>,
1927 },
1928 Tuple(Vec<HashableExprKind<'a>>),
1929}
1930
1931#[derive(Debug, PartialEq, Eq, Hash)]
1932struct HashableNumber {
1933 real: HashableReal,
1934 imag: HashableReal,
1935}
1936
1937impl HashableNumber {
1938 fn real(real: HashableReal) -> Self {
1939 Self {
1940 real,
1941 imag: HashableReal::Integer(0),
1942 }
1943 }
1944
1945 fn complex(real: HashableReal, imag: HashableReal) -> Self {
1946 Self { real, imag }
1947 }
1948
1949 fn negate(mut self) -> Self {
1950 self.real.negate();
1951 self.imag.negate();
1952 self
1953 }
1954
1955 fn into_real(self) -> Option<HashableReal> {
1956 self.imag.is_zero().then_some(self.real)
1957 }
1958}
1959
1960#[derive(Debug, PartialEq, Eq, Hash)]
1961enum HashableReal {
1962 Integer(i128),
1963 Float(u64),
1964}
1965
1966impl HashableReal {
1967 fn from_int(value: &ast::Int) -> Option<Self> {
1968 value.as_u64().map(i128::from).map(Self::Integer)
1969 }
1970
1971 #[expect(
1972 clippy::cast_possible_truncation,
1973 clippy::cast_precision_loss,
1974 clippy::float_cmp,
1975 reason = "the round-trip check guarantees that the float is exactly representable as an integer"
1976 )]
1977 fn from_float(value: f64) -> Self {
1978 if value.is_finite() && value.abs() < U64_EXCLUSIVE_UPPER_BOUND {
1979 let integer = value as i128;
1980 if integer as f64 == value {
1981 return Self::Integer(integer);
1982 }
1983 }
1984 Self::Float(value.to_bits())
1985 }
1986
1987 #[expect(
1988 clippy::cast_precision_loss,
1989 reason = "Python converts real components to floats before complex arithmetic"
1990 )]
1991 fn into_float(self) -> Self {
1992 match self {
1993 Self::Integer(integer) => Self::from_float(integer as f64),
1994 Self::Float(_) => self,
1995 }
1996 }
1997
1998 fn is_zero(&self) -> bool {
1999 matches!(self, Self::Integer(0))
2000 }
2001
2002 fn negate(&mut self) {
2003 match self {
2004 Self::Integer(integer) => *integer = -*integer,
2005 Self::Float(bits) => *bits ^= 1 << 63,
2006 }
2007 }
2008}
2009
2010const U64_EXCLUSIVE_UPPER_BOUND: f64 = 18_446_744_073_709_551_616.0;
2012
2013impl<'a> From<&'a Expr> for HashableExpr<'a> {
2014 fn from(expr: &'a Expr) -> Self {
2015 fn as_hashable(expr: &Expr) -> HashableExprKind<'_> {
2018 if let Some(constant) = as_hashable_constant(expr) {
2019 return constant;
2020 }
2021
2022 match expr {
2023 Expr::Named(named) => HashableExprKind::NamedExpr {
2024 target: ComparableExpr::from(&named.target),
2025 value: Box::new(as_hashable(&named.value)),
2026 },
2027 _ => HashableExprKind::Comparable(ComparableExpr::from(expr)),
2028 }
2029 }
2030
2031 fn as_hashable_constant(expr: &Expr) -> Option<HashableExprKind<'_>> {
2033 if let Some(number) = as_number(expr) {
2034 return Some(HashableExprKind::Number(number));
2035 }
2036
2037 let kind = match expr {
2038 Expr::Tuple(tuple) => HashableExprKind::Tuple(
2039 tuple
2040 .iter()
2041 .map(as_hashable_constant)
2042 .collect::<Option<_>>()?,
2043 ),
2044 _ if expr.is_literal_expr() => {
2045 HashableExprKind::Comparable(ComparableExpr::from(expr))
2046 }
2047 _ => return None,
2048 };
2049
2050 Some(kind)
2051 }
2052
2053 fn as_number(expr: &Expr) -> Option<HashableNumber> {
2054 match expr {
2055 Expr::BooleanLiteral(boolean) => Some(HashableNumber::real(HashableReal::Integer(
2056 i128::from(u8::from(boolean.value)),
2057 ))),
2058 Expr::NumberLiteral(number) => match &number.value {
2059 Number::Int(int) => HashableReal::from_int(int).map(HashableNumber::real),
2060 Number::Float(float) => {
2061 Some(HashableNumber::real(HashableReal::from_float(*float)))
2062 }
2063 Number::Complex { real, imag } => Some(HashableNumber::complex(
2064 HashableReal::from_float(*real),
2065 HashableReal::from_float(*imag),
2066 )),
2067 },
2068 Expr::UnaryOp(ast::ExprUnaryOp { op, operand, .. }) => match op {
2069 ast::UnaryOp::UAdd => as_number(operand),
2070 ast::UnaryOp::USub => as_number(operand).map(HashableNumber::negate),
2071 ast::UnaryOp::Invert | ast::UnaryOp::Not => None,
2072 },
2073 Expr::BinOp(ast::ExprBinOp {
2074 left,
2075 op: op @ (ast::Operator::Add | ast::Operator::Sub),
2076 right,
2077 ..
2078 }) => {
2079 let real = as_number(left)?.into_real()?.into_float();
2080 let Expr::NumberLiteral(ast::ExprNumberLiteral {
2081 value:
2082 Number::Complex {
2083 real: complex_real,
2084 imag,
2085 },
2086 ..
2087 }) = right.as_ref()
2088 else {
2089 return None;
2090 };
2091 let complex_real = HashableReal::from_float(*complex_real);
2092 if !complex_real.is_zero() {
2093 return None;
2094 }
2095 let mut imag = HashableReal::from_float(*imag);
2096 if op.is_sub() {
2097 imag.negate();
2098 }
2099 Some(HashableNumber::complex(real, imag))
2100 }
2101 _ => None,
2102 }
2103 }
2104
2105 Self(as_hashable(expr))
2106 }
2107}