Skip to main content

ruff_python_ast/token/
parentheses.rs

1use ruff_text_size::{Ranged, TextRange};
2
3use super::{TokenKind, Tokens};
4use crate::{AnyNodeRef, ExprRef};
5
6/// Returns an iterator over the ranges of the optional parentheses surrounding an expression.
7///
8/// E.g. for `((f()))` with `f()` as expression, the iterator returns the ranges (1, 6) and (0, 7).
9///
10/// Note that without a parent the range can be inaccurate, e.g. `f(a)` we falsely return a set of
11/// parentheses around `a` even if the parentheses actually belong to `f`. That is why you should
12/// generally prefer [`parenthesized_range`].
13pub fn parentheses_iterator<'a>(
14    expr: ExprRef<'a>,
15    parent: Option<AnyNodeRef>,
16    tokens: &'a Tokens,
17) -> impl Iterator<Item = TextRange> + 'a {
18    let after_tokens = if let Some(parent) = parent {
19        let after_tokens = tokens.in_range(TextRange::new(expr.end(), parent.end()));
20
21        // If the parent is a node that brings its own parentheses, exclude the closing parenthesis
22        // from our search range. Otherwise, we risk matching on calls, like `func(x)`, for which
23        // the open and close parentheses are part of the `Arguments` node.
24        // The closing parenthesis may be missing in an incomplete call.
25        if parent.is_arguments()
26            && let Some((last, remaining)) = after_tokens.split_last()
27            && last.kind() == TokenKind::Rpar
28        {
29            remaining
30        } else {
31            after_tokens
32        }
33    } else {
34        tokens.after(expr.end())
35    };
36
37    let right_parens = after_tokens
38        .iter()
39        .filter(|token| !token.kind().is_trivia())
40        .take_while(move |token| token.kind() == TokenKind::Rpar);
41
42    let left_parens = tokens
43        .before(expr.start())
44        .iter()
45        .rev()
46        .filter(|token| !token.kind().is_trivia())
47        .take_while(|token| token.kind() == TokenKind::Lpar);
48
49    right_parens
50        .zip(left_parens)
51        .map(|(right, left)| TextRange::new(left.start(), right.end()))
52}
53
54/// Returns the [`TextRange`] of a given expression including parentheses, if the expression is
55/// parenthesized; or `None`, if the expression is not parenthesized.
56pub fn parenthesized_range(
57    expr: ExprRef,
58    parent: AnyNodeRef,
59    tokens: &Tokens,
60) -> Option<TextRange> {
61    parentheses_iterator(expr, Some(parent), tokens).last()
62}