Skip to main content

rustpython_ruff_python_ast/
comparable.rs

1//! An equivalent object hierarchy to the `RustPython` AST hierarchy, but with the
2//! ability to compare expressions for equality (via [`Eq`] and [`Hash`]).
3//!
4//! Two [`ComparableExpr`]s are considered equal if the underlying AST nodes have the
5//! same shape, ignoring trivia (e.g., parentheses, comments, and whitespace), the
6//! location in the source code, and other contextual information (e.g., whether they
7//! represent reads or writes, which is typically encoded in the Python AST).
8//!
9//! For example, in `[(a, b) for a, b in c]`, the `(a, b)` and `a, b` expressions are
10//! considered equal, despite the former being parenthesized, and despite the former
11//! being a write ([`ast::ExprContext::Store`]) and the latter being a read
12//! ([`ast::ExprContext::Load`]).
13//!
14//! Similarly, `"a" "b"` and `"ab"` would be considered equal, despite the former being
15//! an implicit concatenation of string literals, as these expressions are considered to
16//! have the same shape in that they evaluate to the same value.
17
18use 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)]
521pub struct InterpolatedElement<'a> {
522    expression: ComparableExpr<'a>,
523    debug_text: Option<&'a ast::DebugText>,
524    conversion: ast::ConversionFlag,
525    format_spec: Option<Vec<ComparableInterpolatedStringElement<'a>>>,
526}
527
528impl<'a> From<&'a ast::InterpolatedStringElement> for ComparableInterpolatedStringElement<'a> {
529    fn from(interpolated_string_element: &'a ast::InterpolatedStringElement) -> Self {
530        match interpolated_string_element {
531            ast::InterpolatedStringElement::Literal(ast::InterpolatedStringLiteralElement {
532                value,
533                ..
534            }) => Self::Literal(value.as_ref().into()),
535            ast::InterpolatedStringElement::Interpolation(formatted_value) => {
536                formatted_value.into()
537            }
538        }
539    }
540}
541
542impl<'a> From<&'a ast::InterpolatedElement> for InterpolatedElement<'a> {
543    fn from(interpolated_element: &'a ast::InterpolatedElement) -> Self {
544        let ast::InterpolatedElement {
545            expression,
546            debug_text,
547            conversion,
548            format_spec,
549            range: _,
550            node_index: _,
551        } = interpolated_element;
552
553        Self {
554            expression: (expression).into(),
555            debug_text: debug_text.as_ref(),
556            conversion: *conversion,
557            format_spec: format_spec
558                .as_ref()
559                .map(|spec| spec.elements.iter().map(Into::into).collect()),
560        }
561    }
562}
563
564impl<'a> From<&'a ast::InterpolatedElement> for ComparableInterpolatedStringElement<'a> {
565    fn from(interpolated_element: &'a ast::InterpolatedElement) -> Self {
566        Self::InterpolatedElement(interpolated_element.into())
567    }
568}
569
570#[derive(Debug, PartialEq, Eq, Hash)]
571pub struct ComparableElifElseClause<'a> {
572    test: Option<ComparableExpr<'a>>,
573    body: Vec<ComparableStmt<'a>>,
574}
575
576impl<'a> From<&'a ast::ElifElseClause> for ComparableElifElseClause<'a> {
577    fn from(elif_else_clause: &'a ast::ElifElseClause) -> Self {
578        let ast::ElifElseClause {
579            range: _,
580            node_index: _,
581            test,
582            body,
583        } = elif_else_clause;
584        Self {
585            test: test.as_ref().map(Into::into),
586            body: body.iter().map(Into::into).collect(),
587        }
588    }
589}
590
591#[derive(Debug, PartialEq, Eq, Hash)]
592pub enum ComparableLiteral<'a> {
593    None,
594    Ellipsis,
595    Bool(&'a bool),
596    Str(Vec<ComparableStringLiteral<'a>>),
597    Bytes(Vec<ComparableBytesLiteral<'a>>),
598    Number(ComparableNumber<'a>),
599}
600
601impl<'a> From<ast::LiteralExpressionRef<'a>> for ComparableLiteral<'a> {
602    fn from(literal: ast::LiteralExpressionRef<'a>) -> Self {
603        match literal {
604            ast::LiteralExpressionRef::NoneLiteral(_) => Self::None,
605            ast::LiteralExpressionRef::EllipsisLiteral(_) => Self::Ellipsis,
606            ast::LiteralExpressionRef::BooleanLiteral(ast::ExprBooleanLiteral {
607                value, ..
608            }) => Self::Bool(value),
609            ast::LiteralExpressionRef::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
610                Self::Str(value.iter().map(Into::into).collect())
611            }
612            ast::LiteralExpressionRef::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => {
613                Self::Bytes(value.iter().map(Into::into).collect())
614            }
615            ast::LiteralExpressionRef::NumberLiteral(ast::ExprNumberLiteral { value, .. }) => {
616                Self::Number(value.into())
617            }
618        }
619    }
620}
621
622#[derive(Debug, PartialEq, Eq, Hash)]
623pub struct ComparableFString<'a> {
624    elements: Box<[ComparableInterpolatedStringElement<'a>]>,
625}
626
627impl<'a> From<&'a ast::FStringValue> for ComparableFString<'a> {
628    // The approach below is somewhat complicated, so it may
629    // require some justification.
630    //
631    // Suppose given an f-string of the form
632    // `f"{foo!r} one" " and two " f" and three {bar!s}"`
633    // This decomposes as:
634    // - An `FStringPart::FString`, `f"{foo!r} one"` with elements
635    //      - `FStringElement::Expression` encoding `{foo!r}`
636    //      - `FStringElement::Literal` encoding " one"
637    // - An `FStringPart::Literal` capturing `" and two "`
638    // - An `FStringPart::FString`, `f" and three {bar!s}"` with elements
639    //      - `FStringElement::Literal` encoding " and three "
640    //      - `FStringElement::Expression` encoding `{bar!s}`
641    //
642    // We would like to extract from this a vector of (comparable) f-string
643    // _elements_ which alternate between expression elements and literal
644    // elements. In order to do so, we need to concatenate adjacent string
645    // literals. String literals may be separated for two reasons: either
646    // they appear in adjacent string literal parts, or else a string literal
647    // part is adjacent to a string literal _element_ inside of an f-string part.
648    fn from(value: &'a ast::FStringValue) -> Self {
649        #[derive(Default)]
650        struct Collector<'a> {
651            elements: Vec<ComparableInterpolatedStringElement<'a>>,
652        }
653
654        impl<'a> Collector<'a> {
655            // The logic for concatenating adjacent string literals
656            // occurs here, implicitly: when we encounter a sequence
657            // of string literals, the first gets pushed to the
658            // `elements` vector, while subsequent strings
659            // are concatenated onto this top string.
660            fn push_literal(&mut self, literal: &'a str) {
661                if let Some(ComparableInterpolatedStringElement::Literal(existing_literal)) =
662                    self.elements.last_mut()
663                {
664                    existing_literal.to_mut().push_str(literal);
665                } else {
666                    self.elements
667                        .push(ComparableInterpolatedStringElement::Literal(literal.into()));
668                }
669            }
670
671            fn push_expression(&mut self, expression: &'a ast::InterpolatedElement) {
672                self.elements.push(expression.into());
673            }
674        }
675
676        let mut collector = Collector::default();
677
678        for part in value {
679            match part {
680                ast::FStringPart::Literal(string_literal) => {
681                    collector.push_literal(&string_literal.value);
682                }
683                ast::FStringPart::FString(fstring) => {
684                    for element in &fstring.elements {
685                        match element {
686                            ast::InterpolatedStringElement::Literal(literal) => {
687                                collector.push_literal(&literal.value);
688                            }
689                            ast::InterpolatedStringElement::Interpolation(expression) => {
690                                collector.push_expression(expression);
691                            }
692                        }
693                    }
694                }
695            }
696        }
697
698        Self {
699            elements: collector.elements.into_boxed_slice(),
700        }
701    }
702}
703
704#[derive(Debug, PartialEq, Eq, Hash)]
705pub struct ComparableTString<'a> {
706    strings: Box<[ComparableInterpolatedStringElement<'a>]>,
707    interpolations: Box<[InterpolatedElement<'a>]>,
708}
709
710impl<'a> From<&'a ast::TStringValue> for ComparableTString<'a> {
711    // We model a [`ComparableTString`] on the actual
712    // [CPython implementation] of a `string.templatelib.Template` object.
713    //
714    // As in CPython, we must be careful to ensure that the length
715    // of `strings` is always one more than the length of `interpolations` -
716    // that way we can recover the original reading order by interleaving
717    // starting with `strings`. This is how we can tell the
718    // difference between, e.g. `t"{foo}bar"` and `t"bar{foo}"`.
719    //
720    // - [CPython implementation](https://github.com/python/cpython/blob/c91ad5da9d92eac4718e4da8d53689c3cc24535e/Python/codegen.c#L4052-L4103)
721    fn from(value: &'a ast::TStringValue) -> Self {
722        struct Collector<'a> {
723            strings: Vec<ComparableInterpolatedStringElement<'a>>,
724            interpolations: Vec<InterpolatedElement<'a>>,
725        }
726
727        impl Default for Collector<'_> {
728            fn default() -> Self {
729                Self {
730                    strings: vec![ComparableInterpolatedStringElement::Literal("".into())],
731                    interpolations: vec![],
732                }
733            }
734        }
735
736        impl<'a> Collector<'a> {
737            // The logic for concatenating adjacent string literals
738            // occurs here, implicitly: when we encounter a sequence
739            // of string literals, the first gets pushed to the
740            // `strings` vector, while subsequent strings
741            // are concatenated onto this top string.
742            fn push_literal(&mut self, literal: &'a str) {
743                if let Some(ComparableInterpolatedStringElement::Literal(existing_literal)) =
744                    self.strings.last_mut()
745                {
746                    existing_literal.to_mut().push_str(literal);
747                } else {
748                    self.strings
749                        .push(ComparableInterpolatedStringElement::Literal(literal.into()));
750                }
751            }
752
753            fn start_new_literal(&mut self) {
754                self.strings
755                    .push(ComparableInterpolatedStringElement::Literal("".into()));
756            }
757
758            fn push_tstring_interpolation(&mut self, expression: &'a ast::InterpolatedElement) {
759                self.interpolations.push(expression.into());
760                self.start_new_literal();
761            }
762        }
763
764        let mut collector = Collector::default();
765
766        for element in value.elements() {
767            match element {
768                ast::InterpolatedStringElement::Literal(literal) => {
769                    collector.push_literal(&literal.value);
770                }
771                ast::InterpolatedStringElement::Interpolation(interpolation) => {
772                    collector.push_tstring_interpolation(interpolation);
773                }
774            }
775        }
776
777        Self {
778            strings: collector.strings.into_boxed_slice(),
779            interpolations: collector.interpolations.into_boxed_slice(),
780        }
781    }
782}
783
784#[derive(Debug, PartialEq, Eq, Hash)]
785pub struct ComparableStringLiteral<'a> {
786    value: &'a str,
787}
788
789impl<'a> From<&'a ast::StringLiteral> for ComparableStringLiteral<'a> {
790    fn from(string_literal: &'a ast::StringLiteral) -> Self {
791        Self {
792            value: &string_literal.value,
793        }
794    }
795}
796
797#[derive(Debug, PartialEq, Eq, Hash)]
798pub struct ComparableBytesLiteral<'a> {
799    value: Cow<'a, [u8]>,
800}
801
802impl<'a> From<&'a ast::BytesLiteral> for ComparableBytesLiteral<'a> {
803    fn from(bytes_literal: &'a ast::BytesLiteral) -> Self {
804        Self {
805            value: Cow::Borrowed(&bytes_literal.value),
806        }
807    }
808}
809
810#[derive(Debug, PartialEq, Eq, Hash)]
811pub struct ExprBoolOp<'a> {
812    op: ComparableBoolOp,
813    values: Vec<ComparableExpr<'a>>,
814}
815
816#[derive(Debug, PartialEq, Eq, Hash)]
817pub struct ExprNamed<'a> {
818    target: Box<ComparableExpr<'a>>,
819    value: Box<ComparableExpr<'a>>,
820}
821
822#[derive(Debug, PartialEq, Eq, Hash)]
823pub struct ExprBinOp<'a> {
824    left: Box<ComparableExpr<'a>>,
825    op: ComparableOperator,
826    right: Box<ComparableExpr<'a>>,
827}
828
829#[derive(Debug, PartialEq, Eq, Hash)]
830pub struct ExprUnaryOp<'a> {
831    op: ComparableUnaryOp,
832    operand: Box<ComparableExpr<'a>>,
833}
834
835#[derive(Debug, PartialEq, Eq, Hash)]
836pub struct ExprLambda<'a> {
837    parameters: Option<ComparableParameters<'a>>,
838    body: Box<ComparableExpr<'a>>,
839}
840
841#[derive(Debug, PartialEq, Eq, Hash)]
842pub struct ExprIf<'a> {
843    test: Box<ComparableExpr<'a>>,
844    body: Box<ComparableExpr<'a>>,
845    orelse: Box<ComparableExpr<'a>>,
846}
847
848#[derive(Debug, PartialEq, Eq, Hash)]
849pub struct ComparableDictItem<'a> {
850    key: Option<ComparableExpr<'a>>,
851    value: ComparableExpr<'a>,
852}
853
854impl<'a> From<&'a ast::DictItem> for ComparableDictItem<'a> {
855    fn from(ast::DictItem { key, value }: &'a ast::DictItem) -> Self {
856        Self {
857            key: key.as_ref().map(ComparableExpr::from),
858            value: value.into(),
859        }
860    }
861}
862
863#[derive(Debug, PartialEq, Eq, Hash)]
864pub struct ExprDict<'a> {
865    items: Vec<ComparableDictItem<'a>>,
866}
867
868#[derive(Debug, PartialEq, Eq, Hash)]
869pub struct ExprSet<'a> {
870    elts: Vec<ComparableExpr<'a>>,
871}
872
873#[derive(Debug, PartialEq, Eq, Hash)]
874pub struct ExprListComp<'a> {
875    elt: Box<ComparableExpr<'a>>,
876    generators: Vec<ComparableComprehension<'a>>,
877}
878
879#[derive(Debug, PartialEq, Eq, Hash)]
880pub struct ExprSetComp<'a> {
881    elt: Box<ComparableExpr<'a>>,
882    generators: Vec<ComparableComprehension<'a>>,
883}
884
885#[derive(Debug, PartialEq, Eq, Hash)]
886pub struct ExprDictComp<'a> {
887    key: Box<ComparableExpr<'a>>,
888    value: Box<ComparableExpr<'a>>,
889    generators: Vec<ComparableComprehension<'a>>,
890}
891
892#[derive(Debug, PartialEq, Eq, Hash)]
893pub struct ExprGenerator<'a> {
894    elt: Box<ComparableExpr<'a>>,
895    generators: Vec<ComparableComprehension<'a>>,
896}
897
898#[derive(Debug, PartialEq, Eq, Hash)]
899pub struct ExprAwait<'a> {
900    value: Box<ComparableExpr<'a>>,
901}
902
903#[derive(Debug, PartialEq, Eq, Hash)]
904pub struct ExprYield<'a> {
905    value: Option<Box<ComparableExpr<'a>>>,
906}
907
908#[derive(Debug, PartialEq, Eq, Hash)]
909pub struct ExprYieldFrom<'a> {
910    value: Box<ComparableExpr<'a>>,
911}
912
913#[derive(Debug, PartialEq, Eq, Hash)]
914pub struct ExprCompare<'a> {
915    left: Box<ComparableExpr<'a>>,
916    ops: Vec<ComparableCmpOp>,
917    comparators: Vec<ComparableExpr<'a>>,
918}
919
920#[derive(Debug, PartialEq, Eq, Hash)]
921pub struct ExprCall<'a> {
922    func: Box<ComparableExpr<'a>>,
923    arguments: ComparableArguments<'a>,
924}
925
926#[derive(Debug, PartialEq, Eq, Hash)]
927pub struct ExprInterpolatedElement<'a> {
928    value: Box<ComparableExpr<'a>>,
929    debug_text: Option<&'a ast::DebugText>,
930    conversion: ast::ConversionFlag,
931    format_spec: Vec<ComparableInterpolatedStringElement<'a>>,
932}
933
934#[derive(Debug, PartialEq, Eq, Hash)]
935pub struct ExprFString<'a> {
936    value: ComparableFString<'a>,
937}
938
939#[derive(Debug, PartialEq, Eq, Hash)]
940pub struct ExprTString<'a> {
941    value: ComparableTString<'a>,
942}
943
944#[derive(Debug, PartialEq, Eq, Hash)]
945pub struct ExprStringLiteral<'a> {
946    value: ComparableStringLiteral<'a>,
947}
948
949#[derive(Debug, PartialEq, Eq, Hash)]
950pub struct ExprBytesLiteral<'a> {
951    value: ComparableBytesLiteral<'a>,
952}
953
954#[derive(Debug, PartialEq, Eq, Hash)]
955pub struct ExprNumberLiteral<'a> {
956    value: ComparableNumber<'a>,
957}
958
959#[derive(Debug, PartialEq, Eq, Hash)]
960pub struct ExprBoolLiteral {
961    value: bool,
962}
963
964#[derive(Debug, PartialEq, Eq, Hash)]
965pub struct ExprAttribute<'a> {
966    value: Box<ComparableExpr<'a>>,
967    attr: &'a str,
968}
969
970#[derive(Debug, PartialEq, Eq, Hash)]
971pub struct ExprSubscript<'a> {
972    value: Box<ComparableExpr<'a>>,
973    slice: Box<ComparableExpr<'a>>,
974}
975
976#[derive(Debug, PartialEq, Eq, Hash)]
977pub struct ExprStarred<'a> {
978    value: Box<ComparableExpr<'a>>,
979}
980
981#[derive(Debug, PartialEq, Eq, Hash)]
982pub struct ExprName<'a> {
983    id: &'a str,
984}
985
986#[derive(Debug, PartialEq, Eq, Hash)]
987pub struct ExprList<'a> {
988    elts: Vec<ComparableExpr<'a>>,
989}
990
991#[derive(Debug, PartialEq, Eq, Hash)]
992pub struct ExprTuple<'a> {
993    elts: Vec<ComparableExpr<'a>>,
994}
995
996#[derive(Debug, PartialEq, Eq, Hash)]
997pub struct ExprSlice<'a> {
998    lower: Option<Box<ComparableExpr<'a>>>,
999    upper: Option<Box<ComparableExpr<'a>>>,
1000    step: Option<Box<ComparableExpr<'a>>>,
1001}
1002
1003#[derive(Debug, PartialEq, Eq, Hash)]
1004pub struct ExprIpyEscapeCommand<'a> {
1005    kind: ast::IpyEscapeKind,
1006    value: &'a str,
1007}
1008
1009#[derive(Debug, PartialEq, Eq, Hash)]
1010pub enum ComparableExpr<'a> {
1011    BoolOp(ExprBoolOp<'a>),
1012    NamedExpr(ExprNamed<'a>),
1013    BinOp(ExprBinOp<'a>),
1014    UnaryOp(ExprUnaryOp<'a>),
1015    Lambda(ExprLambda<'a>),
1016    IfExp(ExprIf<'a>),
1017    Dict(ExprDict<'a>),
1018    Set(ExprSet<'a>),
1019    ListComp(ExprListComp<'a>),
1020    SetComp(ExprSetComp<'a>),
1021    DictComp(ExprDictComp<'a>),
1022    GeneratorExp(ExprGenerator<'a>),
1023    Await(ExprAwait<'a>),
1024    Yield(ExprYield<'a>),
1025    YieldFrom(ExprYieldFrom<'a>),
1026    Compare(ExprCompare<'a>),
1027    Call(ExprCall<'a>),
1028    FStringExpressionElement(ExprInterpolatedElement<'a>),
1029    FString(ExprFString<'a>),
1030    TStringInterpolationElement(ExprInterpolatedElement<'a>),
1031    TString(ExprTString<'a>),
1032    StringLiteral(ExprStringLiteral<'a>),
1033    BytesLiteral(ExprBytesLiteral<'a>),
1034    NumberLiteral(ExprNumberLiteral<'a>),
1035    BoolLiteral(ExprBoolLiteral),
1036    NoneLiteral,
1037    EllipsisLiteral,
1038    Attribute(ExprAttribute<'a>),
1039    Subscript(ExprSubscript<'a>),
1040    Starred(ExprStarred<'a>),
1041    Name(ExprName<'a>),
1042    List(ExprList<'a>),
1043    Tuple(ExprTuple<'a>),
1044    Slice(ExprSlice<'a>),
1045    IpyEscapeCommand(ExprIpyEscapeCommand<'a>),
1046}
1047
1048impl<'a> From<&'a Box<ast::Expr>> for Box<ComparableExpr<'a>> {
1049    fn from(expr: &'a Box<ast::Expr>) -> Self {
1050        Box::new((expr.as_ref()).into())
1051    }
1052}
1053
1054impl<'a> From<&'a Box<ast::Expr>> for ComparableExpr<'a> {
1055    fn from(expr: &'a Box<ast::Expr>) -> Self {
1056        (expr.as_ref()).into()
1057    }
1058}
1059
1060impl<'a> From<&'a ast::Expr> for ComparableExpr<'a> {
1061    fn from(expr: &'a ast::Expr) -> Self {
1062        match expr {
1063            ast::Expr::BoolOp(ast::ExprBoolOp {
1064                op,
1065                values,
1066                range: _,
1067                node_index: _,
1068            }) => Self::BoolOp(ExprBoolOp {
1069                op: (*op).into(),
1070                values: values.iter().map(Into::into).collect(),
1071            }),
1072            ast::Expr::Named(ast::ExprNamed {
1073                target,
1074                value,
1075                range: _,
1076                node_index: _,
1077            }) => Self::NamedExpr(ExprNamed {
1078                target: target.into(),
1079                value: value.into(),
1080            }),
1081            ast::Expr::BinOp(ast::ExprBinOp {
1082                left,
1083                op,
1084                right,
1085                range: _,
1086                node_index: _,
1087            }) => Self::BinOp(ExprBinOp {
1088                left: left.into(),
1089                op: (*op).into(),
1090                right: right.into(),
1091            }),
1092            ast::Expr::UnaryOp(ast::ExprUnaryOp {
1093                op,
1094                operand,
1095                range: _,
1096                node_index: _,
1097            }) => Self::UnaryOp(ExprUnaryOp {
1098                op: (*op).into(),
1099                operand: operand.into(),
1100            }),
1101            ast::Expr::Lambda(ast::ExprLambda {
1102                parameters,
1103                body,
1104                range: _,
1105                node_index: _,
1106            }) => Self::Lambda(ExprLambda {
1107                parameters: parameters.as_ref().map(Into::into),
1108                body: body.into(),
1109            }),
1110            ast::Expr::If(ast::ExprIf {
1111                test,
1112                body,
1113                orelse,
1114                range: _,
1115                node_index: _,
1116            }) => Self::IfExp(ExprIf {
1117                test: test.into(),
1118                body: body.into(),
1119                orelse: orelse.into(),
1120            }),
1121            ast::Expr::Dict(ast::ExprDict {
1122                items,
1123                range: _,
1124                node_index: _,
1125            }) => Self::Dict(ExprDict {
1126                items: items.iter().map(ComparableDictItem::from).collect(),
1127            }),
1128            ast::Expr::Set(ast::ExprSet {
1129                elts,
1130                range: _,
1131                node_index: _,
1132            }) => Self::Set(ExprSet {
1133                elts: elts.iter().map(Into::into).collect(),
1134            }),
1135            ast::Expr::ListComp(ast::ExprListComp {
1136                elt,
1137                generators,
1138                range: _,
1139                node_index: _,
1140            }) => Self::ListComp(ExprListComp {
1141                elt: elt.into(),
1142                generators: generators.iter().map(Into::into).collect(),
1143            }),
1144            ast::Expr::SetComp(ast::ExprSetComp {
1145                elt,
1146                generators,
1147                range: _,
1148                node_index: _,
1149            }) => Self::SetComp(ExprSetComp {
1150                elt: elt.into(),
1151                generators: generators.iter().map(Into::into).collect(),
1152            }),
1153            ast::Expr::DictComp(ast::ExprDictComp {
1154                key,
1155                value,
1156                generators,
1157                range: _,
1158                node_index: _,
1159            }) => Self::DictComp(ExprDictComp {
1160                key: key.into(),
1161                value: value.into(),
1162                generators: generators.iter().map(Into::into).collect(),
1163            }),
1164            ast::Expr::Generator(ast::ExprGenerator {
1165                elt,
1166                generators,
1167                range: _,
1168                node_index: _,
1169                parenthesized: _,
1170            }) => Self::GeneratorExp(ExprGenerator {
1171                elt: elt.into(),
1172                generators: generators.iter().map(Into::into).collect(),
1173            }),
1174            ast::Expr::Await(ast::ExprAwait {
1175                value,
1176                range: _,
1177                node_index: _,
1178            }) => Self::Await(ExprAwait {
1179                value: value.into(),
1180            }),
1181            ast::Expr::Yield(ast::ExprYield {
1182                value,
1183                range: _,
1184                node_index: _,
1185            }) => Self::Yield(ExprYield {
1186                value: value.as_ref().map(Into::into),
1187            }),
1188            ast::Expr::YieldFrom(ast::ExprYieldFrom {
1189                value,
1190                range: _,
1191                node_index: _,
1192            }) => Self::YieldFrom(ExprYieldFrom {
1193                value: value.into(),
1194            }),
1195            ast::Expr::Compare(ast::ExprCompare {
1196                left,
1197                ops,
1198                comparators,
1199                range: _,
1200                node_index: _,
1201            }) => Self::Compare(ExprCompare {
1202                left: left.into(),
1203                ops: ops.iter().copied().map(Into::into).collect(),
1204                comparators: comparators.iter().map(Into::into).collect(),
1205            }),
1206            ast::Expr::Call(ast::ExprCall {
1207                func,
1208                arguments,
1209                range: _,
1210                node_index: _,
1211            }) => Self::Call(ExprCall {
1212                func: func.into(),
1213                arguments: arguments.into(),
1214            }),
1215            ast::Expr::FString(ast::ExprFString {
1216                value,
1217                range: _,
1218                node_index: _,
1219            }) => Self::FString(ExprFString {
1220                value: value.into(),
1221            }),
1222            ast::Expr::TString(ast::ExprTString {
1223                value,
1224                range: _,
1225                node_index: _,
1226            }) => Self::TString(ExprTString {
1227                value: value.into(),
1228            }),
1229            ast::Expr::StringLiteral(ast::ExprStringLiteral {
1230                value,
1231                range: _,
1232                node_index: _,
1233            }) => Self::StringLiteral(ExprStringLiteral {
1234                value: ComparableStringLiteral {
1235                    value: value.to_str(),
1236                },
1237            }),
1238            ast::Expr::BytesLiteral(ast::ExprBytesLiteral {
1239                value,
1240                range: _,
1241                node_index: _,
1242            }) => Self::BytesLiteral(ExprBytesLiteral {
1243                value: ComparableBytesLiteral {
1244                    value: Cow::from(value),
1245                },
1246            }),
1247            ast::Expr::NumberLiteral(ast::ExprNumberLiteral {
1248                value,
1249                range: _,
1250                node_index: _,
1251            }) => Self::NumberLiteral(ExprNumberLiteral {
1252                value: value.into(),
1253            }),
1254            ast::Expr::BooleanLiteral(ast::ExprBooleanLiteral {
1255                value,
1256                range: _,
1257                node_index: _,
1258            }) => Self::BoolLiteral(ExprBoolLiteral { value: *value }),
1259            ast::Expr::NoneLiteral(_) => Self::NoneLiteral,
1260            ast::Expr::EllipsisLiteral(_) => Self::EllipsisLiteral,
1261            ast::Expr::Attribute(ast::ExprAttribute {
1262                value,
1263                attr,
1264                ctx: _,
1265                range: _,
1266                node_index: _,
1267            }) => Self::Attribute(ExprAttribute {
1268                value: value.into(),
1269                attr: attr.as_str(),
1270            }),
1271            ast::Expr::Subscript(ast::ExprSubscript {
1272                value,
1273                slice,
1274                ctx: _,
1275                range: _,
1276                node_index: _,
1277            }) => Self::Subscript(ExprSubscript {
1278                value: value.into(),
1279                slice: slice.into(),
1280            }),
1281            ast::Expr::Starred(ast::ExprStarred {
1282                value,
1283                ctx: _,
1284                range: _,
1285                node_index: _,
1286            }) => Self::Starred(ExprStarred {
1287                value: value.into(),
1288            }),
1289            ast::Expr::Name(name) => name.into(),
1290            ast::Expr::List(ast::ExprList {
1291                elts,
1292                ctx: _,
1293                range: _,
1294                node_index: _,
1295            }) => Self::List(ExprList {
1296                elts: elts.iter().map(Into::into).collect(),
1297            }),
1298            ast::Expr::Tuple(ast::ExprTuple {
1299                elts,
1300                ctx: _,
1301                range: _,
1302                node_index: _,
1303                parenthesized: _,
1304            }) => Self::Tuple(ExprTuple {
1305                elts: elts.iter().map(Into::into).collect(),
1306            }),
1307            ast::Expr::Slice(ast::ExprSlice {
1308                lower,
1309                upper,
1310                step,
1311                range: _,
1312                node_index: _,
1313            }) => Self::Slice(ExprSlice {
1314                lower: lower.as_ref().map(Into::into),
1315                upper: upper.as_ref().map(Into::into),
1316                step: step.as_ref().map(Into::into),
1317            }),
1318            ast::Expr::IpyEscapeCommand(ast::ExprIpyEscapeCommand {
1319                kind,
1320                value,
1321                range: _,
1322                node_index: _,
1323            }) => Self::IpyEscapeCommand(ExprIpyEscapeCommand { kind: *kind, value }),
1324        }
1325    }
1326}
1327
1328impl<'a> From<&'a ast::ExprName> for ComparableExpr<'a> {
1329    fn from(expr: &'a ast::ExprName) -> Self {
1330        Self::Name(ExprName {
1331            id: expr.id.as_str(),
1332        })
1333    }
1334}
1335
1336#[derive(Debug, PartialEq, Eq, Hash)]
1337pub struct StmtFunctionDef<'a> {
1338    is_async: bool,
1339    decorator_list: Vec<ComparableDecorator<'a>>,
1340    name: &'a str,
1341    type_params: Option<ComparableTypeParams<'a>>,
1342    parameters: ComparableParameters<'a>,
1343    returns: Option<ComparableExpr<'a>>,
1344    body: Vec<ComparableStmt<'a>>,
1345}
1346
1347#[derive(Debug, PartialEq, Eq, Hash)]
1348pub struct StmtClassDef<'a> {
1349    decorator_list: Vec<ComparableDecorator<'a>>,
1350    name: &'a str,
1351    type_params: Option<ComparableTypeParams<'a>>,
1352    arguments: ComparableArguments<'a>,
1353    body: Vec<ComparableStmt<'a>>,
1354}
1355
1356#[derive(Debug, PartialEq, Eq, Hash)]
1357pub struct StmtReturn<'a> {
1358    value: Option<ComparableExpr<'a>>,
1359}
1360
1361#[derive(Debug, PartialEq, Eq, Hash)]
1362pub struct StmtDelete<'a> {
1363    targets: Vec<ComparableExpr<'a>>,
1364}
1365
1366#[derive(Debug, PartialEq, Eq, Hash)]
1367pub struct StmtTypeAlias<'a> {
1368    pub name: Box<ComparableExpr<'a>>,
1369    pub type_params: Option<ComparableTypeParams<'a>>,
1370    pub value: Box<ComparableExpr<'a>>,
1371}
1372
1373#[derive(Debug, PartialEq, Eq, Hash)]
1374pub struct ComparableTypeParams<'a> {
1375    pub type_params: Vec<ComparableTypeParam<'a>>,
1376}
1377
1378impl<'a> From<&'a ast::TypeParams> for ComparableTypeParams<'a> {
1379    fn from(type_params: &'a ast::TypeParams) -> Self {
1380        Self {
1381            type_params: type_params.iter().map(Into::into).collect(),
1382        }
1383    }
1384}
1385
1386impl<'a> From<&'a Box<ast::TypeParams>> for ComparableTypeParams<'a> {
1387    fn from(type_params: &'a Box<ast::TypeParams>) -> Self {
1388        type_params.as_ref().into()
1389    }
1390}
1391
1392#[derive(Debug, PartialEq, Eq, Hash)]
1393pub enum ComparableTypeParam<'a> {
1394    TypeVar(TypeParamTypeVar<'a>),
1395    ParamSpec(TypeParamParamSpec<'a>),
1396    TypeVarTuple(TypeParamTypeVarTuple<'a>),
1397}
1398
1399impl<'a> From<&'a ast::TypeParam> for ComparableTypeParam<'a> {
1400    fn from(type_param: &'a ast::TypeParam) -> Self {
1401        match type_param {
1402            ast::TypeParam::TypeVar(ast::TypeParamTypeVar {
1403                name,
1404                bound,
1405                default,
1406                range: _,
1407                node_index: _,
1408            }) => Self::TypeVar(TypeParamTypeVar {
1409                name: name.as_str(),
1410                bound: bound.as_ref().map(Into::into),
1411                default: default.as_ref().map(Into::into),
1412            }),
1413            ast::TypeParam::TypeVarTuple(ast::TypeParamTypeVarTuple {
1414                name,
1415                default,
1416                range: _,
1417                node_index: _,
1418            }) => Self::TypeVarTuple(TypeParamTypeVarTuple {
1419                name: name.as_str(),
1420                default: default.as_ref().map(Into::into),
1421            }),
1422            ast::TypeParam::ParamSpec(ast::TypeParamParamSpec {
1423                name,
1424                default,
1425                range: _,
1426                node_index: _,
1427            }) => Self::ParamSpec(TypeParamParamSpec {
1428                name: name.as_str(),
1429                default: default.as_ref().map(Into::into),
1430            }),
1431        }
1432    }
1433}
1434
1435#[derive(Debug, PartialEq, Eq, Hash)]
1436pub struct TypeParamTypeVar<'a> {
1437    pub name: &'a str,
1438    pub bound: Option<Box<ComparableExpr<'a>>>,
1439    pub default: Option<Box<ComparableExpr<'a>>>,
1440}
1441
1442#[derive(Debug, PartialEq, Eq, Hash)]
1443pub struct TypeParamParamSpec<'a> {
1444    pub name: &'a str,
1445    pub default: Option<Box<ComparableExpr<'a>>>,
1446}
1447
1448#[derive(Debug, PartialEq, Eq, Hash)]
1449pub struct TypeParamTypeVarTuple<'a> {
1450    pub name: &'a str,
1451    pub default: Option<Box<ComparableExpr<'a>>>,
1452}
1453
1454#[derive(Debug, PartialEq, Eq, Hash)]
1455pub struct StmtAssign<'a> {
1456    targets: Vec<ComparableExpr<'a>>,
1457    value: ComparableExpr<'a>,
1458}
1459
1460#[derive(Debug, PartialEq, Eq, Hash)]
1461pub struct StmtAugAssign<'a> {
1462    target: ComparableExpr<'a>,
1463    op: ComparableOperator,
1464    value: ComparableExpr<'a>,
1465}
1466
1467#[derive(Debug, PartialEq, Eq, Hash)]
1468pub struct StmtAnnAssign<'a> {
1469    target: ComparableExpr<'a>,
1470    annotation: ComparableExpr<'a>,
1471    value: Option<ComparableExpr<'a>>,
1472    simple: bool,
1473}
1474
1475#[derive(Debug, PartialEq, Eq, Hash)]
1476pub struct StmtFor<'a> {
1477    is_async: bool,
1478    target: ComparableExpr<'a>,
1479    iter: ComparableExpr<'a>,
1480    body: Vec<ComparableStmt<'a>>,
1481    orelse: Vec<ComparableStmt<'a>>,
1482}
1483
1484#[derive(Debug, PartialEq, Eq, Hash)]
1485pub struct StmtWhile<'a> {
1486    test: ComparableExpr<'a>,
1487    body: Vec<ComparableStmt<'a>>,
1488    orelse: Vec<ComparableStmt<'a>>,
1489}
1490
1491#[derive(Debug, PartialEq, Eq, Hash)]
1492pub struct StmtIf<'a> {
1493    test: ComparableExpr<'a>,
1494    body: Vec<ComparableStmt<'a>>,
1495    elif_else_clauses: Vec<ComparableElifElseClause<'a>>,
1496}
1497
1498#[derive(Debug, PartialEq, Eq, Hash)]
1499pub struct StmtWith<'a> {
1500    is_async: bool,
1501    items: Vec<ComparableWithItem<'a>>,
1502    body: Vec<ComparableStmt<'a>>,
1503}
1504
1505#[derive(Debug, PartialEq, Eq, Hash)]
1506pub struct StmtMatch<'a> {
1507    subject: ComparableExpr<'a>,
1508    cases: Vec<ComparableMatchCase<'a>>,
1509}
1510
1511#[derive(Debug, PartialEq, Eq, Hash)]
1512pub struct StmtRaise<'a> {
1513    exc: Option<ComparableExpr<'a>>,
1514    cause: Option<ComparableExpr<'a>>,
1515}
1516
1517#[derive(Debug, PartialEq, Eq, Hash)]
1518pub struct StmtTry<'a> {
1519    body: Vec<ComparableStmt<'a>>,
1520    handlers: Vec<ComparableExceptHandler<'a>>,
1521    orelse: Vec<ComparableStmt<'a>>,
1522    finalbody: Vec<ComparableStmt<'a>>,
1523    is_star: bool,
1524}
1525
1526#[derive(Debug, PartialEq, Eq, Hash)]
1527pub struct StmtAssert<'a> {
1528    test: ComparableExpr<'a>,
1529    msg: Option<ComparableExpr<'a>>,
1530}
1531
1532#[derive(Debug, PartialEq, Eq, Hash)]
1533pub struct StmtImport<'a> {
1534    names: Vec<ComparableAlias<'a>>,
1535    is_lazy: bool,
1536}
1537
1538#[derive(Debug, PartialEq, Eq, Hash)]
1539pub struct StmtImportFrom<'a> {
1540    module: Option<&'a str>,
1541    names: Vec<ComparableAlias<'a>>,
1542    level: u32,
1543    is_lazy: bool,
1544}
1545
1546#[derive(Debug, PartialEq, Eq, Hash)]
1547pub struct StmtGlobal<'a> {
1548    names: Vec<&'a str>,
1549}
1550
1551#[derive(Debug, PartialEq, Eq, Hash)]
1552pub struct StmtNonlocal<'a> {
1553    names: Vec<&'a str>,
1554}
1555
1556#[derive(Debug, PartialEq, Eq, Hash)]
1557pub struct StmtExpr<'a> {
1558    value: ComparableExpr<'a>,
1559}
1560
1561#[derive(Debug, PartialEq, Eq, Hash)]
1562pub struct StmtIpyEscapeCommand<'a> {
1563    kind: ast::IpyEscapeKind,
1564    value: &'a str,
1565}
1566
1567#[derive(Debug, PartialEq, Eq, Hash)]
1568pub enum ComparableStmt<'a> {
1569    FunctionDef(StmtFunctionDef<'a>),
1570    ClassDef(StmtClassDef<'a>),
1571    Return(StmtReturn<'a>),
1572    Delete(StmtDelete<'a>),
1573    Assign(StmtAssign<'a>),
1574    AugAssign(StmtAugAssign<'a>),
1575    AnnAssign(StmtAnnAssign<'a>),
1576    For(StmtFor<'a>),
1577    While(StmtWhile<'a>),
1578    If(StmtIf<'a>),
1579    With(StmtWith<'a>),
1580    Match(StmtMatch<'a>),
1581    Raise(StmtRaise<'a>),
1582    Try(StmtTry<'a>),
1583    TypeAlias(StmtTypeAlias<'a>),
1584    Assert(StmtAssert<'a>),
1585    Import(StmtImport<'a>),
1586    ImportFrom(StmtImportFrom<'a>),
1587    Global(StmtGlobal<'a>),
1588    Nonlocal(StmtNonlocal<'a>),
1589    IpyEscapeCommand(StmtIpyEscapeCommand<'a>),
1590    Expr(StmtExpr<'a>),
1591    Pass,
1592    Break,
1593    Continue,
1594}
1595
1596impl<'a> From<&'a ast::Stmt> for ComparableStmt<'a> {
1597    fn from(stmt: &'a ast::Stmt) -> Self {
1598        match stmt {
1599            ast::Stmt::FunctionDef(ast::StmtFunctionDef {
1600                is_async,
1601                name,
1602                parameters,
1603                body,
1604                decorator_list,
1605                returns,
1606                type_params,
1607                range: _,
1608                node_index: _,
1609            }) => Self::FunctionDef(StmtFunctionDef {
1610                is_async: *is_async,
1611                name: name.as_str(),
1612                parameters: parameters.into(),
1613                body: body.iter().map(Into::into).collect(),
1614                decorator_list: decorator_list.iter().map(Into::into).collect(),
1615                returns: returns.as_ref().map(Into::into),
1616                type_params: type_params.as_ref().map(Into::into),
1617            }),
1618            ast::Stmt::ClassDef(ast::StmtClassDef {
1619                name,
1620                arguments,
1621                body,
1622                decorator_list,
1623                type_params,
1624                range: _,
1625                node_index: _,
1626            }) => Self::ClassDef(StmtClassDef {
1627                name: name.as_str(),
1628                arguments: arguments.as_ref().map(Into::into).unwrap_or_default(),
1629                body: body.iter().map(Into::into).collect(),
1630                decorator_list: decorator_list.iter().map(Into::into).collect(),
1631                type_params: type_params.as_ref().map(Into::into),
1632            }),
1633            ast::Stmt::Return(ast::StmtReturn {
1634                value,
1635                range: _,
1636                node_index: _,
1637            }) => Self::Return(StmtReturn {
1638                value: value.as_ref().map(Into::into),
1639            }),
1640            ast::Stmt::Delete(ast::StmtDelete {
1641                targets,
1642                range: _,
1643                node_index: _,
1644            }) => Self::Delete(StmtDelete {
1645                targets: targets.iter().map(Into::into).collect(),
1646            }),
1647            ast::Stmt::TypeAlias(ast::StmtTypeAlias {
1648                range: _,
1649                node_index: _,
1650                name,
1651                type_params,
1652                value,
1653            }) => Self::TypeAlias(StmtTypeAlias {
1654                name: name.into(),
1655                type_params: type_params.as_ref().map(Into::into),
1656                value: value.into(),
1657            }),
1658            ast::Stmt::Assign(ast::StmtAssign {
1659                targets,
1660                value,
1661                range: _,
1662                node_index: _,
1663            }) => Self::Assign(StmtAssign {
1664                targets: targets.iter().map(Into::into).collect(),
1665                value: value.into(),
1666            }),
1667            ast::Stmt::AugAssign(ast::StmtAugAssign {
1668                target,
1669                op,
1670                value,
1671                range: _,
1672                node_index: _,
1673            }) => Self::AugAssign(StmtAugAssign {
1674                target: target.into(),
1675                op: (*op).into(),
1676                value: value.into(),
1677            }),
1678            ast::Stmt::AnnAssign(ast::StmtAnnAssign {
1679                target,
1680                annotation,
1681                value,
1682                simple,
1683                range: _,
1684                node_index: _,
1685            }) => Self::AnnAssign(StmtAnnAssign {
1686                target: target.into(),
1687                annotation: annotation.into(),
1688                value: value.as_ref().map(Into::into),
1689                simple: *simple,
1690            }),
1691            ast::Stmt::For(ast::StmtFor {
1692                is_async,
1693                target,
1694                iter,
1695                body,
1696                orelse,
1697                range: _,
1698                node_index: _,
1699            }) => Self::For(StmtFor {
1700                is_async: *is_async,
1701                target: target.into(),
1702                iter: iter.into(),
1703                body: body.iter().map(Into::into).collect(),
1704                orelse: orelse.iter().map(Into::into).collect(),
1705            }),
1706            ast::Stmt::While(ast::StmtWhile {
1707                test,
1708                body,
1709                orelse,
1710                range: _,
1711                node_index: _,
1712            }) => Self::While(StmtWhile {
1713                test: test.into(),
1714                body: body.iter().map(Into::into).collect(),
1715                orelse: orelse.iter().map(Into::into).collect(),
1716            }),
1717            ast::Stmt::If(ast::StmtIf {
1718                test,
1719                body,
1720                elif_else_clauses,
1721                range: _,
1722                node_index: _,
1723            }) => Self::If(StmtIf {
1724                test: test.into(),
1725                body: body.iter().map(Into::into).collect(),
1726                elif_else_clauses: elif_else_clauses.iter().map(Into::into).collect(),
1727            }),
1728            ast::Stmt::With(ast::StmtWith {
1729                is_async,
1730                items,
1731                body,
1732                range: _,
1733                node_index: _,
1734            }) => Self::With(StmtWith {
1735                is_async: *is_async,
1736                items: items.iter().map(Into::into).collect(),
1737                body: body.iter().map(Into::into).collect(),
1738            }),
1739            ast::Stmt::Match(ast::StmtMatch {
1740                subject,
1741                cases,
1742                range: _,
1743                node_index: _,
1744            }) => Self::Match(StmtMatch {
1745                subject: subject.into(),
1746                cases: cases.iter().map(Into::into).collect(),
1747            }),
1748            ast::Stmt::Raise(ast::StmtRaise {
1749                exc,
1750                cause,
1751                range: _,
1752                node_index: _,
1753            }) => Self::Raise(StmtRaise {
1754                exc: exc.as_ref().map(Into::into),
1755                cause: cause.as_ref().map(Into::into),
1756            }),
1757            ast::Stmt::Try(ast::StmtTry {
1758                body,
1759                handlers,
1760                orelse,
1761                finalbody,
1762                is_star,
1763                range: _,
1764                node_index: _,
1765            }) => Self::Try(StmtTry {
1766                body: body.iter().map(Into::into).collect(),
1767                handlers: handlers.iter().map(Into::into).collect(),
1768                orelse: orelse.iter().map(Into::into).collect(),
1769                finalbody: finalbody.iter().map(Into::into).collect(),
1770                is_star: *is_star,
1771            }),
1772            ast::Stmt::Assert(ast::StmtAssert {
1773                test,
1774                msg,
1775                range: _,
1776                node_index: _,
1777            }) => Self::Assert(StmtAssert {
1778                test: test.into(),
1779                msg: msg.as_ref().map(Into::into),
1780            }),
1781            ast::Stmt::Import(ast::StmtImport {
1782                names,
1783                is_lazy,
1784                range: _,
1785                node_index: _,
1786            }) => Self::Import(StmtImport {
1787                names: names.iter().map(Into::into).collect(),
1788                is_lazy: *is_lazy,
1789            }),
1790            ast::Stmt::ImportFrom(ast::StmtImportFrom {
1791                module,
1792                names,
1793                level,
1794                is_lazy,
1795                range: _,
1796                node_index: _,
1797            }) => Self::ImportFrom(StmtImportFrom {
1798                module: module.as_deref(),
1799                names: names.iter().map(Into::into).collect(),
1800                level: *level,
1801                is_lazy: *is_lazy,
1802            }),
1803            ast::Stmt::Global(ast::StmtGlobal {
1804                names,
1805                range: _,
1806                node_index: _,
1807            }) => Self::Global(StmtGlobal {
1808                names: names.iter().map(ast::Identifier::as_str).collect(),
1809            }),
1810            ast::Stmt::Nonlocal(ast::StmtNonlocal {
1811                names,
1812                range: _,
1813                node_index: _,
1814            }) => Self::Nonlocal(StmtNonlocal {
1815                names: names.iter().map(ast::Identifier::as_str).collect(),
1816            }),
1817            ast::Stmt::IpyEscapeCommand(ast::StmtIpyEscapeCommand {
1818                kind,
1819                value,
1820                range: _,
1821                node_index: _,
1822            }) => Self::IpyEscapeCommand(StmtIpyEscapeCommand { kind: *kind, value }),
1823            ast::Stmt::Expr(ast::StmtExpr {
1824                value,
1825                range: _,
1826                node_index: _,
1827            }) => Self::Expr(StmtExpr {
1828                value: value.into(),
1829            }),
1830            ast::Stmt::Pass(_) => Self::Pass,
1831            ast::Stmt::Break(_) => Self::Break,
1832            ast::Stmt::Continue(_) => Self::Continue,
1833        }
1834    }
1835}
1836
1837#[derive(Debug, PartialEq, Eq, Hash)]
1838pub enum ComparableMod<'a> {
1839    Module(ComparableModModule<'a>),
1840    Expression(ComparableModExpression<'a>),
1841}
1842
1843#[derive(Debug, PartialEq, Eq, Hash)]
1844pub struct ComparableModModule<'a> {
1845    body: Vec<ComparableStmt<'a>>,
1846}
1847
1848#[derive(Debug, PartialEq, Eq, Hash)]
1849pub struct ComparableModExpression<'a> {
1850    body: Box<ComparableExpr<'a>>,
1851}
1852
1853impl<'a> From<&'a ast::Mod> for ComparableMod<'a> {
1854    fn from(mod_: &'a ast::Mod) -> Self {
1855        match mod_ {
1856            ast::Mod::Module(module) => Self::Module(module.into()),
1857            ast::Mod::Expression(expr) => Self::Expression(expr.into()),
1858        }
1859    }
1860}
1861
1862impl<'a> From<&'a ast::ModModule> for ComparableModModule<'a> {
1863    fn from(module: &'a ast::ModModule) -> Self {
1864        Self {
1865            body: module.body.iter().map(Into::into).collect(),
1866        }
1867    }
1868}
1869
1870impl<'a> From<&'a ast::ModExpression> for ComparableModExpression<'a> {
1871    fn from(expr: &'a ast::ModExpression) -> Self {
1872        Self {
1873            body: (&expr.body).into(),
1874        }
1875    }
1876}
1877
1878/// Wrapper around [`Expr`] that implements [`Hash`] and [`PartialEq`] according to Python
1879/// semantics:
1880///
1881/// > Values that compare equal (such as 1, 1.0, and True) can be used interchangeably to index the
1882/// > same dictionary entry.
1883///
1884/// For example, considers `True`, `1`, and `1.0` to be equal, as they hash to the same value
1885/// in Python, along with `False`, `0`, and `0.0`.
1886///
1887/// See: <https://docs.python.org/3/library/stdtypes.html#mapping-types-dict>
1888#[derive(Debug)]
1889pub struct HashableExpr<'a>(ComparableExpr<'a>);
1890
1891impl Hash for HashableExpr<'_> {
1892    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1893        self.0.hash(state);
1894    }
1895}
1896
1897impl PartialEq<Self> for HashableExpr<'_> {
1898    fn eq(&self, other: &Self) -> bool {
1899        self.0 == other.0
1900    }
1901}
1902
1903impl Eq for HashableExpr<'_> {}
1904
1905impl<'a> From<&'a Expr> for HashableExpr<'a> {
1906    fn from(expr: &'a Expr) -> Self {
1907        /// Returns a version of the given expression that can be hashed and compared according to
1908        /// Python  semantics.
1909        fn as_hashable(expr: &Expr) -> ComparableExpr<'_> {
1910            match expr {
1911                Expr::Named(named) => ComparableExpr::NamedExpr(ExprNamed {
1912                    target: Box::new(ComparableExpr::from(&named.target)),
1913                    value: Box::new(as_hashable(&named.value)),
1914                }),
1915                Expr::NumberLiteral(number) => as_bool(number)
1916                    .map(|value| ComparableExpr::BoolLiteral(ExprBoolLiteral { value }))
1917                    .unwrap_or_else(|| ComparableExpr::from(expr)),
1918                Expr::Tuple(tuple) => ComparableExpr::Tuple(ExprTuple {
1919                    elts: tuple.iter().map(as_hashable).collect(),
1920                }),
1921                _ => ComparableExpr::from(expr),
1922            }
1923        }
1924
1925        /// Returns the `bool` value of the given expression, if it has an equivalent hash to
1926        /// `True` or `False`.
1927        fn as_bool(number: &crate::ExprNumberLiteral) -> Option<bool> {
1928            match &number.value {
1929                Number::Int(int) => match int.as_u8() {
1930                    Some(0) => Some(false),
1931                    Some(1) => Some(true),
1932                    _ => None,
1933                },
1934                Number::Float(float) => match float {
1935                    0.0 => Some(false),
1936                    1.0 => Some(true),
1937                    _ => None,
1938                },
1939                Number::Complex { real, imag } => match (real, imag) {
1940                    (0.0, 0.0) => Some(false),
1941                    (1.0, 0.0) => Some(true),
1942                    _ => None,
1943                },
1944            }
1945        }
1946
1947        Self(as_hashable(expr))
1948    }
1949}