Skip to main content

ruff_python_ast/
helpers.rs

1use std::borrow::Cow;
2use std::path::Path;
3
4use rustc_hash::FxHashMap;
5
6use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer, indentation_at_offset};
7use ruff_source_file::LineRanges;
8use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
9
10use crate::name::{Name, QualifiedName, QualifiedNameBuilder};
11use crate::statement_visitor::StatementVisitor;
12use crate::token::Tokens;
13use crate::token::parenthesized_range;
14use crate::visitor::Visitor;
15use crate::{
16    self as ast, Arguments, AtomicNodeIndex, CmpOp, DictItem, ExceptHandler, Expr, ExprNoneLiteral,
17    InterpolatedStringElement, MatchCase, Operator, Pattern, Stmt, TypeParam,
18};
19use crate::{AnyNodeRef, ExprContext};
20
21/// Return `true` if the `Stmt` is a compound statement (as opposed to a simple statement).
22pub const fn is_compound_statement(stmt: &Stmt) -> bool {
23    matches!(
24        stmt,
25        Stmt::FunctionDef(_)
26            | Stmt::ClassDef(_)
27            | Stmt::While(_)
28            | Stmt::For(_)
29            | Stmt::Match(_)
30            | Stmt::With(_)
31            | Stmt::If(_)
32            | Stmt::Try(_)
33    )
34}
35
36fn is_iterable_initializer<F>(id: &str, is_builtin: F) -> bool
37where
38    F: Fn(&str) -> bool,
39{
40    matches!(id, "list" | "tuple" | "set" | "dict" | "frozenset") && is_builtin(id)
41}
42
43/// Return `true` if the `Expr` contains an expression that appears to include a
44/// side-effect (like a function call).
45///
46/// Accepts a closure that determines whether a given name (e.g., `"list"`) is a Python builtin.
47pub fn contains_effect<F>(expr: &Expr, is_builtin: F) -> bool
48where
49    F: Fn(&str) -> bool,
50{
51    any_over_expr(expr, &|expr| {
52        // Accept empty initializers.
53        if let Expr::Call(ast::ExprCall {
54            func,
55            arguments,
56            range: _,
57            node_index: _,
58        }) = expr
59        {
60            // Ex) `list()`
61            if arguments.is_empty() {
62                if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
63                    if !is_iterable_initializer(id.as_str(), |id| is_builtin(id)) {
64                        return true;
65                    }
66                    return false;
67                }
68            }
69        }
70
71        // Avoid false positive for overloaded operators.
72        if let Expr::BinOp(ast::ExprBinOp { left, right, .. }) = expr {
73            if !matches!(
74                left.as_ref(),
75                Expr::StringLiteral(_)
76                    | Expr::BytesLiteral(_)
77                    | Expr::NumberLiteral(_)
78                    | Expr::BooleanLiteral(_)
79                    | Expr::NoneLiteral(_)
80                    | Expr::EllipsisLiteral(_)
81                    | Expr::FString(_)
82                    | Expr::List(_)
83                    | Expr::Tuple(_)
84                    | Expr::Set(_)
85                    | Expr::Dict(_)
86                    | Expr::ListComp(_)
87                    | Expr::SetComp(_)
88                    | Expr::DictComp(_)
89            ) {
90                return true;
91            }
92            if !matches!(
93                right.as_ref(),
94                Expr::StringLiteral(_)
95                    | Expr::BytesLiteral(_)
96                    | Expr::NumberLiteral(_)
97                    | Expr::BooleanLiteral(_)
98                    | Expr::NoneLiteral(_)
99                    | Expr::EllipsisLiteral(_)
100                    | Expr::FString(_)
101                    | Expr::List(_)
102                    | Expr::Tuple(_)
103                    | Expr::Set(_)
104                    | Expr::Dict(_)
105                    | Expr::ListComp(_)
106                    | Expr::SetComp(_)
107                    | Expr::DictComp(_)
108            ) {
109                return true;
110            }
111            return false;
112        }
113
114        // Otherwise, avoid all complex expressions.
115        matches!(
116            expr,
117            Expr::Await(_)
118                | Expr::Call(_)
119                | Expr::DictComp(_)
120                | Expr::Generator(_)
121                | Expr::ListComp(_)
122                | Expr::SetComp(_)
123                | Expr::Subscript(_)
124                | Expr::Yield(_)
125                | Expr::YieldFrom(_)
126                | Expr::IpyEscapeCommand(_)
127        )
128    })
129}
130
131/// Call `func` over every `Expr` in `expr`, returning `true` if any expression
132/// returns `true`..
133pub fn any_over_expr(expr: &Expr, func: &dyn Fn(&Expr) -> bool) -> bool {
134    if func(expr) {
135        return true;
136    }
137    match expr {
138        Expr::BoolOp(ast::ExprBoolOp { values, .. }) => {
139            values.iter().any(|expr| any_over_expr(expr, func))
140        }
141        Expr::FString(ast::ExprFString { value, .. }) => value
142            .elements()
143            .any(|expr| any_over_interpolated_string_element(expr, func)),
144        Expr::TString(ast::ExprTString { value, .. }) => value
145            .elements()
146            .any(|expr| any_over_interpolated_string_element(expr, func)),
147        Expr::Named(ast::ExprNamed {
148            target,
149            value,
150            range: _,
151            node_index: _,
152        }) => any_over_expr(target, func) || any_over_expr(value, func),
153        Expr::BinOp(ast::ExprBinOp { left, right, .. }) => {
154            any_over_expr(left, func) || any_over_expr(right, func)
155        }
156        Expr::UnaryOp(ast::ExprUnaryOp { operand, .. }) => any_over_expr(operand, func),
157        Expr::Lambda(ast::ExprLambda { body, .. }) => any_over_expr(body, func),
158        Expr::If(ast::ExprIf {
159            test,
160            body,
161            orelse,
162            range: _,
163            node_index: _,
164        }) => any_over_expr(test, func) || any_over_expr(body, func) || any_over_expr(orelse, func),
165        Expr::Dict(ast::ExprDict {
166            items,
167            range: _,
168            node_index: _,
169        }) => items.iter().any(|ast::DictItem { key, value }| {
170            any_over_expr(value, func) || key.as_ref().is_some_and(|key| any_over_expr(key, func))
171        }),
172        Expr::Set(ast::ExprSet {
173            elts,
174            range: _,
175            node_index: _,
176        })
177        | Expr::List(ast::ExprList {
178            elts,
179            range: _,
180            node_index: _,
181            ..
182        })
183        | Expr::Tuple(ast::ExprTuple {
184            elts,
185            range: _,
186            node_index: _,
187            ..
188        }) => elts.iter().any(|expr| any_over_expr(expr, func)),
189        Expr::ListComp(ast::ExprListComp {
190            elt,
191            generators,
192            range: _,
193            node_index: _,
194        })
195        | Expr::SetComp(ast::ExprSetComp {
196            elt,
197            generators,
198            range: _,
199            node_index: _,
200        })
201        | Expr::Generator(ast::ExprGenerator {
202            elt,
203            generators,
204            range: _,
205            node_index: _,
206            parenthesized: _,
207        }) => {
208            any_over_expr(elt, func)
209                || generators.iter().any(|generator| {
210                    any_over_expr(&generator.target, func)
211                        || any_over_expr(&generator.iter, func)
212                        || generator.ifs.iter().any(|expr| any_over_expr(expr, func))
213                })
214        }
215        Expr::DictComp(ast::ExprDictComp {
216            key,
217            value,
218            generators,
219            range: _,
220            node_index: _,
221        }) => {
222            any_over_expr(key, func)
223                || any_over_expr(value, func)
224                || generators.iter().any(|generator| {
225                    any_over_expr(&generator.target, func)
226                        || any_over_expr(&generator.iter, func)
227                        || generator.ifs.iter().any(|expr| any_over_expr(expr, func))
228                })
229        }
230        Expr::Await(ast::ExprAwait {
231            value,
232            range: _,
233            node_index: _,
234        })
235        | Expr::YieldFrom(ast::ExprYieldFrom {
236            value,
237            range: _,
238            node_index: _,
239        })
240        | Expr::Attribute(ast::ExprAttribute {
241            value,
242            range: _,
243            node_index: _,
244            ..
245        })
246        | Expr::Starred(ast::ExprStarred {
247            value,
248            range: _,
249            node_index: _,
250            ..
251        }) => any_over_expr(value, func),
252        Expr::Yield(ast::ExprYield {
253            value,
254            range: _,
255            node_index: _,
256        }) => value
257            .as_ref()
258            .is_some_and(|value| any_over_expr(value, func)),
259        Expr::Compare(ast::ExprCompare {
260            left, comparators, ..
261        }) => any_over_expr(left, func) || comparators.iter().any(|expr| any_over_expr(expr, func)),
262        Expr::Call(ast::ExprCall {
263            func: call_func,
264            arguments,
265            range: _,
266            node_index: _,
267        }) => {
268            any_over_expr(call_func, func)
269                // Note that this is the evaluation order but not necessarily the declaration order
270                // (e.g. for `f(*args, a=2, *args2, **kwargs)` it's not)
271                || arguments.args.iter().any(|expr| any_over_expr(expr, func))
272                || arguments.keywords
273                    .iter()
274                    .any(|keyword| any_over_expr(&keyword.value, func))
275        }
276        Expr::Subscript(ast::ExprSubscript { value, slice, .. }) => {
277            any_over_expr(value, func) || any_over_expr(slice, func)
278        }
279        Expr::Slice(ast::ExprSlice {
280            lower,
281            upper,
282            step,
283            range: _,
284            node_index: _,
285        }) => {
286            lower
287                .as_ref()
288                .is_some_and(|value| any_over_expr(value, func))
289                || upper
290                    .as_ref()
291                    .is_some_and(|value| any_over_expr(value, func))
292                || step
293                    .as_ref()
294                    .is_some_and(|value| any_over_expr(value, func))
295        }
296        Expr::Name(_)
297        | Expr::StringLiteral(_)
298        | Expr::BytesLiteral(_)
299        | Expr::NumberLiteral(_)
300        | Expr::BooleanLiteral(_)
301        | Expr::NoneLiteral(_)
302        | Expr::EllipsisLiteral(_)
303        | Expr::IpyEscapeCommand(_) => false,
304    }
305}
306
307pub fn any_over_type_param(type_param: &TypeParam, func: &dyn Fn(&Expr) -> bool) -> bool {
308    match type_param {
309        TypeParam::TypeVar(ast::TypeParamTypeVar { bound, default, .. }) => {
310            bound
311                .as_ref()
312                .is_some_and(|value| any_over_expr(value, func))
313                || default
314                    .as_ref()
315                    .is_some_and(|value| any_over_expr(value, func))
316        }
317        TypeParam::TypeVarTuple(ast::TypeParamTypeVarTuple { default, .. }) => default
318            .as_ref()
319            .is_some_and(|value| any_over_expr(value, func)),
320        TypeParam::ParamSpec(ast::TypeParamParamSpec { default, .. }) => default
321            .as_ref()
322            .is_some_and(|value| any_over_expr(value, func)),
323    }
324}
325
326pub fn any_over_pattern(pattern: &Pattern, func: &dyn Fn(&Expr) -> bool) -> bool {
327    match pattern {
328        Pattern::MatchValue(ast::PatternMatchValue {
329            value,
330            range: _,
331            node_index: _,
332        }) => any_over_expr(value, func),
333        Pattern::MatchSingleton(_) => false,
334        Pattern::MatchSequence(ast::PatternMatchSequence {
335            patterns,
336            range: _,
337            node_index: _,
338        }) => patterns
339            .iter()
340            .any(|pattern| any_over_pattern(pattern, func)),
341        Pattern::MatchMapping(ast::PatternMatchMapping { keys, patterns, .. }) => {
342            keys.iter().any(|key| any_over_expr(key, func))
343                || patterns
344                    .iter()
345                    .any(|pattern| any_over_pattern(pattern, func))
346        }
347        Pattern::MatchClass(ast::PatternMatchClass { cls, arguments, .. }) => {
348            any_over_expr(cls, func)
349                || arguments
350                    .patterns
351                    .iter()
352                    .any(|pattern| any_over_pattern(pattern, func))
353                || arguments
354                    .keywords
355                    .iter()
356                    .any(|keyword| any_over_pattern(&keyword.pattern, func))
357        }
358        Pattern::MatchStar(_) => false,
359        Pattern::MatchAs(ast::PatternMatchAs { pattern, .. }) => pattern
360            .as_ref()
361            .is_some_and(|pattern| any_over_pattern(pattern, func)),
362        Pattern::MatchOr(ast::PatternMatchOr {
363            patterns,
364            range: _,
365            node_index: _,
366        }) => patterns
367            .iter()
368            .any(|pattern| any_over_pattern(pattern, func)),
369    }
370}
371
372pub fn any_over_interpolated_string_element(
373    element: &ast::InterpolatedStringElement,
374    func: &dyn Fn(&Expr) -> bool,
375) -> bool {
376    match element {
377        ast::InterpolatedStringElement::Literal(_) => false,
378        ast::InterpolatedStringElement::Interpolation(ast::InterpolatedElement {
379            expression,
380            format_spec,
381            ..
382        }) => {
383            any_over_expr(expression, func)
384                || format_spec.as_ref().is_some_and(|spec| {
385                    spec.elements.iter().any(|spec_element| {
386                        any_over_interpolated_string_element(spec_element, func)
387                    })
388                })
389        }
390    }
391}
392
393pub fn any_over_stmt(stmt: &Stmt, func: &dyn Fn(&Expr) -> bool) -> bool {
394    match stmt {
395        Stmt::FunctionDef(ast::StmtFunctionDef {
396            parameters,
397            type_params,
398            body,
399            decorator_list,
400            returns,
401            ..
402        }) => {
403            parameters.iter().any(|param| {
404                param
405                    .default()
406                    .is_some_and(|default| any_over_expr(default, func))
407                    || param
408                        .annotation()
409                        .is_some_and(|annotation| any_over_expr(annotation, func))
410            }) || type_params.as_ref().is_some_and(|type_params| {
411                type_params
412                    .iter()
413                    .any(|type_param| any_over_type_param(type_param, func))
414            }) || body.iter().any(|stmt| any_over_stmt(stmt, func))
415                || decorator_list
416                    .iter()
417                    .any(|decorator| any_over_expr(&decorator.expression, func))
418                || returns
419                    .as_ref()
420                    .is_some_and(|value| any_over_expr(value, func))
421        }
422        Stmt::ClassDef(ast::StmtClassDef {
423            arguments,
424            type_params,
425            body,
426            decorator_list,
427            ..
428        }) => {
429            // Note that e.g. `class A(*args, a=2, *args2, **kwargs): pass` is a valid class
430            // definition
431            arguments
432                .as_deref()
433                .is_some_and(|Arguments { args, keywords, .. }| {
434                    args.iter().any(|expr| any_over_expr(expr, func))
435                        || keywords
436                            .iter()
437                            .any(|keyword| any_over_expr(&keyword.value, func))
438                })
439                || type_params.as_ref().is_some_and(|type_params| {
440                    type_params
441                        .iter()
442                        .any(|type_param| any_over_type_param(type_param, func))
443                })
444                || body.iter().any(|stmt| any_over_stmt(stmt, func))
445                || decorator_list
446                    .iter()
447                    .any(|decorator| any_over_expr(&decorator.expression, func))
448        }
449        Stmt::Return(ast::StmtReturn {
450            value,
451            range: _,
452            node_index: _,
453        }) => value
454            .as_ref()
455            .is_some_and(|value| any_over_expr(value, func)),
456        Stmt::Delete(ast::StmtDelete {
457            targets,
458            range: _,
459            node_index: _,
460        }) => targets.iter().any(|expr| any_over_expr(expr, func)),
461        Stmt::TypeAlias(ast::StmtTypeAlias {
462            name,
463            type_params,
464            value,
465            ..
466        }) => {
467            any_over_expr(name, func)
468                || type_params.as_ref().is_some_and(|type_params| {
469                    type_params
470                        .iter()
471                        .any(|type_param| any_over_type_param(type_param, func))
472                })
473                || any_over_expr(value, func)
474        }
475        Stmt::Assign(ast::StmtAssign { targets, value, .. }) => {
476            targets.iter().any(|expr| any_over_expr(expr, func)) || any_over_expr(value, func)
477        }
478        Stmt::AugAssign(ast::StmtAugAssign { target, value, .. }) => {
479            any_over_expr(target, func) || any_over_expr(value, func)
480        }
481        Stmt::AnnAssign(ast::StmtAnnAssign {
482            target,
483            annotation,
484            value,
485            ..
486        }) => {
487            any_over_expr(target, func)
488                || any_over_expr(annotation, func)
489                || value
490                    .as_ref()
491                    .is_some_and(|value| any_over_expr(value, func))
492        }
493        Stmt::For(ast::StmtFor {
494            target,
495            iter,
496            body,
497            orelse,
498            ..
499        }) => {
500            any_over_expr(target, func)
501                || any_over_expr(iter, func)
502                || any_over_body(body, func)
503                || any_over_body(orelse, func)
504        }
505        Stmt::While(ast::StmtWhile {
506            test,
507            body,
508            orelse,
509            range: _,
510            node_index: _,
511        }) => any_over_expr(test, func) || any_over_body(body, func) || any_over_body(orelse, func),
512        Stmt::If(ast::StmtIf {
513            test,
514            body,
515            elif_else_clauses,
516            range: _,
517            node_index: _,
518        }) => {
519            any_over_expr(test, func)
520                || any_over_body(body, func)
521                || elif_else_clauses.iter().any(|clause| {
522                    clause
523                        .test
524                        .as_ref()
525                        .is_some_and(|test| any_over_expr(test, func))
526                        || any_over_body(&clause.body, func)
527                })
528        }
529        Stmt::With(ast::StmtWith { items, body, .. }) => {
530            items.iter().any(|with_item| {
531                any_over_expr(&with_item.context_expr, func)
532                    || with_item
533                        .optional_vars
534                        .as_ref()
535                        .is_some_and(|expr| any_over_expr(expr, func))
536            }) || any_over_body(body, func)
537        }
538        Stmt::Raise(ast::StmtRaise {
539            exc,
540            cause,
541            range: _,
542            node_index: _,
543        }) => {
544            exc.as_ref().is_some_and(|value| any_over_expr(value, func))
545                || cause
546                    .as_ref()
547                    .is_some_and(|value| any_over_expr(value, func))
548        }
549        Stmt::Try(ast::StmtTry {
550            body,
551            handlers,
552            orelse,
553            finalbody,
554            is_star: _,
555            range: _,
556            node_index: _,
557        }) => {
558            any_over_body(body, func)
559                || handlers.iter().any(|handler| {
560                    let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler {
561                        type_,
562                        body,
563                        ..
564                    }) = handler;
565                    type_.as_ref().is_some_and(|expr| any_over_expr(expr, func))
566                        || any_over_body(body, func)
567                })
568                || any_over_body(orelse, func)
569                || any_over_body(finalbody, func)
570        }
571        Stmt::Assert(ast::StmtAssert {
572            test,
573            msg,
574            range: _,
575            node_index: _,
576        }) => {
577            any_over_expr(test, func)
578                || msg.as_ref().is_some_and(|value| any_over_expr(value, func))
579        }
580        Stmt::Match(ast::StmtMatch {
581            subject,
582            cases,
583            range: _,
584            node_index: _,
585        }) => {
586            any_over_expr(subject, func)
587                || cases.iter().any(|case| {
588                    let MatchCase {
589                        pattern,
590                        guard,
591                        body,
592                        range: _,
593                        node_index: _,
594                    } = case;
595                    any_over_pattern(pattern, func)
596                        || guard.as_ref().is_some_and(|expr| any_over_expr(expr, func))
597                        || any_over_body(body, func)
598                })
599        }
600        Stmt::Import(_) => false,
601        Stmt::ImportFrom(_) => false,
602        Stmt::Global(_) => false,
603        Stmt::Nonlocal(_) => false,
604        Stmt::Expr(ast::StmtExpr {
605            value,
606            range: _,
607            node_index: _,
608        }) => any_over_expr(value, func),
609        Stmt::Pass(_) | Stmt::Break(_) | Stmt::Continue(_) => false,
610        Stmt::IpyEscapeCommand(_) => false,
611    }
612}
613
614pub fn any_over_body(body: &[Stmt], func: &dyn Fn(&Expr) -> bool) -> bool {
615    body.iter().any(|stmt| any_over_stmt(stmt, func))
616}
617
618pub fn is_dunder(id: &str) -> bool {
619    id.starts_with("__") && id.ends_with("__")
620}
621
622/// Whether a name starts and ends with a single underscore.
623///
624/// `_a__` is considered neither a dunder nor a sunder name.
625pub fn is_sunder(id: &str) -> bool {
626    id.starts_with('_') && id.ends_with('_') && !id.starts_with("__") && !id.ends_with("__")
627}
628
629/// Return `true` if the [`Stmt`] is an assignment to a dunder (like `__all__`).
630pub fn is_assignment_to_a_dunder(stmt: &Stmt) -> bool {
631    // Check whether it's an assignment to a dunder, with or without a type
632    // annotation. This is what pycodestyle (as of 2.9.1) does.
633    match stmt {
634        Stmt::Assign(ast::StmtAssign { targets, .. }) => {
635            if let [Expr::Name(ast::ExprName { id, .. })] = targets.as_slice() {
636                is_dunder(id)
637            } else {
638                false
639            }
640        }
641        Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) => {
642            if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() {
643                is_dunder(id)
644            } else {
645                false
646            }
647        }
648        _ => false,
649    }
650}
651
652/// Return `true` if the [`Expr`] is a singleton (`None`, `True`, `False`, or
653/// `...`).
654pub const fn is_singleton(expr: &Expr) -> bool {
655    matches!(
656        expr,
657        Expr::NoneLiteral(_) | Expr::BooleanLiteral(_) | Expr::EllipsisLiteral(_)
658    )
659}
660
661/// Return `true` if the [`Expr`] is a literal or tuple of literals.
662pub fn is_constant(expr: &Expr) -> bool {
663    if let Expr::Tuple(tuple) = expr {
664        tuple.iter().all(is_constant)
665    } else {
666        expr.is_literal_expr()
667    }
668}
669
670/// Return `true` if the [`Expr`] is a non-singleton constant.
671pub fn is_constant_non_singleton(expr: &Expr) -> bool {
672    is_constant(expr) && !is_singleton(expr)
673}
674
675/// Return `true` if an [`Expr`] is a literal `True`.
676pub const fn is_const_true(expr: &Expr) -> bool {
677    matches!(
678        expr,
679        Expr::BooleanLiteral(ast::ExprBooleanLiteral { value: true, .. }),
680    )
681}
682
683/// Return `true` if an [`Expr`] is a literal `False`.
684pub const fn is_const_false(expr: &Expr) -> bool {
685    matches!(
686        expr,
687        Expr::BooleanLiteral(ast::ExprBooleanLiteral { value: false, .. }),
688    )
689}
690
691/// Return `true` if the [`Expr`] is a mutable iterable initializer, like `{}` or `[]`.
692pub const fn is_mutable_iterable_initializer(expr: &Expr) -> bool {
693    matches!(
694        expr,
695        Expr::Set(_)
696            | Expr::SetComp(_)
697            | Expr::List(_)
698            | Expr::ListComp(_)
699            | Expr::Dict(_)
700            | Expr::DictComp(_)
701    )
702}
703
704/// Extract the names of all handled exceptions.
705pub fn extract_handled_exceptions(handlers: &[ExceptHandler]) -> Vec<&Expr> {
706    let mut handled_exceptions = Vec::new();
707    for handler in handlers {
708        match handler {
709            ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { type_, .. }) => {
710                if let Some(type_) = type_ {
711                    if let Expr::Tuple(tuple) = &**type_ {
712                        for type_ in tuple {
713                            handled_exceptions.push(type_);
714                        }
715                    } else {
716                        handled_exceptions.push(type_);
717                    }
718                }
719            }
720        }
721    }
722    handled_exceptions
723}
724
725/// Given an [`Expr`] that can be callable or not (like a decorator, which could
726/// be used with or without explicit call syntax), return the underlying
727/// callable.
728pub fn map_callable(decorator: &Expr) -> &Expr {
729    if let Expr::Call(ast::ExprCall { func, .. }) = decorator {
730        // Ex) `@decorator()`
731        func
732    } else {
733        // Ex) `@decorator`
734        decorator
735    }
736}
737
738/// Given an [`Expr`] that can be a [`ExprSubscript`][ast::ExprSubscript] or not
739/// (like an annotation that may be generic or not), return the underlying expr.
740pub fn map_subscript(expr: &Expr) -> &Expr {
741    if let Expr::Subscript(ast::ExprSubscript { value, .. }) = expr {
742        // Ex) `Iterable[T]`  => return `Iterable`
743        value
744    } else {
745        // Ex) `Iterable`  => return `Iterable`
746        expr
747    }
748}
749
750/// Given an [`Expr`] that can be starred, return the underlying starred expression.
751pub fn map_starred(expr: &Expr) -> &Expr {
752    if let Expr::Starred(ast::ExprStarred { value, .. }) = expr {
753        // Ex) `*args`
754        value
755    } else {
756        // Ex) `args`
757        expr
758    }
759}
760
761/// Return `true` if the body uses `locals()`, `globals()`, `vars()`, `eval()`.
762///
763/// Accepts a closure that determines whether a given name (e.g., `"list"`) is a Python builtin.
764pub fn uses_magic_variable_access<F>(body: &[Stmt], is_builtin: F) -> bool
765where
766    F: Fn(&str) -> bool,
767{
768    any_over_body(body, &|expr| {
769        if let Expr::Call(ast::ExprCall { func, .. }) = expr {
770            if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
771                if matches!(id.as_str(), "locals" | "globals" | "vars" | "exec" | "eval") {
772                    if is_builtin(id.as_str()) {
773                        return true;
774                    }
775                }
776            }
777        }
778        false
779    })
780}
781
782/// Format the module reference name for a relative import.
783///
784/// # Examples
785///
786/// ```rust
787/// # use ruff_python_ast::helpers::format_import_from;
788///
789/// assert_eq!(format_import_from(0, None), "".to_string());
790/// assert_eq!(format_import_from(1, None), ".".to_string());
791/// assert_eq!(format_import_from(1, Some("foo")), ".foo".to_string());
792/// ```
793pub fn format_import_from(level: u32, module: Option<&str>) -> Cow<'_, str> {
794    match (level, module) {
795        (0, Some(module)) => Cow::Borrowed(module),
796        (level, module) => {
797            let mut module_name =
798                String::with_capacity((level as usize) + module.map_or(0, str::len));
799            for _ in 0..level {
800                module_name.push('.');
801            }
802            if let Some(module) = module {
803                module_name.push_str(module);
804            }
805            Cow::Owned(module_name)
806        }
807    }
808}
809
810/// Format the member reference name for a relative import.
811///
812/// # Examples
813///
814/// ```rust
815/// # use ruff_python_ast::helpers::format_import_from_member;
816///
817/// assert_eq!(format_import_from_member(0, None, "bar"), "bar".to_string());
818/// assert_eq!(format_import_from_member(1, None, "bar"), ".bar".to_string());
819/// assert_eq!(format_import_from_member(1, Some("foo"), "bar"), ".foo.bar".to_string());
820/// ```
821pub fn format_import_from_member(level: u32, module: Option<&str>, member: &str) -> String {
822    let mut qualified_name =
823        String::with_capacity((level as usize) + module.map_or(0, str::len) + 1 + member.len());
824    if level > 0 {
825        for _ in 0..level {
826            qualified_name.push('.');
827        }
828    }
829    if let Some(module) = module {
830        qualified_name.push_str(module);
831        qualified_name.push('.');
832    }
833    qualified_name.push_str(member);
834    qualified_name
835}
836
837/// Create a module path from a (package, path) pair.
838///
839/// For example, if the package is `foo/bar` and the path is `foo/bar/baz.py`,
840/// the call path is `["baz"]`.
841pub fn to_module_path(package: &Path, path: &Path) -> Option<Vec<String>> {
842    path.strip_prefix(package.parent()?)
843        .ok()?
844        .iter()
845        .map(Path::new)
846        .map(Path::file_stem)
847        .map(|path| path.and_then(|path| path.to_os_string().into_string().ok()))
848        .collect::<Option<Vec<String>>>()
849}
850
851/// Format the call path for a relative import.
852///
853/// # Examples
854///
855/// ```rust
856/// # use ruff_python_ast::helpers::collect_import_from_member;
857///
858/// assert_eq!(collect_import_from_member(0, None, "bar").segments(), ["bar"]);
859/// assert_eq!(collect_import_from_member(1, None, "bar").segments(), [".", "bar"]);
860/// assert_eq!(collect_import_from_member(1, Some("foo"), "bar").segments(), [".", "foo", "bar"]);
861/// ```
862pub fn collect_import_from_member<'a>(
863    level: u32,
864    module: Option<&'a str>,
865    member: &'a str,
866) -> QualifiedName<'a> {
867    let mut qualified_name_builder = QualifiedNameBuilder::with_capacity(
868        level as usize
869            + module
870                .map(|module| module.split('.').count())
871                .unwrap_or_default()
872            + 1,
873    );
874
875    // Include the dots as standalone segments.
876    if level > 0 {
877        for _ in 0..level {
878            qualified_name_builder.push(".");
879        }
880    }
881
882    // Add the remaining segments.
883    if let Some(module) = module {
884        qualified_name_builder.extend(module.split('.'));
885    }
886
887    // Add the member.
888    qualified_name_builder.push(member);
889
890    qualified_name_builder.build()
891}
892
893/// Format the call path for a relative import, or `None` if the relative import extends beyond
894/// the root module.
895pub fn from_relative_import<'a>(
896    // The path from which the import is relative.
897    module: &'a [String],
898    // The path of the import itself (e.g., given `from ..foo import bar`, `[".", ".", "foo", "bar]`).
899    import: &[&'a str],
900    // The remaining segments to the call path (e.g., given `bar.baz`, `["baz"]`).
901    tail: &[&'a str],
902) -> Option<QualifiedName<'a>> {
903    let mut qualified_name_builder =
904        QualifiedNameBuilder::with_capacity(module.len() + import.len() + tail.len());
905
906    // Start with the module path.
907    qualified_name_builder.extend(module.iter().map(String::as_str));
908
909    // Remove segments based on the number of dots.
910    for segment in import {
911        if *segment == "." {
912            if qualified_name_builder.is_empty() {
913                return None;
914            }
915            qualified_name_builder.pop();
916        } else {
917            qualified_name_builder.push(segment);
918        }
919    }
920
921    // Add the remaining segments.
922    qualified_name_builder.extend_from_slice(tail);
923
924    Some(qualified_name_builder.build())
925}
926
927/// Given an imported module (based on its relative import level and module name), return the
928/// fully-qualified module path.
929pub fn resolve_imported_module_path<'a>(
930    level: u32,
931    module: Option<&'a str>,
932    module_path: Option<&[String]>,
933) -> Option<Cow<'a, str>> {
934    if level == 0 {
935        return Some(Cow::Borrowed(module.unwrap_or("")));
936    }
937
938    let module_path = module_path?;
939
940    if level as usize >= module_path.len() {
941        return None;
942    }
943
944    let mut qualified_path = module_path[..module_path.len() - level as usize].join(".");
945    if let Some(module) = module {
946        if !qualified_path.is_empty() {
947            qualified_path.push('.');
948        }
949        qualified_path.push_str(module);
950    }
951    Some(Cow::Owned(qualified_path))
952}
953
954/// A [`Visitor`] to collect all [`Expr::Name`] nodes in an AST.
955#[derive(Debug, Default)]
956pub struct NameFinder<'a> {
957    /// A map from identifier to defining expression.
958    pub names: FxHashMap<&'a str, &'a ast::ExprName>,
959}
960
961impl<'a> Visitor<'a> for NameFinder<'a> {
962    fn visit_expr(&mut self, expr: &'a Expr) {
963        if let Expr::Name(name) = expr {
964            self.names.insert(&name.id, name);
965        }
966        crate::visitor::walk_expr(self, expr);
967    }
968}
969
970/// A [`Visitor`] to collect all stored [`Expr::Name`] nodes in an AST.
971#[derive(Debug, Default)]
972pub struct StoredNameFinder<'a> {
973    /// A map from identifier to defining expression.
974    pub names: FxHashMap<&'a str, &'a ast::ExprName>,
975}
976
977impl<'a> Visitor<'a> for StoredNameFinder<'a> {
978    fn visit_expr(&mut self, expr: &'a Expr) {
979        if let Expr::Name(name) = expr {
980            if name.ctx.is_store() {
981                self.names.insert(&name.id, name);
982            }
983        }
984        crate::visitor::walk_expr(self, expr);
985    }
986}
987
988/// A [`Visitor`] that collects all `return` statements in a function or method.
989#[derive(Default)]
990pub struct ReturnStatementVisitor<'a> {
991    pub returns: Vec<&'a ast::StmtReturn>,
992    pub is_generator: bool,
993}
994
995impl<'a> Visitor<'a> for ReturnStatementVisitor<'a> {
996    fn visit_stmt(&mut self, stmt: &'a Stmt) {
997        match stmt {
998            Stmt::FunctionDef(_) | Stmt::ClassDef(_) => {
999                // Don't recurse.
1000            }
1001            Stmt::Return(stmt) => self.returns.push(stmt),
1002            _ => crate::visitor::walk_stmt(self, stmt),
1003        }
1004    }
1005
1006    fn visit_expr(&mut self, expr: &'a Expr) {
1007        if let Expr::Yield(_) | Expr::YieldFrom(_) = expr {
1008            self.is_generator = true;
1009        } else {
1010            crate::visitor::walk_expr(self, expr);
1011        }
1012    }
1013}
1014
1015/// A [`StatementVisitor`] that collects all `raise` statements in a function or method.
1016#[derive(Default)]
1017pub struct RaiseStatementVisitor<'a> {
1018    pub raises: Vec<(TextRange, Option<&'a Expr>, Option<&'a Expr>)>,
1019}
1020
1021impl<'a> StatementVisitor<'a> for RaiseStatementVisitor<'a> {
1022    fn visit_stmt(&mut self, stmt: &'a Stmt) {
1023        match stmt {
1024            Stmt::Raise(ast::StmtRaise {
1025                exc,
1026                cause,
1027                range: _,
1028                node_index: _,
1029            }) => {
1030                self.raises
1031                    .push((stmt.range(), exc.as_deref(), cause.as_deref()));
1032            }
1033            Stmt::ClassDef(_) | Stmt::FunctionDef(_) | Stmt::Try(_) => {}
1034            Stmt::If(ast::StmtIf {
1035                body,
1036                elif_else_clauses,
1037                ..
1038            }) => {
1039                crate::statement_visitor::walk_body(self, body);
1040                for clause in elif_else_clauses {
1041                    self.visit_elif_else_clause(clause);
1042                }
1043            }
1044            Stmt::While(ast::StmtWhile { body, .. })
1045            | Stmt::With(ast::StmtWith { body, .. })
1046            | Stmt::For(ast::StmtFor { body, .. }) => {
1047                crate::statement_visitor::walk_body(self, body);
1048            }
1049            Stmt::Match(ast::StmtMatch { cases, .. }) => {
1050                for case in cases {
1051                    crate::statement_visitor::walk_body(self, &case.body);
1052                }
1053            }
1054            _ => {}
1055        }
1056    }
1057}
1058
1059/// A [`Visitor`] that detects the presence of `await` expressions in the current scope.
1060#[derive(Debug, Default)]
1061pub struct AwaitVisitor {
1062    pub seen_await: bool,
1063}
1064
1065impl Visitor<'_> for AwaitVisitor {
1066    fn visit_stmt(&mut self, stmt: &Stmt) {
1067        match stmt {
1068            Stmt::FunctionDef(_) | Stmt::ClassDef(_) => (),
1069            Stmt::With(ast::StmtWith { is_async: true, .. }) => {
1070                self.seen_await = true;
1071            }
1072            Stmt::For(ast::StmtFor { is_async: true, .. }) => {
1073                self.seen_await = true;
1074            }
1075            _ => crate::visitor::walk_stmt(self, stmt),
1076        }
1077    }
1078
1079    fn visit_expr(&mut self, expr: &Expr) {
1080        if let Expr::Await(ast::ExprAwait { .. }) = expr {
1081            self.seen_await = true;
1082        } else {
1083            crate::visitor::walk_expr(self, expr);
1084        }
1085    }
1086
1087    fn visit_comprehension(&mut self, comprehension: &'_ crate::Comprehension) {
1088        if comprehension.is_async {
1089            self.seen_await = true;
1090        } else {
1091            crate::visitor::walk_comprehension(self, comprehension);
1092        }
1093    }
1094}
1095
1096/// Return `true` if a `Stmt` is a docstring.
1097pub fn is_docstring_stmt(stmt: &Stmt) -> bool {
1098    if let Stmt::Expr(ast::StmtExpr {
1099        value,
1100        range: _,
1101        node_index: _,
1102    }) = stmt
1103    {
1104        value.is_string_literal_expr()
1105    } else {
1106        false
1107    }
1108}
1109
1110/// Check if a node is part of a conditional branch.
1111pub fn on_conditional_branch<'a>(parents: &mut impl Iterator<Item = &'a Stmt>) -> bool {
1112    parents.any(|parent| {
1113        if matches!(parent, Stmt::If(_) | Stmt::While(_) | Stmt::Match(_)) {
1114            return true;
1115        }
1116        if let Stmt::Expr(ast::StmtExpr {
1117            value,
1118            range: _,
1119            node_index: _,
1120        }) = parent
1121        {
1122            if value.is_if_expr() {
1123                return true;
1124            }
1125        }
1126        false
1127    })
1128}
1129
1130/// Check if a node is in a nested block.
1131pub fn in_nested_block<'a>(mut parents: impl Iterator<Item = &'a Stmt>) -> bool {
1132    parents.any(|parent| {
1133        matches!(
1134            parent,
1135            Stmt::Try(_) | Stmt::If(_) | Stmt::With(_) | Stmt::Match(_)
1136        )
1137    })
1138}
1139
1140/// Check if a node represents an unpacking assignment.
1141pub fn is_unpacking_assignment(parent: &Stmt, child: &Expr) -> bool {
1142    match parent {
1143        Stmt::With(ast::StmtWith { items, .. }) => items.iter().any(|item| {
1144            if let Some(optional_vars) = &item.optional_vars {
1145                if optional_vars.is_tuple_expr() {
1146                    if any_over_expr(optional_vars, &|expr| expr == child) {
1147                        return true;
1148                    }
1149                }
1150            }
1151            false
1152        }),
1153        Stmt::Assign(ast::StmtAssign { targets, value, .. }) => {
1154            // In `(a, b) = (1, 2)`, `(1, 2)` is the target, and it is a tuple.
1155            let value_is_tuple = matches!(
1156                value.as_ref(),
1157                Expr::Set(_) | Expr::List(_) | Expr::Tuple(_)
1158            );
1159            // In `(a, b) = coords = (1, 2)`, `(a, b)` and `coords` are the targets, and
1160            // `(a, b)` is a tuple. (We use "tuple" as a placeholder for any
1161            // unpackable expression.)
1162            let targets_are_tuples = targets
1163                .iter()
1164                .all(|item| matches!(item, Expr::Set(_) | Expr::List(_) | Expr::Tuple(_)));
1165            // If we're looking at `a` in `(a, b) = coords = (1, 2)`, then we should
1166            // identify that the current expression is in a tuple.
1167            let child_in_tuple = targets_are_tuples
1168                || targets.iter().any(|item| {
1169                    matches!(item, Expr::Set(_) | Expr::List(_) | Expr::Tuple(_))
1170                        && any_over_expr(item, &|expr| expr == child)
1171                });
1172
1173            // If our child is a tuple, and value is not, it's always an unpacking
1174            // expression. Ex) `x, y = tup`
1175            if child_in_tuple && !value_is_tuple {
1176                return true;
1177            }
1178
1179            // If our child isn't a tuple, but value is, it's never an unpacking expression.
1180            // Ex) `coords = (1, 2)`
1181            if !child_in_tuple && value_is_tuple {
1182                return false;
1183            }
1184
1185            // If our target and the value are both tuples, then it's an unpacking
1186            // expression assuming there's at least one non-tuple child.
1187            // Ex) Given `(x, y) = coords = 1, 2`, `(x, y)` is considered an unpacking
1188            // expression. Ex) Given `(x, y) = (a, b) = 1, 2`, `(x, y)` isn't
1189            // considered an unpacking expression.
1190            if child_in_tuple && value_is_tuple {
1191                return !targets_are_tuples;
1192            }
1193
1194            false
1195        }
1196        _ => false,
1197    }
1198}
1199
1200#[derive(Copy, Clone, Debug, PartialEq, is_macro::Is)]
1201pub enum Truthiness {
1202    /// The expression is `True`.
1203    True,
1204    /// The expression is `False`.
1205    False,
1206    /// The expression evaluates to a `False`-like value (e.g., `None`, `0`, `[]`, `""`).
1207    Falsey,
1208    /// The expression evaluates to a `True`-like value (e.g., `1`, `"foo"`).
1209    Truthy,
1210    /// The expression evaluates to `None`.
1211    None,
1212    /// The expression evaluates to an unknown value (e.g., a variable `x` of unknown type).
1213    Unknown,
1214}
1215
1216impl Truthiness {
1217    /// Return the truthiness of an expression.
1218    pub fn from_expr<F>(expr: &Expr, is_builtin: F) -> Self
1219    where
1220        F: Fn(&str) -> bool,
1221    {
1222        match expr {
1223            Expr::Lambda(_) => Self::Truthy,
1224            Expr::Generator(_) => Self::Truthy,
1225            Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
1226                if value.is_empty() {
1227                    Self::Falsey
1228                } else {
1229                    Self::Truthy
1230                }
1231            }
1232            Expr::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => {
1233                if value.is_empty() {
1234                    Self::Falsey
1235                } else {
1236                    Self::Truthy
1237                }
1238            }
1239            Expr::NumberLiteral(ast::ExprNumberLiteral { value, .. }) => match value {
1240                ast::Number::Int(int) => {
1241                    if *int == 0 {
1242                        Self::Falsey
1243                    } else {
1244                        Self::Truthy
1245                    }
1246                }
1247                ast::Number::Float(float) => {
1248                    if *float == 0.0 {
1249                        Self::Falsey
1250                    } else {
1251                        Self::Truthy
1252                    }
1253                }
1254                ast::Number::Complex { real, imag, .. } => {
1255                    if *real == 0.0 && *imag == 0.0 {
1256                        Self::Falsey
1257                    } else {
1258                        Self::Truthy
1259                    }
1260                }
1261            },
1262            Expr::BooleanLiteral(ast::ExprBooleanLiteral { value, .. }) => {
1263                if *value {
1264                    Self::True
1265                } else {
1266                    Self::False
1267                }
1268            }
1269            Expr::NoneLiteral(_) => Self::None,
1270            Expr::EllipsisLiteral(_) => Self::Truthy,
1271            Expr::FString(f_string) => {
1272                if is_empty_f_string(f_string) {
1273                    Self::Falsey
1274                } else if is_non_empty_f_string(f_string) {
1275                    Self::Truthy
1276                } else {
1277                    Self::Unknown
1278                }
1279            }
1280            Expr::TString(_) => Self::Truthy,
1281            Expr::List(ast::ExprList { elts, .. })
1282            | Expr::Set(ast::ExprSet { elts, .. })
1283            | Expr::Tuple(ast::ExprTuple { elts, .. }) => {
1284                if elts.is_empty() {
1285                    return Self::Falsey;
1286                }
1287
1288                if elts.iter().all(Expr::is_starred_expr) {
1289                    // [*foo] / [*foo, *bar]
1290                    Self::Unknown
1291                } else {
1292                    Self::Truthy
1293                }
1294            }
1295            Expr::Dict(dict) => {
1296                if dict.is_empty() {
1297                    return Self::Falsey;
1298                }
1299
1300                // If the dict consists only of double-starred items (e.g., {**x, **y}),
1301                // consider its truthiness unknown. This matches lists/sets/tuples containing
1302                // only starred elements, which are also Unknown.
1303                if dict
1304                    .items
1305                    .iter()
1306                    .all(|item| matches!(item, DictItem { key: None, .. }))
1307                {
1308                    // {**foo} / {**foo, **bar}
1309                    Self::Unknown
1310                } else {
1311                    Self::Truthy
1312                }
1313            }
1314            Expr::Call(ast::ExprCall {
1315                func, arguments, ..
1316            }) => {
1317                if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
1318                    if is_iterable_initializer(id.as_str(), |id| is_builtin(id)) {
1319                        if arguments.is_empty() {
1320                            // Ex) `list()`
1321                            Self::Falsey
1322                        } else if let [argument] = &*arguments.args
1323                            && arguments.keywords.is_empty()
1324                        {
1325                            // Ex) `list([1, 2, 3])`
1326                            match argument {
1327                                // Return Unknown for types with definite truthiness that might
1328                                // result in empty iterables (t-strings and generators) or will
1329                                // raise a type error (non-iterable types like numbers, booleans,
1330                                // None, etc.).
1331                                Expr::NumberLiteral(_)
1332                                | Expr::BooleanLiteral(_)
1333                                | Expr::NoneLiteral(_)
1334                                | Expr::EllipsisLiteral(_)
1335                                | Expr::TString(_)
1336                                | Expr::Lambda(_)
1337                                | Expr::Generator(_) => Self::Unknown,
1338                                // Recurse for all other types - collections, comprehensions, variables, etc.
1339                                // StringLiteral, FString, and BytesLiteral recurse because Self::from_expr
1340                                // correctly handles their truthiness (checking if empty or not).
1341                                _ => Self::from_expr(argument, is_builtin),
1342                            }
1343                        } else {
1344                            Self::Unknown
1345                        }
1346                    } else {
1347                        Self::Unknown
1348                    }
1349                } else {
1350                    Self::Unknown
1351                }
1352            }
1353            _ => Self::Unknown,
1354        }
1355    }
1356
1357    pub fn into_bool(self) -> Option<bool> {
1358        match self {
1359            Self::True | Self::Truthy => Some(true),
1360            Self::False | Self::Falsey => Some(false),
1361            Self::None => Some(false),
1362            Self::Unknown => None,
1363        }
1364    }
1365}
1366
1367/// Returns `true` if the expression definitely resolves to a non-empty string, when used as an
1368/// f-string expression, or `false` if the expression may resolve to an empty string.
1369fn is_non_empty_f_string(expr: &ast::ExprFString) -> bool {
1370    fn inner(expr: &Expr) -> bool {
1371        match expr {
1372            // When stringified, these expressions are always non-empty.
1373            Expr::Lambda(_) => true,
1374            Expr::Dict(_) => true,
1375            Expr::Set(_) => true,
1376            Expr::ListComp(_) => true,
1377            Expr::SetComp(_) => true,
1378            Expr::DictComp(_) => true,
1379            Expr::Compare(_) => true,
1380            Expr::NumberLiteral(_) => true,
1381            Expr::BooleanLiteral(_) => true,
1382            Expr::NoneLiteral(_) => true,
1383            Expr::EllipsisLiteral(_) => true,
1384            Expr::List(_) => true,
1385            Expr::Tuple(_) => true,
1386            Expr::TString(_) => true,
1387
1388            // These expressions must resolve to the inner expression.
1389            Expr::If(ast::ExprIf { body, orelse, .. }) => inner(body) && inner(orelse),
1390            Expr::Named(ast::ExprNamed { value, .. }) => inner(value),
1391
1392            // These expressions are complex. We can't determine whether they're empty or not.
1393            Expr::BoolOp(ast::ExprBoolOp { .. }) => false,
1394            Expr::BinOp(ast::ExprBinOp { .. }) => false,
1395            Expr::UnaryOp(ast::ExprUnaryOp { .. }) => false,
1396            Expr::Generator(_) => false,
1397            Expr::Await(_) => false,
1398            Expr::Yield(_) => false,
1399            Expr::YieldFrom(_) => false,
1400            Expr::Call(_) => false,
1401            Expr::Attribute(_) => false,
1402            Expr::Subscript(_) => false,
1403            Expr::Starred(_) => false,
1404            Expr::Name(_) => false,
1405            Expr::Slice(_) => false,
1406            Expr::IpyEscapeCommand(_) => false,
1407
1408            // These literals may or may not be empty.
1409            Expr::FString(f_string) => is_non_empty_f_string(f_string),
1410            // These literals may or may not be empty.
1411            Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => !value.is_empty(),
1412            // Confusingly, f"{b""}" renders as the string 'b""', which is non-empty.
1413            // Therefore, any bytes interpolation is guaranteed non-empty when stringified.
1414            Expr::BytesLiteral(_) => true,
1415        }
1416    }
1417
1418    expr.value.iter().any(|part| match part {
1419        ast::FStringPart::Literal(string_literal) => !string_literal.is_empty(),
1420        ast::FStringPart::FString(f_string) => {
1421            f_string.elements.iter().all(|element| match element {
1422                InterpolatedStringElement::Literal(string_literal) => !string_literal.is_empty(),
1423                InterpolatedStringElement::Interpolation(f_string) => {
1424                    f_string.debug_text.is_some() || inner(&f_string.expression)
1425                }
1426            })
1427        }
1428    })
1429}
1430
1431/// Returns `true` if the expression definitely resolves to the empty string, when used as an f-string
1432/// expression.
1433pub fn is_empty_f_string(expr: &ast::ExprFString) -> bool {
1434    fn inner(expr: &Expr) -> bool {
1435        match expr {
1436            Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => value.is_empty(),
1437            // Confusingly, `bool(f"{b""}") == True` even though
1438            // `bool(b"") == False`. This is because `f"{b""}"`
1439            // evaluates as the string `'b""'` of length 3.
1440            Expr::BytesLiteral(_) => false,
1441            Expr::FString(ast::ExprFString { value, .. }) => {
1442                is_empty_interpolated_elements(value.elements())
1443            }
1444            _ => false,
1445        }
1446    }
1447
1448    fn is_empty_interpolated_elements<'a>(
1449        mut elements: impl Iterator<Item = &'a InterpolatedStringElement>,
1450    ) -> bool {
1451        elements.all(|element| match element {
1452            InterpolatedStringElement::Literal(ast::InterpolatedStringLiteralElement {
1453                value,
1454                ..
1455            }) => value.is_empty(),
1456            InterpolatedStringElement::Interpolation(f_string) => {
1457                f_string.debug_text.is_none()
1458                    && f_string.conversion.is_none()
1459                    && f_string.format_spec.is_none()
1460                    && inner(&f_string.expression)
1461            }
1462        })
1463    }
1464
1465    expr.value.iter().all(|part| match part {
1466        ast::FStringPart::Literal(string_literal) => string_literal.is_empty(),
1467        ast::FStringPart::FString(f_string) => {
1468            is_empty_interpolated_elements(f_string.elements.iter())
1469        }
1470    })
1471}
1472
1473pub fn generate_comparison(
1474    left: &Expr,
1475    ops: &[CmpOp],
1476    comparators: &[Expr],
1477    parent: AnyNodeRef,
1478    tokens: &Tokens,
1479    source: &str,
1480) -> String {
1481    let start = left.start();
1482    let end = comparators.last().map_or_else(|| left.end(), Ranged::end);
1483    let mut contents = String::with_capacity(usize::from(end - start));
1484
1485    // Add the left side of the comparison.
1486    contents.push_str(
1487        &source[parenthesized_range(left.into(), parent, tokens).unwrap_or(left.range())],
1488    );
1489
1490    for (op, comparator) in ops.iter().zip(comparators) {
1491        // Add the operator.
1492        contents.push_str(match op {
1493            CmpOp::Eq => " == ",
1494            CmpOp::NotEq => " != ",
1495            CmpOp::Lt => " < ",
1496            CmpOp::LtE => " <= ",
1497            CmpOp::Gt => " > ",
1498            CmpOp::GtE => " >= ",
1499            CmpOp::In => " in ",
1500            CmpOp::NotIn => " not in ",
1501            CmpOp::Is => " is ",
1502            CmpOp::IsNot => " is not ",
1503        });
1504
1505        // Add the right side of the comparison.
1506        contents.push_str(
1507            &source[parenthesized_range(comparator.into(), parent, tokens)
1508                .unwrap_or(comparator.range())],
1509        );
1510    }
1511
1512    contents
1513}
1514
1515/// Format the expression as a PEP 604-style optional.
1516pub fn pep_604_optional(expr: &Expr) -> Expr {
1517    ast::ExprBinOp {
1518        left: Box::new(expr.clone()),
1519        op: Operator::BitOr,
1520        right: Box::new(Expr::NoneLiteral(ExprNoneLiteral::default())),
1521        range: TextRange::default(),
1522        node_index: AtomicNodeIndex::NONE,
1523    }
1524    .into()
1525}
1526
1527/// Format the expressions as a PEP 604-style union.
1528pub fn pep_604_union(elts: &[Expr]) -> Expr {
1529    match elts {
1530        [] => Expr::Tuple(ast::ExprTuple {
1531            elts: vec![],
1532            ctx: ExprContext::Load,
1533            range: TextRange::default(),
1534            node_index: AtomicNodeIndex::NONE,
1535            parenthesized: true,
1536        }),
1537        [Expr::Tuple(ast::ExprTuple { elts, .. })] => pep_604_union(elts),
1538        [elt] => elt.clone(),
1539        [rest @ .., elt] => Expr::BinOp(ast::ExprBinOp {
1540            left: Box::new(pep_604_union(rest)),
1541            op: Operator::BitOr,
1542            right: Box::new(pep_604_union(std::slice::from_ref(elt))),
1543            range: TextRange::default(),
1544            node_index: AtomicNodeIndex::NONE,
1545        }),
1546    }
1547}
1548
1549/// Format the expression as a `typing.Optional`-style optional.
1550pub fn typing_optional(elt: Expr, binding: Name) -> Expr {
1551    Expr::Subscript(ast::ExprSubscript {
1552        value: Box::new(Expr::Name(ast::ExprName {
1553            id: binding,
1554            range: TextRange::default(),
1555            node_index: AtomicNodeIndex::NONE,
1556            ctx: ExprContext::Load,
1557        })),
1558        slice: Box::new(elt),
1559        ctx: ExprContext::Load,
1560        range: TextRange::default(),
1561        node_index: AtomicNodeIndex::NONE,
1562    })
1563}
1564
1565/// Format the expressions as a `typing.Union`-style union.
1566///
1567/// Note: It is a syntax error to have `Union[]` so the caller
1568/// should ensure that the `elts` argument is nonempty.
1569pub fn typing_union(elts: &[Expr], binding: Name) -> Expr {
1570    Expr::Subscript(ast::ExprSubscript {
1571        value: Box::new(Expr::Name(ast::ExprName {
1572            id: binding,
1573            range: TextRange::default(),
1574            node_index: AtomicNodeIndex::NONE,
1575            ctx: ExprContext::Load,
1576        })),
1577        slice: Box::new(Expr::Tuple(ast::ExprTuple {
1578            range: TextRange::default(),
1579            node_index: AtomicNodeIndex::NONE,
1580            elts: elts.to_vec(),
1581            ctx: ExprContext::Load,
1582            parenthesized: false,
1583        })),
1584        ctx: ExprContext::Load,
1585        range: TextRange::default(),
1586        node_index: AtomicNodeIndex::NONE,
1587    })
1588}
1589
1590/// Determine the indentation level of an own-line comment, defined as the minimum indentation of
1591/// all comments between the preceding node and the comment, including the comment itself. In
1592/// other words, we don't allow successive comments to ident _further_ than any preceding comments.
1593///
1594/// For example, given:
1595/// ```python
1596/// if True:
1597///     pass
1598///     # comment
1599/// ```
1600///
1601/// The indentation would be 4, as the comment is indented by 4 spaces.
1602///
1603/// Given:
1604/// ```python
1605/// if True:
1606///     pass
1607/// # comment
1608/// else:
1609///     pass
1610/// ```
1611///
1612/// The indentation would be 0, as the comment is not indented at all.
1613///
1614/// Given:
1615/// ```python
1616/// if True:
1617///     pass
1618///     # comment
1619///         # comment
1620/// ```
1621///
1622/// Both comments would be marked as indented at 4 spaces, as the indentation of the first comment
1623/// is used for the second comment.
1624///
1625/// This logic avoids pathological cases like:
1626/// ```python
1627/// try:
1628///     if True:
1629///         if True:
1630///             pass
1631///
1632///         # a
1633///             # b
1634///         # c
1635/// except Exception:
1636///     pass
1637/// ```
1638///
1639/// If we don't use the minimum indentation of any preceding comments, we would mark `# b` as
1640/// indented to the same depth as `pass`, which could in turn lead to us treating it as a trailing
1641/// comment of `pass`, despite there being a comment between them that "resets" the indentation.
1642pub fn comment_indentation_after(
1643    preceding: AnyNodeRef,
1644    comment_range: TextRange,
1645    source: &str,
1646) -> TextSize {
1647    let tokenizer = SimpleTokenizer::new(
1648        source,
1649        TextRange::new(source.full_line_end(preceding.end()), comment_range.end()),
1650    );
1651
1652    tokenizer
1653        .filter_map(|token| {
1654            if token.kind() == SimpleTokenKind::Comment {
1655                indentation_at_offset(token.start(), source).map(TextLen::text_len)
1656            } else {
1657                None
1658            }
1659        })
1660        .min()
1661        .unwrap_or_default()
1662}
1663
1664#[cfg(test)]
1665mod tests {
1666    use std::borrow::Cow;
1667    use std::cell::RefCell;
1668    use std::vec;
1669
1670    use ruff_text_size::TextRange;
1671
1672    use crate::helpers::{any_over_stmt, any_over_type_param, resolve_imported_module_path};
1673    use crate::{
1674        AtomicNodeIndex, Expr, ExprContext, ExprName, ExprNumberLiteral, Identifier, Int, Number,
1675        Stmt, StmtTypeAlias, TypeParam, TypeParamParamSpec, TypeParamTypeVar,
1676        TypeParamTypeVarTuple, TypeParams,
1677    };
1678
1679    #[test]
1680    fn resolve_import() {
1681        // Return the module directly.
1682        assert_eq!(
1683            resolve_imported_module_path(0, Some("foo"), None),
1684            Some(Cow::Borrowed("foo"))
1685        );
1686
1687        // Construct the module path from the calling module's path.
1688        assert_eq!(
1689            resolve_imported_module_path(
1690                1,
1691                Some("foo"),
1692                Some(&["bar".to_string(), "baz".to_string()])
1693            ),
1694            Some(Cow::Owned("bar.foo".to_string()))
1695        );
1696
1697        // We can't return the module if it's a relative import, and we don't know the calling
1698        // module's path.
1699        assert_eq!(resolve_imported_module_path(1, Some("foo"), None), None);
1700
1701        // We can't return the module if it's a relative import, and the path goes beyond the
1702        // calling module's path.
1703        assert_eq!(
1704            resolve_imported_module_path(1, Some("foo"), Some(&["bar".to_string()])),
1705            None,
1706        );
1707        assert_eq!(
1708            resolve_imported_module_path(2, Some("foo"), Some(&["bar".to_string()])),
1709            None
1710        );
1711    }
1712
1713    #[test]
1714    fn any_over_stmt_type_alias() {
1715        let seen = RefCell::new(Vec::new());
1716        let name = Expr::Name(ExprName {
1717            id: "x".into(),
1718            range: TextRange::default(),
1719            node_index: AtomicNodeIndex::NONE,
1720            ctx: ExprContext::Load,
1721        });
1722        let constant_one = Expr::NumberLiteral(ExprNumberLiteral {
1723            value: Number::Int(Int::from(1u8)),
1724            range: TextRange::default(),
1725            node_index: AtomicNodeIndex::NONE,
1726        });
1727        let constant_two = Expr::NumberLiteral(ExprNumberLiteral {
1728            value: Number::Int(Int::from(2u8)),
1729            range: TextRange::default(),
1730            node_index: AtomicNodeIndex::NONE,
1731        });
1732        let constant_three = Expr::NumberLiteral(ExprNumberLiteral {
1733            value: Number::Int(Int::from(3u8)),
1734            range: TextRange::default(),
1735            node_index: AtomicNodeIndex::NONE,
1736        });
1737        let type_var_one = TypeParam::TypeVar(TypeParamTypeVar {
1738            range: TextRange::default(),
1739            node_index: AtomicNodeIndex::NONE,
1740            bound: Some(Box::new(constant_one.clone())),
1741            default: None,
1742            name: Identifier::new("x", TextRange::default()),
1743        });
1744        let type_var_two = TypeParam::TypeVar(TypeParamTypeVar {
1745            range: TextRange::default(),
1746            node_index: AtomicNodeIndex::NONE,
1747            bound: None,
1748            default: Some(Box::new(constant_two.clone())),
1749            name: Identifier::new("x", TextRange::default()),
1750        });
1751        let type_alias = Stmt::TypeAlias(StmtTypeAlias {
1752            name: Box::new(name.clone()),
1753            type_params: Some(Box::new(TypeParams {
1754                type_params: vec![type_var_one, type_var_two],
1755                range: TextRange::default(),
1756                node_index: AtomicNodeIndex::NONE,
1757            })),
1758            value: Box::new(constant_three.clone()),
1759            range: TextRange::default(),
1760            node_index: AtomicNodeIndex::NONE,
1761        });
1762        assert!(!any_over_stmt(&type_alias, &|expr| {
1763            seen.borrow_mut().push(expr.clone());
1764            false
1765        }));
1766        assert_eq!(
1767            seen.take(),
1768            vec![name, constant_one, constant_two, constant_three]
1769        );
1770    }
1771
1772    #[test]
1773    fn any_over_type_param_type_var() {
1774        let type_var_no_bound = TypeParam::TypeVar(TypeParamTypeVar {
1775            range: TextRange::default(),
1776            node_index: AtomicNodeIndex::NONE,
1777            bound: None,
1778            default: None,
1779            name: Identifier::new("x", TextRange::default()),
1780        });
1781        assert!(!any_over_type_param(&type_var_no_bound, &|_expr| true));
1782
1783        let constant = Expr::NumberLiteral(ExprNumberLiteral {
1784            value: Number::Int(Int::ONE),
1785            range: TextRange::default(),
1786            node_index: AtomicNodeIndex::NONE,
1787        });
1788
1789        let type_var_with_bound = TypeParam::TypeVar(TypeParamTypeVar {
1790            range: TextRange::default(),
1791            node_index: AtomicNodeIndex::NONE,
1792            bound: Some(Box::new(constant.clone())),
1793            default: None,
1794            name: Identifier::new("x", TextRange::default()),
1795        });
1796        assert!(
1797            any_over_type_param(&type_var_with_bound, &|expr| {
1798                assert_eq!(
1799                    *expr, constant,
1800                    "the received expression should be the unwrapped bound"
1801                );
1802                true
1803            }),
1804            "if true is returned from `func` it should be respected"
1805        );
1806
1807        let type_var_with_default = TypeParam::TypeVar(TypeParamTypeVar {
1808            range: TextRange::default(),
1809            node_index: AtomicNodeIndex::NONE,
1810            default: Some(Box::new(constant.clone())),
1811            bound: None,
1812            name: Identifier::new("x", TextRange::default()),
1813        });
1814        assert!(
1815            any_over_type_param(&type_var_with_default, &|expr| {
1816                assert_eq!(
1817                    *expr, constant,
1818                    "the received expression should be the unwrapped default"
1819                );
1820                true
1821            }),
1822            "if true is returned from `func` it should be respected"
1823        );
1824    }
1825
1826    #[test]
1827    fn any_over_type_param_type_var_tuple() {
1828        let type_var_tuple = TypeParam::TypeVarTuple(TypeParamTypeVarTuple {
1829            range: TextRange::default(),
1830            node_index: AtomicNodeIndex::NONE,
1831            name: Identifier::new("x", TextRange::default()),
1832            default: None,
1833        });
1834        assert!(
1835            !any_over_type_param(&type_var_tuple, &|_expr| true),
1836            "this TypeVarTuple has no expressions to visit"
1837        );
1838
1839        let constant = Expr::NumberLiteral(ExprNumberLiteral {
1840            value: Number::Int(Int::ONE),
1841            range: TextRange::default(),
1842            node_index: AtomicNodeIndex::NONE,
1843        });
1844
1845        let type_var_tuple_with_default = TypeParam::TypeVarTuple(TypeParamTypeVarTuple {
1846            range: TextRange::default(),
1847            node_index: AtomicNodeIndex::NONE,
1848            default: Some(Box::new(constant.clone())),
1849            name: Identifier::new("x", TextRange::default()),
1850        });
1851        assert!(
1852            any_over_type_param(&type_var_tuple_with_default, &|expr| {
1853                assert_eq!(
1854                    *expr, constant,
1855                    "the received expression should be the unwrapped default"
1856                );
1857                true
1858            }),
1859            "if true is returned from `func` it should be respected"
1860        );
1861    }
1862
1863    #[test]
1864    fn any_over_type_param_param_spec() {
1865        let type_param_spec = TypeParam::ParamSpec(TypeParamParamSpec {
1866            range: TextRange::default(),
1867            node_index: AtomicNodeIndex::NONE,
1868            name: Identifier::new("x", TextRange::default()),
1869            default: None,
1870        });
1871        assert!(
1872            !any_over_type_param(&type_param_spec, &|_expr| true),
1873            "this ParamSpec has no expressions to visit"
1874        );
1875
1876        let constant = Expr::NumberLiteral(ExprNumberLiteral {
1877            value: Number::Int(Int::ONE),
1878            range: TextRange::default(),
1879            node_index: AtomicNodeIndex::NONE,
1880        });
1881
1882        let param_spec_with_default = TypeParam::TypeVarTuple(TypeParamTypeVarTuple {
1883            range: TextRange::default(),
1884            node_index: AtomicNodeIndex::NONE,
1885            default: Some(Box::new(constant.clone())),
1886            name: Identifier::new("x", TextRange::default()),
1887        });
1888        assert!(
1889            any_over_type_param(&param_spec_with_default, &|expr| {
1890                assert_eq!(
1891                    *expr, constant,
1892                    "the received expression should be the unwrapped default"
1893                );
1894                true
1895            }),
1896            "if true is returned from `func` it should be respected"
1897        );
1898    }
1899}