mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 18:28:24 +00:00
Comments outside expression parentheses (#7873)
<!-- Thank you for contributing to Ruff! To help us out with reviewing, please consider the following: - Does this pull request include a summary of the change? (See below.) - Does this pull request include a descriptive title? - Does this pull request include references to any relevant issues? --> ## Summary Fixes https://github.com/astral-sh/ruff/issues/7448 Fixes https://github.com/astral-sh/ruff/issues/7892 I've removed automatic dangling comment formatting, we're doing manual dangling comment formatting everywhere anyway (the assert-all-comments-formatted ensures this) and dangling comments would break the formatting there. ## Test Plan New test file. --------- Co-authored-by: Micha Reiser <micha@reiser.io>
This commit is contained in:
parent
67b043482a
commit
8f9753f58e
13 changed files with 652 additions and 124 deletions
|
@ -4,35 +4,44 @@ use ruff_text_size::{Ranged, TextLen, TextRange};
|
|||
use crate::AnyNodeRef;
|
||||
use crate::ExpressionRef;
|
||||
|
||||
/// Returns the [`TextRange`] of a given expression including parentheses, if the expression is
|
||||
/// parenthesized; or `None`, if the expression is not parenthesized.
|
||||
pub fn parenthesized_range(
|
||||
expr: ExpressionRef,
|
||||
parent: AnyNodeRef,
|
||||
comment_ranges: &CommentRanges,
|
||||
source: &str,
|
||||
) -> Option<TextRange> {
|
||||
// If the parent is a node that brings its own parentheses, exclude the closing parenthesis
|
||||
// from our search range. Otherwise, we risk matching on calls, like `func(x)`, for which
|
||||
// the open and close parentheses are part of the `Arguments` node.
|
||||
//
|
||||
// There are a few other nodes that may have their own parentheses, but are fine to exclude:
|
||||
// - `Parameters`: The parameters to a function definition. Any expressions would represent
|
||||
// default arguments, and so must be preceded by _at least_ the parameter name. As such,
|
||||
// we won't mistake any parentheses for the opening and closing parentheses on the
|
||||
// `Parameters` node itself.
|
||||
// - `Tuple`: The elements of a tuple. The only risk is a single-element tuple (e.g., `(x,)`),
|
||||
// which must have a trailing comma anyway.
|
||||
let exclusive_parent_end = if parent.is_arguments() {
|
||||
parent.end() - ")".text_len()
|
||||
/// Returns an iterator over the ranges of the optional parentheses surrounding an expression.
|
||||
///
|
||||
/// E.g. for `((f()))` with `f()` as expression, the iterator returns the ranges (1, 6) and (0, 7).
|
||||
///
|
||||
/// Note that without a parent the range can be inaccurate, e.g. `f(a)` we falsely return a set of
|
||||
/// parentheses around `a` even if the parentheses actually belong to `f`. That is why you should
|
||||
/// generally prefer [`parenthesized_range`].
|
||||
pub fn parentheses_iterator<'a>(
|
||||
expr: ExpressionRef<'a>,
|
||||
parent: Option<AnyNodeRef>,
|
||||
comment_ranges: &'a CommentRanges,
|
||||
source: &'a str,
|
||||
) -> impl Iterator<Item = TextRange> + 'a {
|
||||
let right_tokenizer = if let Some(parent) = parent {
|
||||
// If the parent is a node that brings its own parentheses, exclude the closing parenthesis
|
||||
// from our search range. Otherwise, we risk matching on calls, like `func(x)`, for which
|
||||
// the open and close parentheses are part of the `Arguments` node.
|
||||
//
|
||||
// There are a few other nodes that may have their own parentheses, but are fine to exclude:
|
||||
// - `Parameters`: The parameters to a function definition. Any expressions would represent
|
||||
// default arguments, and so must be preceded by _at least_ the parameter name. As such,
|
||||
// we won't mistake any parentheses for the opening and closing parentheses on the
|
||||
// `Parameters` node itself.
|
||||
// - `Tuple`: The elements of a tuple. The only risk is a single-element tuple (e.g., `(x,)`),
|
||||
// which must have a trailing comma anyway.
|
||||
let exclusive_parent_end = if parent.is_arguments() {
|
||||
parent.end() - ")".text_len()
|
||||
} else {
|
||||
parent.end()
|
||||
};
|
||||
SimpleTokenizer::new(source, TextRange::new(expr.end(), exclusive_parent_end))
|
||||
} else {
|
||||
parent.end()
|
||||
SimpleTokenizer::starts_at(expr.end(), source)
|
||||
};
|
||||
|
||||
let right_tokenizer =
|
||||
SimpleTokenizer::new(source, TextRange::new(expr.end(), exclusive_parent_end))
|
||||
.skip_trivia()
|
||||
.take_while(|token| token.kind == SimpleTokenKind::RParen);
|
||||
let right_tokenizer = right_tokenizer
|
||||
.skip_trivia()
|
||||
.take_while(|token| token.kind == SimpleTokenKind::RParen);
|
||||
|
||||
let left_tokenizer = BackwardsTokenizer::up_to(expr.start(), source, comment_ranges)
|
||||
.skip_trivia()
|
||||
|
@ -43,6 +52,16 @@ pub fn parenthesized_range(
|
|||
// the `right_tokenizer` is exhausted.
|
||||
right_tokenizer
|
||||
.zip(left_tokenizer)
|
||||
.last()
|
||||
.map(|(right, left)| TextRange::new(left.start(), right.end()))
|
||||
}
|
||||
|
||||
/// Returns the [`TextRange`] of a given expression including parentheses, if the expression is
|
||||
/// parenthesized; or `None`, if the expression is not parenthesized.
|
||||
pub fn parenthesized_range(
|
||||
expr: ExpressionRef,
|
||||
parent: AnyNodeRef,
|
||||
comment_ranges: &CommentRanges,
|
||||
source: &str,
|
||||
) -> Option<TextRange> {
|
||||
parentheses_iterator(expr, Some(parent), comment_ranges, source).last()
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue