1use crate as ast;
19use crate::{ConstantValue, 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, PartialEq, Eq, Hash)]
369pub enum ComparableConstantValue<'a> {
370 None,
371 Boolean(bool),
372 Str(&'a str),
373 Bytes(Cow<'a, [u8]>),
374 Integer(&'a str),
375 Tuple(Vec<Self>),
376 Frozenset(Vec<Self>),
377 Float(u64),
378 Complex { real: u64, imag: u64 },
379 Ellipsis,
380}
381
382impl<'a> From<&'a ConstantValue> for ComparableConstantValue<'a> {
383 fn from(value: &'a ConstantValue) -> Self {
384 match value {
385 ConstantValue::None => Self::None,
386 ConstantValue::Boolean(value) => Self::Boolean(*value),
387 ConstantValue::Str(value) => Self::Str(value),
388 ConstantValue::Bytes(value) => Self::Bytes(Cow::from(value.as_ref())),
389 ConstantValue::Integer(value) => Self::Integer(value),
390 ConstantValue::Tuple(elements) => {
391 Self::Tuple(elements.iter().map(Into::into).collect())
392 }
393 ConstantValue::Frozenset(elements) => {
394 Self::Frozenset(elements.iter().map(Into::into).collect())
395 }
396 ConstantValue::Float(value) => Self::Float(value.to_bits()),
397 ConstantValue::Complex { real, imag } => Self::Complex {
398 real: real.to_bits(),
399 imag: imag.to_bits(),
400 },
401 ConstantValue::Ellipsis => Self::Ellipsis,
402 }
403 }
404}
405
406#[derive(Debug, Default, PartialEq, Eq, Hash)]
407pub struct ComparableArguments<'a> {
408 args: Vec<ComparableExpr<'a>>,
409 keywords: Vec<ComparableKeyword<'a>>,
410}
411
412impl<'a> From<&'a ast::Arguments> for ComparableArguments<'a> {
413 fn from(arguments: &'a ast::Arguments) -> Self {
414 Self {
415 args: arguments.args.iter().map(Into::into).collect(),
416 keywords: arguments.keywords.iter().map(Into::into).collect(),
417 }
418 }
419}
420
421impl<'a> From<&'a Box<ast::Arguments>> for ComparableArguments<'a> {
422 fn from(arguments: &'a Box<ast::Arguments>) -> Self {
423 (arguments.as_ref()).into()
424 }
425}
426
427#[derive(Debug, PartialEq, Eq, Hash)]
428pub struct ComparableParameters<'a> {
429 posonlyargs: Vec<ComparableParameterWithDefault<'a>>,
430 args: Vec<ComparableParameterWithDefault<'a>>,
431 vararg: Option<ComparableParameter<'a>>,
432 kwonlyargs: Vec<ComparableParameterWithDefault<'a>>,
433 kwarg: Option<ComparableParameter<'a>>,
434}
435
436impl<'a> From<&'a ast::Parameters> for ComparableParameters<'a> {
437 fn from(parameters: &'a ast::Parameters) -> Self {
438 Self {
439 posonlyargs: parameters.posonlyargs.iter().map(Into::into).collect(),
440 args: parameters.args.iter().map(Into::into).collect(),
441 vararg: parameters.vararg.as_ref().map(Into::into),
442 kwonlyargs: parameters.kwonlyargs.iter().map(Into::into).collect(),
443 kwarg: parameters.kwarg.as_ref().map(Into::into),
444 }
445 }
446}
447
448impl<'a> From<&'a Box<ast::Parameters>> for ComparableParameters<'a> {
449 fn from(parameters: &'a Box<ast::Parameters>) -> Self {
450 (parameters.as_ref()).into()
451 }
452}
453
454impl<'a> From<&'a Box<ast::Parameter>> for ComparableParameter<'a> {
455 fn from(arg: &'a Box<ast::Parameter>) -> Self {
456 (arg.as_ref()).into()
457 }
458}
459
460#[derive(Debug, PartialEq, Eq, Hash)]
461pub struct ComparableParameter<'a> {
462 arg: &'a str,
463 annotation: Option<Box<ComparableExpr<'a>>>,
464}
465
466impl<'a> From<&'a ast::Parameter> for ComparableParameter<'a> {
467 fn from(arg: &'a ast::Parameter) -> Self {
468 Self {
469 arg: arg.name.as_str(),
470 annotation: arg.annotation.as_ref().map(Into::into),
471 }
472 }
473}
474
475#[derive(Debug, PartialEq, Eq, Hash)]
476pub struct ComparableParameterWithDefault<'a> {
477 def: ComparableParameter<'a>,
478 default: Option<ComparableExpr<'a>>,
479}
480
481impl<'a> From<&'a ast::ParameterWithDefault> for ComparableParameterWithDefault<'a> {
482 fn from(arg: &'a ast::ParameterWithDefault) -> Self {
483 Self {
484 def: (&arg.parameter).into(),
485 default: arg.default.as_ref().map(Into::into),
486 }
487 }
488}
489
490#[derive(Debug, PartialEq, Eq, Hash)]
491pub struct ComparableKeyword<'a> {
492 arg: Option<&'a str>,
493 value: ComparableExpr<'a>,
494}
495
496impl<'a> From<&'a ast::Keyword> for ComparableKeyword<'a> {
497 fn from(keyword: &'a ast::Keyword) -> Self {
498 Self {
499 arg: keyword.arg.as_ref().map(ast::Identifier::as_str),
500 value: (&keyword.value).into(),
501 }
502 }
503}
504
505#[derive(Debug, PartialEq, Eq, Hash)]
506pub struct ComparableComprehension<'a> {
507 target: ComparableExpr<'a>,
508 iter: ComparableExpr<'a>,
509 ifs: Vec<ComparableExpr<'a>>,
510 is_async: bool,
511}
512
513impl<'a> From<&'a ast::Comprehension> for ComparableComprehension<'a> {
514 fn from(comprehension: &'a ast::Comprehension) -> Self {
515 Self {
516 target: (&comprehension.target).into(),
517 iter: (&comprehension.iter).into(),
518 ifs: comprehension.ifs.iter().map(Into::into).collect(),
519 is_async: comprehension.is_async,
520 }
521 }
522}
523
524#[derive(Debug, PartialEq, Eq, Hash)]
525pub struct ExceptHandlerExceptHandler<'a> {
526 type_: Option<Box<ComparableExpr<'a>>>,
527 name: Option<&'a str>,
528 body: Vec<ComparableStmt<'a>>,
529}
530
531#[derive(Debug, PartialEq, Eq, Hash)]
532pub enum ComparableExceptHandler<'a> {
533 ExceptHandler(ExceptHandlerExceptHandler<'a>),
534}
535
536impl<'a> From<&'a ast::ExceptHandler> for ComparableExceptHandler<'a> {
537 fn from(except_handler: &'a ast::ExceptHandler) -> Self {
538 let ast::ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler {
539 type_,
540 name,
541 body,
542 ..
543 }) = except_handler;
544 Self::ExceptHandler(ExceptHandlerExceptHandler {
545 type_: type_.as_ref().map(Into::into),
546 name: name.as_deref(),
547 body: body.iter().map(Into::into).collect(),
548 })
549 }
550}
551
552#[derive(Debug, PartialEq, Eq, Hash)]
553pub enum ComparableInterpolatedStringElement<'a> {
554 Literal(Cow<'a, str>),
555 InterpolatedElement(InterpolatedElement<'a>),
556}
557
558#[derive(Debug, PartialEq, Eq, Hash)]
565pub struct ComparableDebugText<'a> {
566 text: Cow<'a, str>,
567}
568
569impl<'a> From<&'a ast::DebugText> for ComparableDebugText<'a> {
570 fn from(debug_text: &'a ast::DebugText) -> Self {
571 Self {
574 text: normalize_newlines(debug_text.as_str()),
575 }
576 }
577}
578
579fn normalize_newlines(contents: &str) -> Cow<'_, str> {
580 if contents.contains('\r') {
581 Cow::Owned(contents.replace("\r\n", "\n").replace('\r', "\n"))
582 } else {
583 Cow::Borrowed(contents)
584 }
585}
586
587#[derive(Debug, PartialEq, Eq, Hash)]
588pub struct InterpolatedElement<'a> {
589 expression: ComparableExpr<'a>,
590 debug_text: Option<ComparableDebugText<'a>>,
591 conversion: ast::ConversionFlag,
592 format_spec: Option<Vec<ComparableInterpolatedStringElement<'a>>>,
593}
594
595impl<'a> From<&'a ast::InterpolatedStringElement> for ComparableInterpolatedStringElement<'a> {
596 fn from(interpolated_string_element: &'a ast::InterpolatedStringElement) -> Self {
597 match interpolated_string_element {
598 ast::InterpolatedStringElement::Literal(ast::InterpolatedStringLiteralElement {
599 value,
600 ..
601 }) => Self::Literal(value.as_ref().into()),
602 ast::InterpolatedStringElement::Interpolation(formatted_value) => {
603 formatted_value.into()
604 }
605 }
606 }
607}
608
609impl<'a> From<&'a ast::InterpolatedElement> for InterpolatedElement<'a> {
610 fn from(interpolated_element: &'a ast::InterpolatedElement) -> Self {
611 let ast::InterpolatedElement {
612 expression,
613 debug_text,
614 conversion,
615 format_spec,
616 ..
617 } = interpolated_element;
618
619 Self {
620 expression: (expression).into(),
621 debug_text: debug_text.as_ref().map(Into::into),
622 conversion: *conversion,
623 format_spec: format_spec
624 .as_ref()
625 .map(|spec| spec.elements.iter().map(Into::into).collect()),
626 }
627 }
628}
629
630impl<'a> From<&'a ast::InterpolatedElement> for ComparableInterpolatedStringElement<'a> {
631 fn from(interpolated_element: &'a ast::InterpolatedElement) -> Self {
632 Self::InterpolatedElement(interpolated_element.into())
633 }
634}
635
636#[derive(Debug, PartialEq, Eq, Hash)]
637pub struct ComparableElifElseClause<'a> {
638 test: Option<ComparableExpr<'a>>,
639 body: Vec<ComparableStmt<'a>>,
640}
641
642impl<'a> From<&'a ast::ElifElseClause> for ComparableElifElseClause<'a> {
643 fn from(elif_else_clause: &'a ast::ElifElseClause) -> Self {
644 let ast::ElifElseClause {
645 range: _,
646 node_index: _,
647 test,
648 body,
649 ..
650 } = elif_else_clause;
651 Self {
652 test: test.as_ref().map(Into::into),
653 body: body.iter().map(Into::into).collect(),
654 }
655 }
656}
657
658#[derive(Debug, PartialEq, Eq, Hash)]
659pub enum ComparableLiteral<'a> {
660 None,
661 Ellipsis,
662 Bool(&'a bool),
663 Str(Vec<ComparableStringLiteral<'a>>),
664 Bytes(Vec<ComparableBytesLiteral<'a>>),
665 Number(ComparableNumber<'a>),
666}
667
668impl<'a> From<ast::LiteralExpressionRef<'a>> for ComparableLiteral<'a> {
669 fn from(literal: ast::LiteralExpressionRef<'a>) -> Self {
670 match literal {
671 ast::LiteralExpressionRef::NoneLiteral(_) => Self::None,
672 ast::LiteralExpressionRef::EllipsisLiteral(_) => Self::Ellipsis,
673 ast::LiteralExpressionRef::BooleanLiteral(ast::ExprBooleanLiteral {
674 value, ..
675 }) => Self::Bool(value),
676 ast::LiteralExpressionRef::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
677 Self::Str(value.iter().map(Into::into).collect())
678 }
679 ast::LiteralExpressionRef::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => {
680 Self::Bytes(value.iter().map(Into::into).collect())
681 }
682 ast::LiteralExpressionRef::NumberLiteral(ast::ExprNumberLiteral { value, .. }) => {
683 Self::Number(value.into())
684 }
685 }
686 }
687}
688
689#[derive(Debug, PartialEq, Eq, Hash)]
690pub struct ComparableFString<'a> {
691 elements: Box<[ComparableInterpolatedStringElement<'a>]>,
692}
693
694impl<'a> From<&'a ast::FStringValue> for ComparableFString<'a> {
695 fn from(value: &'a ast::FStringValue) -> Self {
716 #[derive(Default)]
717 struct Collector<'a> {
718 elements: Vec<ComparableInterpolatedStringElement<'a>>,
719 }
720
721 impl<'a> Collector<'a> {
722 fn push_literal(&mut self, literal: &'a str) {
728 if let Some(ComparableInterpolatedStringElement::Literal(existing_literal)) =
729 self.elements.last_mut()
730 {
731 existing_literal.to_mut().push_str(literal);
732 } else {
733 self.elements
734 .push(ComparableInterpolatedStringElement::Literal(literal.into()));
735 }
736 }
737
738 fn push_expression(&mut self, expression: &'a ast::InterpolatedElement) {
739 self.elements.push(expression.into());
740 }
741 }
742
743 let mut collector = Collector::default();
744
745 for part in value {
746 match part {
747 ast::FStringPart::Literal(string_literal) => {
748 collector.push_literal(&string_literal.value);
749 }
750 ast::FStringPart::FString(fstring) => {
751 for element in &fstring.elements {
752 match element {
753 ast::InterpolatedStringElement::Literal(literal) => {
754 collector.push_literal(&literal.value);
755 }
756 ast::InterpolatedStringElement::Interpolation(expression) => {
757 collector.push_expression(expression);
758 }
759 }
760 }
761 }
762 }
763 }
764
765 Self {
766 elements: collector.elements.into_boxed_slice(),
767 }
768 }
769}
770
771#[derive(Debug, PartialEq, Eq, Hash)]
772pub struct ComparableTString<'a> {
773 strings: Box<[ComparableInterpolatedStringElement<'a>]>,
774 interpolations: Box<[InterpolatedElement<'a>]>,
775}
776
777impl<'a> From<&'a ast::TStringValue> for ComparableTString<'a> {
778 fn from(value: &'a ast::TStringValue) -> Self {
789 struct Collector<'a> {
790 strings: Vec<ComparableInterpolatedStringElement<'a>>,
791 interpolations: Vec<InterpolatedElement<'a>>,
792 }
793
794 impl Default for Collector<'_> {
795 fn default() -> Self {
796 Self {
797 strings: vec![ComparableInterpolatedStringElement::Literal("".into())],
798 interpolations: vec![],
799 }
800 }
801 }
802
803 impl<'a> Collector<'a> {
804 fn push_literal(&mut self, literal: &'a str) {
810 if let Some(ComparableInterpolatedStringElement::Literal(existing_literal)) =
811 self.strings.last_mut()
812 {
813 existing_literal.to_mut().push_str(literal);
814 } else {
815 self.strings
816 .push(ComparableInterpolatedStringElement::Literal(literal.into()));
817 }
818 }
819
820 fn start_new_literal(&mut self) {
821 self.strings
822 .push(ComparableInterpolatedStringElement::Literal("".into()));
823 }
824
825 fn push_tstring_interpolation(&mut self, expression: &'a ast::InterpolatedElement) {
826 self.interpolations.push(expression.into());
827 self.start_new_literal();
828 }
829 }
830
831 let mut collector = Collector::default();
832
833 for element in value.elements() {
834 match element {
835 ast::InterpolatedStringElement::Literal(literal) => {
836 collector.push_literal(&literal.value);
837 }
838 ast::InterpolatedStringElement::Interpolation(interpolation) => {
839 collector.push_tstring_interpolation(interpolation);
840 }
841 }
842 }
843
844 Self {
845 strings: collector.strings.into_boxed_slice(),
846 interpolations: collector.interpolations.into_boxed_slice(),
847 }
848 }
849}
850
851#[derive(Debug, PartialEq, Eq, Hash)]
852pub struct ComparableStringLiteral<'a> {
853 value: &'a str,
854}
855
856impl<'a> From<&'a ast::StringLiteral> for ComparableStringLiteral<'a> {
857 fn from(string_literal: &'a ast::StringLiteral) -> Self {
858 Self {
859 value: &string_literal.value,
860 }
861 }
862}
863
864#[derive(Debug, PartialEq, Eq, Hash)]
865pub struct ComparableBytesLiteral<'a> {
866 value: Cow<'a, [u8]>,
867}
868
869impl<'a> From<&'a ast::BytesLiteral> for ComparableBytesLiteral<'a> {
870 fn from(bytes_literal: &'a ast::BytesLiteral) -> Self {
871 Self {
872 value: Cow::Borrowed(&bytes_literal.value),
873 }
874 }
875}
876
877#[derive(Debug, PartialEq, Eq, Hash)]
878pub struct ExprBoolOp<'a> {
879 op: ComparableBoolOp,
880 values: Vec<ComparableExpr<'a>>,
881}
882
883#[derive(Debug, PartialEq, Eq, Hash)]
884pub struct ExprNamed<'a> {
885 target: Box<ComparableExpr<'a>>,
886 value: Box<ComparableExpr<'a>>,
887}
888
889#[derive(Debug, PartialEq, Eq, Hash)]
890pub struct ExprBinOp<'a> {
891 left: Box<ComparableExpr<'a>>,
892 op: ComparableOperator,
893 right: Box<ComparableExpr<'a>>,
894}
895
896#[derive(Debug, PartialEq, Eq, Hash)]
897pub struct ExprUnaryOp<'a> {
898 op: ComparableUnaryOp,
899 operand: Box<ComparableExpr<'a>>,
900}
901
902#[derive(Debug, PartialEq, Eq, Hash)]
903pub struct ExprLambda<'a> {
904 parameters: Option<ComparableParameters<'a>>,
905 body: Box<ComparableExpr<'a>>,
906}
907
908#[derive(Debug, PartialEq, Eq, Hash)]
909pub struct ExprIf<'a> {
910 test: Box<ComparableExpr<'a>>,
911 body: Box<ComparableExpr<'a>>,
912 orelse: Box<ComparableExpr<'a>>,
913}
914
915#[derive(Debug, PartialEq, Eq, Hash)]
916pub struct ComparableDictItem<'a> {
917 key: Option<ComparableExpr<'a>>,
918 value: ComparableExpr<'a>,
919}
920
921impl<'a> From<&'a ast::DictItem> for ComparableDictItem<'a> {
922 fn from(ast::DictItem { key, value }: &'a ast::DictItem) -> Self {
923 Self {
924 key: key.as_ref().map(ComparableExpr::from),
925 value: value.into(),
926 }
927 }
928}
929
930#[derive(Debug, PartialEq, Eq, Hash)]
931pub struct ExprDict<'a> {
932 items: Vec<ComparableDictItem<'a>>,
933}
934
935#[derive(Debug, PartialEq, Eq, Hash)]
936pub struct ExprSet<'a> {
937 elts: Vec<ComparableExpr<'a>>,
938}
939
940#[derive(Debug, PartialEq, Eq, Hash)]
941pub struct ExprListComp<'a> {
942 elt: Box<ComparableExpr<'a>>,
943 generators: Vec<ComparableComprehension<'a>>,
944}
945
946#[derive(Debug, PartialEq, Eq, Hash)]
947pub struct ExprSetComp<'a> {
948 elt: Box<ComparableExpr<'a>>,
949 generators: Vec<ComparableComprehension<'a>>,
950}
951
952#[derive(Debug, PartialEq, Eq, Hash)]
953pub struct ExprDictComp<'a> {
954 key: Option<Box<ComparableExpr<'a>>>,
955 value: Box<ComparableExpr<'a>>,
956 generators: Vec<ComparableComprehension<'a>>,
957}
958
959#[derive(Debug, PartialEq, Eq, Hash)]
960pub struct ExprGenerator<'a> {
961 elt: Box<ComparableExpr<'a>>,
962 generators: Vec<ComparableComprehension<'a>>,
963}
964
965#[derive(Debug, PartialEq, Eq, Hash)]
966pub struct ExprAwait<'a> {
967 value: Box<ComparableExpr<'a>>,
968}
969
970#[derive(Debug, PartialEq, Eq, Hash)]
971pub struct ExprYield<'a> {
972 value: Option<Box<ComparableExpr<'a>>>,
973}
974
975#[derive(Debug, PartialEq, Eq, Hash)]
976pub struct ExprYieldFrom<'a> {
977 value: Box<ComparableExpr<'a>>,
978}
979
980#[derive(Debug, PartialEq, Eq, Hash)]
981pub struct ExprCompare<'a> {
982 left: Box<ComparableExpr<'a>>,
983 ops: Vec<ComparableCmpOp>,
984 comparators: Vec<ComparableExpr<'a>>,
985}
986
987#[derive(Debug, PartialEq, Eq, Hash)]
988pub struct ExprCall<'a> {
989 func: Box<ComparableExpr<'a>>,
990 arguments: ComparableArguments<'a>,
991}
992
993#[derive(Debug, PartialEq, Eq, Hash)]
994pub struct ExprInterpolatedElement<'a> {
995 value: Box<ComparableExpr<'a>>,
996 debug_text: Option<ComparableDebugText<'a>>,
997 conversion: ast::ConversionFlag,
998 format_spec: Vec<ComparableInterpolatedStringElement<'a>>,
999}
1000
1001#[derive(Debug, PartialEq, Eq, Hash)]
1002pub struct ExprFString<'a> {
1003 value: ComparableFString<'a>,
1004}
1005
1006#[derive(Debug, PartialEq, Eq, Hash)]
1007pub struct ExprTString<'a> {
1008 value: ComparableTString<'a>,
1009}
1010
1011#[derive(Debug, PartialEq, Eq, Hash)]
1012pub struct ExprStringLiteral<'a> {
1013 value: ComparableStringLiteral<'a>,
1014}
1015
1016#[derive(Debug, PartialEq, Eq, Hash)]
1017pub struct ExprBytesLiteral<'a> {
1018 value: ComparableBytesLiteral<'a>,
1019}
1020
1021#[derive(Debug, PartialEq, Eq, Hash)]
1022pub struct ExprNumberLiteral<'a> {
1023 value: ComparableNumber<'a>,
1024}
1025
1026#[derive(Debug, PartialEq, Eq, Hash)]
1027pub struct ExprConstant<'a> {
1028 value: ComparableConstantValue<'a>,
1029}
1030
1031#[derive(Debug, PartialEq, Eq, Hash)]
1032pub struct ExprBoolLiteral {
1033 value: bool,
1034}
1035
1036#[derive(Debug, PartialEq, Eq, Hash)]
1037pub struct ExprAttribute<'a> {
1038 value: Box<ComparableExpr<'a>>,
1039 attr: &'a str,
1040}
1041
1042#[derive(Debug, PartialEq, Eq, Hash)]
1043pub struct ExprSubscript<'a> {
1044 value: Box<ComparableExpr<'a>>,
1045 slice: Box<ComparableExpr<'a>>,
1046}
1047
1048#[derive(Debug, PartialEq, Eq, Hash)]
1049pub struct ExprStarred<'a> {
1050 value: Box<ComparableExpr<'a>>,
1051}
1052
1053#[derive(Debug, PartialEq, Eq, Hash)]
1054pub struct ExprName<'a> {
1055 id: &'a str,
1056}
1057
1058#[derive(Debug, PartialEq, Eq, Hash)]
1059pub struct ExprList<'a> {
1060 elts: Vec<ComparableExpr<'a>>,
1061}
1062
1063#[derive(Debug, PartialEq, Eq, Hash)]
1064pub struct ExprTuple<'a> {
1065 elts: Vec<ComparableExpr<'a>>,
1066}
1067
1068#[derive(Debug, PartialEq, Eq, Hash)]
1069pub struct ExprSlice<'a> {
1070 lower: Option<Box<ComparableExpr<'a>>>,
1071 upper: Option<Box<ComparableExpr<'a>>>,
1072 step: Option<Box<ComparableExpr<'a>>>,
1073}
1074
1075#[derive(Debug, PartialEq, Eq, Hash)]
1076pub struct ExprIpyEscapeCommand<'a> {
1077 kind: ast::IpyEscapeKind,
1078 value: &'a str,
1079}
1080
1081#[derive(Debug, PartialEq, Eq, Hash)]
1082pub enum ComparableExpr<'a> {
1083 BoolOp(ExprBoolOp<'a>),
1084 NamedExpr(ExprNamed<'a>),
1085 BinOp(ExprBinOp<'a>),
1086 UnaryOp(ExprUnaryOp<'a>),
1087 Lambda(ExprLambda<'a>),
1088 IfExp(ExprIf<'a>),
1089 Dict(ExprDict<'a>),
1090 Set(ExprSet<'a>),
1091 ListComp(ExprListComp<'a>),
1092 SetComp(ExprSetComp<'a>),
1093 DictComp(ExprDictComp<'a>),
1094 GeneratorExp(ExprGenerator<'a>),
1095 Await(ExprAwait<'a>),
1096 Yield(ExprYield<'a>),
1097 YieldFrom(ExprYieldFrom<'a>),
1098 Compare(ExprCompare<'a>),
1099 Call(ExprCall<'a>),
1100 FStringExpressionElement(ExprInterpolatedElement<'a>),
1101 FString(ExprFString<'a>),
1102 TStringInterpolationElement(ExprInterpolatedElement<'a>),
1103 TString(ExprTString<'a>),
1104 StringLiteral(ExprStringLiteral<'a>),
1105 BytesLiteral(ExprBytesLiteral<'a>),
1106 NumberLiteral(ExprNumberLiteral<'a>),
1107 Constant(ExprConstant<'a>),
1108 BoolLiteral(ExprBoolLiteral),
1109 NoneLiteral,
1110 EllipsisLiteral,
1111 Attribute(ExprAttribute<'a>),
1112 Subscript(ExprSubscript<'a>),
1113 Starred(ExprStarred<'a>),
1114 Name(ExprName<'a>),
1115 List(ExprList<'a>),
1116 Tuple(ExprTuple<'a>),
1117 Slice(ExprSlice<'a>),
1118 IpyEscapeCommand(ExprIpyEscapeCommand<'a>),
1119}
1120
1121impl<'a> From<&'a Box<ast::Expr>> for Box<ComparableExpr<'a>> {
1122 fn from(expr: &'a Box<ast::Expr>) -> Self {
1123 Box::new((expr.as_ref()).into())
1124 }
1125}
1126
1127impl<'a> From<&'a Box<ast::Expr>> for ComparableExpr<'a> {
1128 fn from(expr: &'a Box<ast::Expr>) -> Self {
1129 (expr.as_ref()).into()
1130 }
1131}
1132
1133impl<'a> From<&'a ast::Expr> for ComparableExpr<'a> {
1134 fn from(expr: &'a ast::Expr) -> Self {
1135 match expr {
1136 ast::Expr::BoolOp(ast::ExprBoolOp { op, values, .. }) => Self::BoolOp(ExprBoolOp {
1137 op: (*op).into(),
1138 values: values.iter().map(Into::into).collect(),
1139 }),
1140 ast::Expr::Named(ast::ExprNamed { target, value, .. }) => Self::NamedExpr(ExprNamed {
1141 target: target.into(),
1142 value: value.into(),
1143 }),
1144 ast::Expr::BinOp(ast::ExprBinOp {
1145 left, op, right, ..
1146 }) => Self::BinOp(ExprBinOp {
1147 left: left.into(),
1148 op: (*op).into(),
1149 right: right.into(),
1150 }),
1151 ast::Expr::UnaryOp(ast::ExprUnaryOp { op, operand, .. }) => {
1152 Self::UnaryOp(ExprUnaryOp {
1153 op: (*op).into(),
1154 operand: operand.into(),
1155 })
1156 }
1157 ast::Expr::Lambda(ast::ExprLambda {
1158 parameters, body, ..
1159 }) => Self::Lambda(ExprLambda {
1160 parameters: parameters.as_ref().map(Into::into),
1161 body: body.into(),
1162 }),
1163 ast::Expr::If(ast::ExprIf {
1164 test, body, orelse, ..
1165 }) => Self::IfExp(ExprIf {
1166 test: test.into(),
1167 body: body.into(),
1168 orelse: orelse.into(),
1169 }),
1170 ast::Expr::Dict(ast::ExprDict { items, .. }) => Self::Dict(ExprDict {
1171 items: items.iter().map(ComparableDictItem::from).collect(),
1172 }),
1173 ast::Expr::Set(ast::ExprSet { elts, .. }) => Self::Set(ExprSet {
1174 elts: elts.iter().map(Into::into).collect(),
1175 }),
1176 ast::Expr::ListComp(ast::ExprListComp {
1177 elt, generators, ..
1178 }) => Self::ListComp(ExprListComp {
1179 elt: elt.into(),
1180 generators: generators.iter().map(Into::into).collect(),
1181 }),
1182 ast::Expr::SetComp(ast::ExprSetComp {
1183 elt, generators, ..
1184 }) => Self::SetComp(ExprSetComp {
1185 elt: elt.into(),
1186 generators: generators.iter().map(Into::into).collect(),
1187 }),
1188 ast::Expr::DictComp(ast::ExprDictComp {
1189 key,
1190 value,
1191 generators,
1192 ..
1193 }) => Self::DictComp(ExprDictComp {
1194 key: key.as_ref().map(Into::into),
1195 value: value.into(),
1196 generators: generators.iter().map(Into::into).collect(),
1197 }),
1198 ast::Expr::Generator(ast::ExprGenerator {
1199 elt, generators, ..
1200 }) => Self::GeneratorExp(ExprGenerator {
1201 elt: elt.into(),
1202 generators: generators.iter().map(Into::into).collect(),
1203 }),
1204 ast::Expr::Await(ast::ExprAwait { value, .. }) => Self::Await(ExprAwait {
1205 value: value.into(),
1206 }),
1207 ast::Expr::Yield(ast::ExprYield { value, .. }) => Self::Yield(ExprYield {
1208 value: value.as_ref().map(Into::into),
1209 }),
1210 ast::Expr::YieldFrom(ast::ExprYieldFrom { value, .. }) => {
1211 Self::YieldFrom(ExprYieldFrom {
1212 value: value.into(),
1213 })
1214 }
1215 ast::Expr::Compare(ast::ExprCompare {
1216 left,
1217 ops,
1218 comparators,
1219 ..
1220 }) => Self::Compare(ExprCompare {
1221 left: left.into(),
1222 ops: ops.iter().copied().map(Into::into).collect(),
1223 comparators: comparators.iter().map(Into::into).collect(),
1224 }),
1225 ast::Expr::Call(ast::ExprCall {
1226 func, arguments, ..
1227 }) => Self::Call(ExprCall {
1228 func: func.into(),
1229 arguments: arguments.into(),
1230 }),
1231 ast::Expr::FString(ast::ExprFString { value, .. }) => Self::FString(ExprFString {
1232 value: value.into(),
1233 }),
1234 ast::Expr::TString(ast::ExprTString { value, .. }) => Self::TString(ExprTString {
1235 value: value.into(),
1236 }),
1237 ast::Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
1238 Self::StringLiteral(ExprStringLiteral {
1239 value: ComparableStringLiteral {
1240 value: value.to_str(),
1241 },
1242 })
1243 }
1244 ast::Expr::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => {
1245 Self::BytesLiteral(ExprBytesLiteral {
1246 value: ComparableBytesLiteral {
1247 value: Cow::from(value),
1248 },
1249 })
1250 }
1251 ast::Expr::NumberLiteral(ast::ExprNumberLiteral { value, .. }) => {
1252 Self::NumberLiteral(ExprNumberLiteral {
1253 value: value.into(),
1254 })
1255 }
1256 ast::Expr::Constant(ast::ExprConstant { value, .. }) => Self::Constant(ExprConstant {
1257 value: value.into(),
1258 }),
1259 ast::Expr::BooleanLiteral(ast::ExprBooleanLiteral { value, .. }) => {
1260 Self::BoolLiteral(ExprBoolLiteral { value: *value })
1261 }
1262 ast::Expr::NoneLiteral(_) => Self::NoneLiteral,
1263 ast::Expr::EllipsisLiteral(_) => Self::EllipsisLiteral,
1264 ast::Expr::Attribute(ast::ExprAttribute { value, attr, .. }) => {
1265 Self::Attribute(ExprAttribute {
1266 value: value.into(),
1267 attr: attr.as_str(),
1268 })
1269 }
1270 ast::Expr::Subscript(ast::ExprSubscript { value, slice, .. }) => {
1271 Self::Subscript(ExprSubscript {
1272 value: value.into(),
1273 slice: slice.into(),
1274 })
1275 }
1276 ast::Expr::Starred(ast::ExprStarred { value, .. }) => Self::Starred(ExprStarred {
1277 value: value.into(),
1278 }),
1279 ast::Expr::Name(name) => name.into(),
1280 ast::Expr::List(ast::ExprList { elts, .. }) => Self::List(ExprList {
1281 elts: elts.iter().map(Into::into).collect(),
1282 }),
1283 ast::Expr::Tuple(ast::ExprTuple { elts, .. }) => Self::Tuple(ExprTuple {
1284 elts: elts.iter().map(Into::into).collect(),
1285 }),
1286 ast::Expr::Slice(ast::ExprSlice {
1287 lower, upper, step, ..
1288 }) => Self::Slice(ExprSlice {
1289 lower: lower.as_ref().map(Into::into),
1290 upper: upper.as_ref().map(Into::into),
1291 step: step.as_ref().map(Into::into),
1292 }),
1293 ast::Expr::IpyEscapeCommand(ast::ExprIpyEscapeCommand { kind, value, .. }) => {
1294 Self::IpyEscapeCommand(ExprIpyEscapeCommand { kind: *kind, value })
1295 }
1296 }
1297 }
1298}
1299
1300impl<'a> From<&'a ast::ExprName> for ComparableExpr<'a> {
1301 fn from(expr: &'a ast::ExprName) -> Self {
1302 Self::Name(ExprName {
1303 id: expr.id.as_str(),
1304 })
1305 }
1306}
1307
1308#[derive(Debug, PartialEq, Eq, Hash)]
1309pub struct StmtFunctionDef<'a> {
1310 is_async: bool,
1311 decorator_list: Vec<ComparableDecorator<'a>>,
1312 name: &'a str,
1313 type_params: Option<ComparableTypeParams<'a>>,
1314 parameters: ComparableParameters<'a>,
1315 returns: Option<ComparableExpr<'a>>,
1316 body: Vec<ComparableStmt<'a>>,
1317}
1318
1319#[derive(Debug, PartialEq, Eq, Hash)]
1320pub struct StmtClassDef<'a> {
1321 decorator_list: Vec<ComparableDecorator<'a>>,
1322 name: &'a str,
1323 type_params: Option<ComparableTypeParams<'a>>,
1324 arguments: ComparableArguments<'a>,
1325 body: Vec<ComparableStmt<'a>>,
1326}
1327
1328#[derive(Debug, PartialEq, Eq, Hash)]
1329pub struct StmtReturn<'a> {
1330 value: Option<ComparableExpr<'a>>,
1331}
1332
1333#[derive(Debug, PartialEq, Eq, Hash)]
1334pub struct StmtDelete<'a> {
1335 targets: Vec<ComparableExpr<'a>>,
1336}
1337
1338#[derive(Debug, PartialEq, Eq, Hash)]
1339pub struct StmtTypeAlias<'a> {
1340 pub name: Box<ComparableExpr<'a>>,
1341 pub type_params: Option<ComparableTypeParams<'a>>,
1342 pub value: Box<ComparableExpr<'a>>,
1343}
1344
1345#[derive(Debug, PartialEq, Eq, Hash)]
1346pub struct ComparableTypeParams<'a> {
1347 pub type_params: Vec<ComparableTypeParam<'a>>,
1348}
1349
1350impl<'a> From<&'a ast::TypeParams> for ComparableTypeParams<'a> {
1351 fn from(type_params: &'a ast::TypeParams) -> Self {
1352 Self {
1353 type_params: type_params.iter().map(Into::into).collect(),
1354 }
1355 }
1356}
1357
1358impl<'a> From<&'a Box<ast::TypeParams>> for ComparableTypeParams<'a> {
1359 fn from(type_params: &'a Box<ast::TypeParams>) -> Self {
1360 type_params.as_ref().into()
1361 }
1362}
1363
1364#[derive(Debug, PartialEq, Eq, Hash)]
1365pub enum ComparableTypeParam<'a> {
1366 TypeVar(TypeParamTypeVar<'a>),
1367 ParamSpec(TypeParamParamSpec<'a>),
1368 TypeVarTuple(TypeParamTypeVarTuple<'a>),
1369}
1370
1371impl<'a> From<&'a ast::TypeParam> for ComparableTypeParam<'a> {
1372 fn from(type_param: &'a ast::TypeParam) -> Self {
1373 match type_param {
1374 ast::TypeParam::TypeVar(ast::TypeParamTypeVar {
1375 name,
1376 bound,
1377 default,
1378 ..
1379 }) => Self::TypeVar(TypeParamTypeVar {
1380 name: name.as_str(),
1381 bound: bound.as_ref().map(Into::into),
1382 default: default.as_ref().map(Into::into),
1383 }),
1384 ast::TypeParam::TypeVarTuple(ast::TypeParamTypeVarTuple { name, default, .. }) => {
1385 Self::TypeVarTuple(TypeParamTypeVarTuple {
1386 name: name.as_str(),
1387 default: default.as_ref().map(Into::into),
1388 })
1389 }
1390 ast::TypeParam::ParamSpec(ast::TypeParamParamSpec { name, default, .. }) => {
1391 Self::ParamSpec(TypeParamParamSpec {
1392 name: name.as_str(),
1393 default: default.as_ref().map(Into::into),
1394 })
1395 }
1396 }
1397 }
1398}
1399
1400#[derive(Debug, PartialEq, Eq, Hash)]
1401pub struct TypeParamTypeVar<'a> {
1402 pub name: &'a str,
1403 pub bound: Option<Box<ComparableExpr<'a>>>,
1404 pub default: Option<Box<ComparableExpr<'a>>>,
1405}
1406
1407#[derive(Debug, PartialEq, Eq, Hash)]
1408pub struct TypeParamParamSpec<'a> {
1409 pub name: &'a str,
1410 pub default: Option<Box<ComparableExpr<'a>>>,
1411}
1412
1413#[derive(Debug, PartialEq, Eq, Hash)]
1414pub struct TypeParamTypeVarTuple<'a> {
1415 pub name: &'a str,
1416 pub default: Option<Box<ComparableExpr<'a>>>,
1417}
1418
1419#[derive(Debug, PartialEq, Eq, Hash)]
1420pub struct StmtAssign<'a> {
1421 targets: Vec<ComparableExpr<'a>>,
1422 value: ComparableExpr<'a>,
1423}
1424
1425#[derive(Debug, PartialEq, Eq, Hash)]
1426pub struct StmtAugAssign<'a> {
1427 target: ComparableExpr<'a>,
1428 op: ComparableOperator,
1429 value: ComparableExpr<'a>,
1430}
1431
1432#[derive(Debug, PartialEq, Eq, Hash)]
1433pub struct StmtAnnAssign<'a> {
1434 target: ComparableExpr<'a>,
1435 annotation: ComparableExpr<'a>,
1436 value: Option<ComparableExpr<'a>>,
1437 simple: bool,
1438}
1439
1440#[derive(Debug, PartialEq, Eq, Hash)]
1441pub struct StmtFor<'a> {
1442 is_async: bool,
1443 target: ComparableExpr<'a>,
1444 iter: ComparableExpr<'a>,
1445 body: Vec<ComparableStmt<'a>>,
1446 orelse: Vec<ComparableStmt<'a>>,
1447}
1448
1449#[derive(Debug, PartialEq, Eq, Hash)]
1450pub struct StmtWhile<'a> {
1451 test: ComparableExpr<'a>,
1452 body: Vec<ComparableStmt<'a>>,
1453 orelse: Vec<ComparableStmt<'a>>,
1454}
1455
1456#[derive(Debug, PartialEq, Eq, Hash)]
1457pub struct StmtIf<'a> {
1458 test: ComparableExpr<'a>,
1459 body: Vec<ComparableStmt<'a>>,
1460 elif_else_clauses: Vec<ComparableElifElseClause<'a>>,
1461}
1462
1463#[derive(Debug, PartialEq, Eq, Hash)]
1464pub struct StmtWith<'a> {
1465 is_async: bool,
1466 items: Vec<ComparableWithItem<'a>>,
1467 body: Vec<ComparableStmt<'a>>,
1468}
1469
1470#[derive(Debug, PartialEq, Eq, Hash)]
1471pub struct StmtMatch<'a> {
1472 subject: ComparableExpr<'a>,
1473 cases: Vec<ComparableMatchCase<'a>>,
1474}
1475
1476#[derive(Debug, PartialEq, Eq, Hash)]
1477pub struct StmtRaise<'a> {
1478 exc: Option<ComparableExpr<'a>>,
1479 cause: Option<ComparableExpr<'a>>,
1480}
1481
1482#[derive(Debug, PartialEq, Eq, Hash)]
1483pub struct StmtTry<'a> {
1484 body: Vec<ComparableStmt<'a>>,
1485 handlers: Vec<ComparableExceptHandler<'a>>,
1486 orelse: Vec<ComparableStmt<'a>>,
1487 finalbody: Vec<ComparableStmt<'a>>,
1488 is_star: bool,
1489}
1490
1491#[derive(Debug, PartialEq, Eq, Hash)]
1492pub struct StmtAssert<'a> {
1493 test: ComparableExpr<'a>,
1494 msg: Option<ComparableExpr<'a>>,
1495}
1496
1497#[derive(Debug, PartialEq, Eq, Hash)]
1498pub struct StmtImport<'a> {
1499 names: Vec<ComparableAlias<'a>>,
1500 is_lazy: bool,
1501}
1502
1503#[derive(Debug, PartialEq, Eq, Hash)]
1504pub struct StmtImportFrom<'a> {
1505 module: Option<&'a str>,
1506 names: Vec<ComparableAlias<'a>>,
1507 level: u32,
1508 is_lazy: bool,
1509}
1510
1511#[derive(Debug, PartialEq, Eq, Hash)]
1512pub struct StmtGlobal<'a> {
1513 names: Vec<&'a str>,
1514}
1515
1516#[derive(Debug, PartialEq, Eq, Hash)]
1517pub struct StmtNonlocal<'a> {
1518 names: Vec<&'a str>,
1519}
1520
1521#[derive(Debug, PartialEq, Eq, Hash)]
1522pub struct StmtExpr<'a> {
1523 value: ComparableExpr<'a>,
1524}
1525
1526#[derive(Debug, PartialEq, Eq, Hash)]
1527pub struct StmtIpyEscapeCommand<'a> {
1528 kind: ast::IpyEscapeKind,
1529 value: &'a str,
1530}
1531
1532#[derive(Debug, PartialEq, Eq, Hash)]
1533pub enum ComparableStmt<'a> {
1534 FunctionDef(StmtFunctionDef<'a>),
1535 ClassDef(StmtClassDef<'a>),
1536 Return(StmtReturn<'a>),
1537 Delete(StmtDelete<'a>),
1538 Assign(StmtAssign<'a>),
1539 AugAssign(StmtAugAssign<'a>),
1540 AnnAssign(StmtAnnAssign<'a>),
1541 For(StmtFor<'a>),
1542 While(StmtWhile<'a>),
1543 If(StmtIf<'a>),
1544 With(StmtWith<'a>),
1545 Match(StmtMatch<'a>),
1546 Raise(StmtRaise<'a>),
1547 Try(StmtTry<'a>),
1548 TypeAlias(StmtTypeAlias<'a>),
1549 Assert(StmtAssert<'a>),
1550 Import(StmtImport<'a>),
1551 ImportFrom(StmtImportFrom<'a>),
1552 Global(StmtGlobal<'a>),
1553 Nonlocal(StmtNonlocal<'a>),
1554 IpyEscapeCommand(StmtIpyEscapeCommand<'a>),
1555 Expr(StmtExpr<'a>),
1556 Pass,
1557 Break,
1558 Continue,
1559}
1560
1561impl<'a> From<&'a ast::Stmt> for ComparableStmt<'a> {
1562 fn from(stmt: &'a ast::Stmt) -> Self {
1563 match stmt {
1564 ast::Stmt::FunctionDef(ast::StmtFunctionDef {
1565 is_async,
1566 name,
1567 parameters,
1568 body,
1569 decorator_list,
1570 returns,
1571 type_params,
1572 ..
1573 }) => Self::FunctionDef(StmtFunctionDef {
1574 is_async: *is_async,
1575 name: name.as_str(),
1576 parameters: parameters.into(),
1577 body: body.iter().map(Into::into).collect(),
1578 decorator_list: decorator_list.iter().map(Into::into).collect(),
1579 returns: returns.as_ref().map(Into::into),
1580 type_params: type_params.as_ref().map(Into::into),
1581 }),
1582 ast::Stmt::ClassDef(ast::StmtClassDef {
1583 name,
1584 arguments,
1585 body,
1586 decorator_list,
1587 type_params,
1588 ..
1589 }) => Self::ClassDef(StmtClassDef {
1590 name: name.as_str(),
1591 arguments: arguments.as_ref().map(Into::into).unwrap_or_default(),
1592 body: body.iter().map(Into::into).collect(),
1593 decorator_list: decorator_list.iter().map(Into::into).collect(),
1594 type_params: type_params.as_ref().map(Into::into),
1595 }),
1596 ast::Stmt::Return(ast::StmtReturn { value, .. }) => Self::Return(StmtReturn {
1597 value: value.as_ref().map(Into::into),
1598 }),
1599 ast::Stmt::Delete(ast::StmtDelete { targets, .. }) => Self::Delete(StmtDelete {
1600 targets: targets.iter().map(Into::into).collect(),
1601 }),
1602 ast::Stmt::TypeAlias(ast::StmtTypeAlias {
1603 range: _,
1604 node_index: _,
1605 name,
1606 type_params,
1607 value,
1608 }) => Self::TypeAlias(StmtTypeAlias {
1609 name: name.into(),
1610 type_params: type_params.as_ref().map(Into::into),
1611 value: value.into(),
1612 }),
1613 ast::Stmt::Assign(ast::StmtAssign { targets, value, .. }) => Self::Assign(StmtAssign {
1614 targets: targets.iter().map(Into::into).collect(),
1615 value: value.into(),
1616 }),
1617 ast::Stmt::AugAssign(ast::StmtAugAssign {
1618 target, op, value, ..
1619 }) => Self::AugAssign(StmtAugAssign {
1620 target: target.into(),
1621 op: (*op).into(),
1622 value: value.into(),
1623 }),
1624 ast::Stmt::AnnAssign(ast::StmtAnnAssign {
1625 target,
1626 annotation,
1627 value,
1628 simple,
1629 ..
1630 }) => Self::AnnAssign(StmtAnnAssign {
1631 target: target.into(),
1632 annotation: annotation.into(),
1633 value: value.as_ref().map(Into::into),
1634 simple: *simple,
1635 }),
1636 ast::Stmt::For(ast::StmtFor {
1637 is_async,
1638 target,
1639 iter,
1640 body,
1641 orelse,
1642 ..
1643 }) => Self::For(StmtFor {
1644 is_async: *is_async,
1645 target: target.into(),
1646 iter: iter.into(),
1647 body: body.iter().map(Into::into).collect(),
1648 orelse: orelse.iter().map(Into::into).collect(),
1649 }),
1650 ast::Stmt::While(ast::StmtWhile {
1651 test, body, orelse, ..
1652 }) => Self::While(StmtWhile {
1653 test: test.into(),
1654 body: body.iter().map(Into::into).collect(),
1655 orelse: orelse.iter().map(Into::into).collect(),
1656 }),
1657 ast::Stmt::If(ast::StmtIf {
1658 test,
1659 body,
1660 elif_else_clauses,
1661 ..
1662 }) => Self::If(StmtIf {
1663 test: test.into(),
1664 body: body.iter().map(Into::into).collect(),
1665 elif_else_clauses: elif_else_clauses.iter().map(Into::into).collect(),
1666 }),
1667 ast::Stmt::With(ast::StmtWith {
1668 is_async,
1669 items,
1670 body,
1671 ..
1672 }) => Self::With(StmtWith {
1673 is_async: *is_async,
1674 items: items.iter().map(Into::into).collect(),
1675 body: body.iter().map(Into::into).collect(),
1676 }),
1677 ast::Stmt::Match(ast::StmtMatch { subject, cases, .. }) => Self::Match(StmtMatch {
1678 subject: subject.into(),
1679 cases: cases.iter().map(Into::into).collect(),
1680 }),
1681 ast::Stmt::Raise(ast::StmtRaise { exc, cause, .. }) => Self::Raise(StmtRaise {
1682 exc: exc.as_ref().map(Into::into),
1683 cause: cause.as_ref().map(Into::into),
1684 }),
1685 ast::Stmt::Try(ast::StmtTry {
1686 body,
1687 handlers,
1688 orelse,
1689 finalbody,
1690 is_star,
1691 ..
1692 }) => Self::Try(StmtTry {
1693 body: body.iter().map(Into::into).collect(),
1694 handlers: handlers.iter().map(Into::into).collect(),
1695 orelse: orelse.iter().map(Into::into).collect(),
1696 finalbody: finalbody.iter().map(Into::into).collect(),
1697 is_star: *is_star,
1698 }),
1699 ast::Stmt::Assert(ast::StmtAssert { test, msg, .. }) => Self::Assert(StmtAssert {
1700 test: test.into(),
1701 msg: msg.as_ref().map(Into::into),
1702 }),
1703 ast::Stmt::Import(ast::StmtImport { names, is_lazy, .. }) => Self::Import(StmtImport {
1704 names: names.iter().map(Into::into).collect(),
1705 is_lazy: *is_lazy,
1706 }),
1707 ast::Stmt::ImportFrom(ast::StmtImportFrom {
1708 module,
1709 names,
1710 level,
1711 is_lazy,
1712 ..
1713 }) => Self::ImportFrom(StmtImportFrom {
1714 module: module.as_deref(),
1715 names: names.iter().map(Into::into).collect(),
1716 level: *level,
1717 is_lazy: *is_lazy,
1718 }),
1719 ast::Stmt::Global(ast::StmtGlobal { names, .. }) => Self::Global(StmtGlobal {
1720 names: names.iter().map(ast::Identifier::as_str).collect(),
1721 }),
1722 ast::Stmt::Nonlocal(ast::StmtNonlocal { names, .. }) => Self::Nonlocal(StmtNonlocal {
1723 names: names.iter().map(ast::Identifier::as_str).collect(),
1724 }),
1725 ast::Stmt::IpyEscapeCommand(ast::StmtIpyEscapeCommand { kind, value, .. }) => {
1726 Self::IpyEscapeCommand(StmtIpyEscapeCommand { kind: *kind, value })
1727 }
1728 ast::Stmt::Expr(ast::StmtExpr { value, .. }) => Self::Expr(StmtExpr {
1729 value: value.into(),
1730 }),
1731 ast::Stmt::Pass(_) => Self::Pass,
1732 ast::Stmt::Break(_) => Self::Break,
1733 ast::Stmt::Continue(_) => Self::Continue,
1734 }
1735 }
1736}
1737
1738#[derive(Debug, PartialEq, Eq, Hash)]
1739pub enum ComparableMod<'a> {
1740 Module(ComparableModModule<'a>),
1741 Expression(ComparableModExpression<'a>),
1742}
1743
1744#[derive(Debug, PartialEq, Eq, Hash)]
1745pub struct ComparableModModule<'a> {
1746 body: Vec<ComparableStmt<'a>>,
1747}
1748
1749#[derive(Debug, PartialEq, Eq, Hash)]
1750pub struct ComparableModExpression<'a> {
1751 body: Box<ComparableExpr<'a>>,
1752}
1753
1754impl<'a> From<&'a ast::Mod> for ComparableMod<'a> {
1755 fn from(mod_: &'a ast::Mod) -> Self {
1756 match mod_ {
1757 ast::Mod::Module(module) => Self::Module(module.into()),
1758 ast::Mod::Expression(expr) => Self::Expression(expr.into()),
1759 }
1760 }
1761}
1762
1763impl<'a> From<&'a ast::ModModule> for ComparableModModule<'a> {
1764 fn from(module: &'a ast::ModModule) -> Self {
1765 Self {
1766 body: module.body.iter().map(Into::into).collect(),
1767 }
1768 }
1769}
1770
1771impl<'a> From<&'a ast::ModExpression> for ComparableModExpression<'a> {
1772 fn from(expr: &'a ast::ModExpression) -> Self {
1773 Self {
1774 body: (&expr.body).into(),
1775 }
1776 }
1777}
1778
1779#[derive(Debug, PartialEq, Eq, Hash)]
1790pub struct HashableExpr<'a>(HashableExprKind<'a>);
1791
1792#[derive(Debug, PartialEq, Eq, Hash)]
1793enum HashableExprKind<'a> {
1794 Comparable(ComparableExpr<'a>),
1795 Number(HashableNumber),
1796 NamedExpr {
1797 target: ComparableExpr<'a>,
1798 value: Box<HashableExprKind<'a>>,
1799 },
1800 Tuple(Vec<HashableExprKind<'a>>),
1801}
1802
1803#[derive(Debug, PartialEq, Eq, Hash)]
1804struct HashableNumber {
1805 real: HashableReal,
1806 imag: HashableReal,
1807}
1808
1809impl HashableNumber {
1810 fn real(real: HashableReal) -> Self {
1811 Self {
1812 real,
1813 imag: HashableReal::Integer(0),
1814 }
1815 }
1816
1817 fn complex(real: HashableReal, imag: HashableReal) -> Self {
1818 Self { real, imag }
1819 }
1820
1821 fn negate(mut self) -> Self {
1822 self.real.negate();
1823 self.imag.negate();
1824 self
1825 }
1826
1827 fn into_real(self) -> Option<HashableReal> {
1828 self.imag.is_zero().then_some(self.real)
1829 }
1830}
1831
1832#[derive(Debug, PartialEq, Eq, Hash)]
1833enum HashableReal {
1834 Integer(i128),
1835 Float(u64),
1836}
1837
1838impl HashableReal {
1839 fn from_int(value: &ast::Int) -> Option<Self> {
1840 value.as_u64().map(i128::from).map(Self::Integer)
1841 }
1842
1843 #[expect(
1844 clippy::cast_possible_truncation,
1845 clippy::cast_precision_loss,
1846 clippy::float_cmp,
1847 reason = "the round-trip check guarantees that the float is exactly representable as an integer"
1848 )]
1849 fn from_float(value: f64) -> Self {
1850 if value.is_finite() && value.abs() < U64_EXCLUSIVE_UPPER_BOUND {
1851 let integer = value as i128;
1852 if integer as f64 == value {
1853 return Self::Integer(integer);
1854 }
1855 }
1856 Self::Float(value.to_bits())
1857 }
1858
1859 #[expect(
1860 clippy::cast_precision_loss,
1861 reason = "Python converts real components to floats before complex arithmetic"
1862 )]
1863 fn into_float(self) -> Self {
1864 match self {
1865 Self::Integer(integer) => Self::from_float(integer as f64),
1866 Self::Float(_) => self,
1867 }
1868 }
1869
1870 fn is_zero(&self) -> bool {
1871 matches!(self, Self::Integer(0))
1872 }
1873
1874 fn negate(&mut self) {
1875 match self {
1876 Self::Integer(integer) => *integer = -*integer,
1877 Self::Float(bits) => *bits ^= 1 << 63,
1878 }
1879 }
1880}
1881
1882const U64_EXCLUSIVE_UPPER_BOUND: f64 = 18_446_744_073_709_551_616.0;
1884
1885impl<'a> From<&'a Expr> for HashableExpr<'a> {
1886 fn from(expr: &'a Expr) -> Self {
1887 fn as_hashable(expr: &Expr) -> HashableExprKind<'_> {
1890 if let Some(constant) = as_hashable_constant(expr) {
1891 return constant;
1892 }
1893
1894 match expr {
1895 Expr::Named(named) => HashableExprKind::NamedExpr {
1896 target: ComparableExpr::from(&named.target),
1897 value: Box::new(as_hashable(&named.value)),
1898 },
1899 _ => HashableExprKind::Comparable(ComparableExpr::from(expr)),
1900 }
1901 }
1902
1903 fn as_hashable_constant(expr: &Expr) -> Option<HashableExprKind<'_>> {
1905 if let Some(number) = as_number(expr) {
1906 return Some(HashableExprKind::Number(number));
1907 }
1908
1909 let kind = match expr {
1910 Expr::Tuple(tuple) => HashableExprKind::Tuple(
1911 tuple
1912 .iter()
1913 .map(as_hashable_constant)
1914 .collect::<Option<_>>()?,
1915 ),
1916 _ if expr.is_literal_expr() => {
1917 HashableExprKind::Comparable(ComparableExpr::from(expr))
1918 }
1919 _ => return None,
1920 };
1921
1922 Some(kind)
1923 }
1924
1925 fn as_number(expr: &Expr) -> Option<HashableNumber> {
1926 match expr {
1927 Expr::BooleanLiteral(boolean) => Some(HashableNumber::real(HashableReal::Integer(
1928 i128::from(u8::from(boolean.value)),
1929 ))),
1930 Expr::Constant(ast::ExprConstant {
1931 value: ConstantValue::Boolean(value),
1932 ..
1933 }) => Some(HashableNumber::real(HashableReal::Integer(i128::from(
1934 u8::from(*value),
1935 )))),
1936 Expr::NumberLiteral(number) => match &number.value {
1937 Number::Int(int) => HashableReal::from_int(int).map(HashableNumber::real),
1938 Number::Float(float) => {
1939 Some(HashableNumber::real(HashableReal::from_float(*float)))
1940 }
1941 Number::Complex { real, imag } => Some(HashableNumber::complex(
1942 HashableReal::from_float(*real),
1943 HashableReal::from_float(*imag),
1944 )),
1945 },
1946 Expr::UnaryOp(ast::ExprUnaryOp { op, operand, .. }) => match op {
1947 ast::UnaryOp::UAdd => as_number(operand),
1948 ast::UnaryOp::USub => as_number(operand).map(HashableNumber::negate),
1949 ast::UnaryOp::Invert | ast::UnaryOp::Not => None,
1950 },
1951 Expr::BinOp(ast::ExprBinOp {
1952 left,
1953 op: op @ (ast::Operator::Add | ast::Operator::Sub),
1954 right,
1955 ..
1956 }) => {
1957 let real = as_number(left)?.into_real()?.into_float();
1958 let Expr::NumberLiteral(ast::ExprNumberLiteral {
1959 value:
1960 Number::Complex {
1961 real: complex_real,
1962 imag,
1963 },
1964 ..
1965 }) = right.as_ref()
1966 else {
1967 return None;
1968 };
1969 let complex_real = HashableReal::from_float(*complex_real);
1970 if !complex_real.is_zero() {
1971 return None;
1972 }
1973 let mut imag = HashableReal::from_float(*imag);
1974 if op.is_sub() {
1975 imag.negate();
1976 }
1977 Some(HashableNumber::complex(real, imag))
1978 }
1979 _ => None,
1980 }
1981 }
1982
1983 Self(as_hashable(expr))
1984 }
1985}