Implement RUF028 to detect useless formatter suppression comments (#9899)

<!--
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?
-->
Fixes #6611

## Summary

This lint rule spots comments that are _intended_ to suppress or enable
the formatter, but will be ignored by the Ruff formatter.

We borrow some functions the formatter uses for determining comment
placement / putting them in context within an AST.

The analysis function uses an AST visitor to visit each comment and
attach it to the AST. It then uses that context to check:
1. Is this comment in an expression?
2. Does this comment have bad placement? (e.g. a `# fmt: skip` above a
function instead of at the end of a line)
3. Is this comment redundant?
4. Does this comment actually suppress any code?
5. Does this comment have ambiguous placement? (e.g. a `# fmt: off`
above an `else:` block)

If any of these are true, a violation is thrown. The reported reason
depends on the order of the above check-list: in other words, a `# fmt:
skip` comment on its own line within a list expression will be reported
as being in an expression, since that reason takes priority.

The lint suggests removing the comment as an unsafe fix, regardless of
the reason.

## Test Plan

A snapshot test has been created.
This commit is contained in:
Jane Lewis 2024-02-28 11:21:06 -08:00 committed by GitHub
parent 36bc725eaa
commit 0293908b71
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 1215 additions and 438 deletions

View file

@ -182,11 +182,11 @@ mod tests {
use ruff_formatter::SourceCode;
use ruff_python_ast::AnyNode;
use ruff_python_ast::{StmtBreak, StmtContinue};
use ruff_python_trivia::CommentRanges;
use ruff_python_trivia::{CommentLinePosition, CommentRanges};
use ruff_text_size::{TextRange, TextSize};
use crate::comments::map::MultiMap;
use crate::comments::{CommentLinePosition, Comments, CommentsMap, SourceComment};
use crate::comments::{Comments, CommentsMap, SourceComment};
#[test]
fn debug() {

View file

@ -3,11 +3,11 @@ use std::borrow::Cow;
use ruff_formatter::{format_args, write, FormatError, FormatOptions, SourceCode};
use ruff_python_ast::{AnyNodeRef, AstNode, NodeKind, PySourceType};
use ruff_python_trivia::{
is_pragma_comment, lines_after, lines_after_ignoring_trivia, lines_before,
is_pragma_comment, lines_after, lines_after_ignoring_trivia, lines_before, CommentLinePosition,
};
use ruff_text_size::{Ranged, TextLen, TextRange};
use crate::comments::{CommentLinePosition, SourceComment};
use crate::comments::SourceComment;
use crate::context::NodeLevel;
use crate::prelude::*;
use crate::preview::is_blank_line_after_nested_stub_class_enabled;

View file

@ -98,7 +98,7 @@ pub(crate) use format::{
use ruff_formatter::{SourceCode, SourceCodeSlice};
use ruff_python_ast::visitor::preorder::{PreorderVisitor, TraversalSignal};
use ruff_python_ast::AnyNodeRef;
use ruff_python_trivia::{CommentRanges, PythonWhitespace};
use ruff_python_trivia::{CommentLinePosition, CommentRanges, SuppressionKind};
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextRange};
pub(crate) use visitor::collect_comments;
@ -168,73 +168,16 @@ impl SourceComment {
DebugComment::new(self, source_code)
}
pub(crate) fn suppression_kind(&self, source: &str) -> Option<SuppressionKind> {
let text = self.slice.text(SourceCode::new(source));
// Match against `# fmt: on`, `# fmt: off`, `# yapf: disable`, and `# yapf: enable`, which
// must be on their own lines.
let trimmed = text.strip_prefix('#').unwrap_or(text).trim_whitespace();
if let Some(command) = trimmed.strip_prefix("fmt:") {
match command.trim_whitespace_start() {
"off" => return Some(SuppressionKind::Off),
"on" => return Some(SuppressionKind::On),
"skip" => return Some(SuppressionKind::Skip),
_ => {}
}
} else if let Some(command) = trimmed.strip_prefix("yapf:") {
match command.trim_whitespace_start() {
"disable" => return Some(SuppressionKind::Off),
"enable" => return Some(SuppressionKind::On),
_ => {}
}
}
// Search for `# fmt: skip` comments, which can be interspersed with other comments (e.g.,
// `# fmt: skip # noqa: E501`).
for segment in text.split('#') {
let trimmed = segment.trim_whitespace();
if let Some(command) = trimmed.strip_prefix("fmt:") {
if command.trim_whitespace_start() == "skip" {
return Some(SuppressionKind::Skip);
}
}
}
None
pub(crate) fn is_suppression_off_comment(&self, text: &str) -> bool {
SuppressionKind::is_suppression_off(self.text(text), self.line_position)
}
/// Returns true if this comment is a `fmt: off` or `yapf: disable` own line suppression comment.
pub(crate) fn is_suppression_off_comment(&self, source: &str) -> bool {
self.line_position.is_own_line()
&& matches!(self.suppression_kind(source), Some(SuppressionKind::Off))
pub(crate) fn is_suppression_on_comment(&self, text: &str) -> bool {
SuppressionKind::is_suppression_on(self.text(text), self.line_position)
}
/// Returns true if this comment is a `fmt: on` or `yapf: enable` own line suppression comment.
pub(crate) fn is_suppression_on_comment(&self, source: &str) -> bool {
self.line_position.is_own_line()
&& matches!(self.suppression_kind(source), Some(SuppressionKind::On))
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub(crate) enum SuppressionKind {
/// A `fmt: off` or `yapf: disable` comment
Off,
/// A `fmt: on` or `yapf: enable` comment
On,
/// A `fmt: skip` comment
Skip,
}
impl SuppressionKind {
pub(crate) fn has_skip_comment(trailing_comments: &[SourceComment], source: &str) -> bool {
trailing_comments.iter().any(|comment| {
comment.line_position().is_end_of_line()
&& matches!(
comment.suppression_kind(source),
Some(SuppressionKind::Skip | SuppressionKind::Off)
)
})
fn text<'a>(&self, text: &'a str) -> &'a str {
self.slice.text(SourceCode::new(text))
}
}
@ -245,48 +188,6 @@ impl Ranged for SourceComment {
}
}
/// The position of a comment in the source text.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub(crate) enum CommentLinePosition {
/// A comment that is on the same line as the preceding token and is separated by at least one line break from the following token.
///
/// # Examples
///
/// ## End of line
///
/// ```python
/// a; # comment
/// b;
/// ```
///
/// `# comment` is an end of line comments because it is separated by at least one line break from the following token `b`.
/// Comments that not only end, but also start on a new line are [`OwnLine`](CommentLinePosition::OwnLine) comments.
EndOfLine,
/// A Comment that is separated by at least one line break from the preceding token.
///
/// # Examples
///
/// ```python
/// a;
/// # comment
/// b;
/// ```
///
/// `# comment` line comments because they are separated by one line break from the preceding token `a`.
OwnLine,
}
impl CommentLinePosition {
pub(crate) const fn is_own_line(self) -> bool {
matches!(self, CommentLinePosition::OwnLine)
}
pub(crate) const fn is_end_of_line(self) -> bool {
matches!(self, CommentLinePosition::EndOfLine)
}
}
type CommentsMap<'a> = MultiMap<NodeRefEqualityKey<'a>, SourceComment>;
/// The comments of a syntax tree stored by node.
@ -563,6 +464,16 @@ impl<'a> PreorderVisitor<'a> for MarkVerbatimCommentsAsFormattedVisitor<'a> {
}
}
pub(crate) fn has_skip_comment(trailing_comments: &[SourceComment], source: &str) -> bool {
trailing_comments.iter().any(|comment| {
comment.line_position().is_end_of_line()
&& matches!(
SuppressionKind::from_comment(comment.text(source)),
Some(SuppressionKind::Skip | SuppressionKind::Off)
)
})
}
#[cfg(test)]
mod tests {
use insta::assert_debug_snapshot;

View file

@ -1,15 +1,14 @@
use std::cmp::Ordering;
use ast::helpers::comment_indentation_after;
use ruff_python_ast::whitespace::indentation;
use ruff_python_ast::{
self as ast, AnyNodeRef, Comprehension, Expr, MatchCase, ModModule, Parameters,
};
use ruff_python_ast::{self as ast, AnyNodeRef, Comprehension, Expr, ModModule, Parameters};
use ruff_python_trivia::{
find_only_token_in_range, indentation_at_offset, BackwardsTokenizer, CommentRanges,
SimpleToken, SimpleTokenKind, SimpleTokenizer,
};
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
use ruff_text_size::{Ranged, TextLen, TextRange};
use crate::comments::visitor::{CommentPlacement, DecoratedComment};
use crate::expression::expr_slice::{assign_comment_in_slice, ExprSliceCommentSection};
@ -342,7 +341,7 @@ fn handle_end_of_line_comment_around_body<'a>(
// pass
// ```
if let Some(following) = comment.following_node() {
if is_first_statement_in_body(following, comment.enclosing_node())
if following.is_first_statement_in_body(comment.enclosing_node())
&& SimpleTokenizer::new(
locator.contents(),
TextRange::new(comment.end(), following.start()),
@ -379,86 +378,6 @@ fn handle_end_of_line_comment_around_body<'a>(
CommentPlacement::Default(comment)
}
/// Check if the given statement is the first statement after the colon of a branch, be it in if
/// statements, for statements, after each part of a try-except-else-finally or function/class
/// definitions.
///
///
/// ```python
/// if True: <- has body
/// a <- first statement
/// b
/// elif b: <- has body
/// c <- first statement
/// d
/// else: <- has body
/// e <- first statement
/// f
///
/// class: <- has body
/// a: int <- first statement
/// b: int
///
/// ```
///
/// For nodes with multiple bodies, we check all bodies that don't have their own node. For
/// try-except-else-finally, each except branch has it's own node, so for the `StmtTry`, we check
/// the `try:`, `else:` and `finally:`, bodies, while `ExceptHandlerExceptHandler` has it's own
/// check. For for-else and while-else, we check both branches for the whole statement.
///
/// ```python
/// try: <- has body (a)
/// 6/8 <- first statement (a)
/// 1/0
/// except: <- has body (b)
/// a <- first statement (b)
/// b
/// else:
/// c <- first statement (a)
/// d
/// finally:
/// e <- first statement (a)
/// f
/// ```
fn is_first_statement_in_body(statement: AnyNodeRef, has_body: AnyNodeRef) -> bool {
match has_body {
AnyNodeRef::StmtFor(ast::StmtFor { body, orelse, .. })
| AnyNodeRef::StmtWhile(ast::StmtWhile { body, orelse, .. }) => {
are_same_optional(statement, body.first())
|| are_same_optional(statement, orelse.first())
}
AnyNodeRef::StmtTry(ast::StmtTry {
body,
orelse,
finalbody,
..
}) => {
are_same_optional(statement, body.first())
|| are_same_optional(statement, orelse.first())
|| are_same_optional(statement, finalbody.first())
}
AnyNodeRef::StmtIf(ast::StmtIf { body, .. })
| AnyNodeRef::ElifElseClause(ast::ElifElseClause { body, .. })
| AnyNodeRef::StmtWith(ast::StmtWith { body, .. })
| AnyNodeRef::ExceptHandlerExceptHandler(ast::ExceptHandlerExceptHandler {
body, ..
})
| AnyNodeRef::MatchCase(MatchCase { body, .. })
| AnyNodeRef::StmtFunctionDef(ast::StmtFunctionDef { body, .. })
| AnyNodeRef::StmtClassDef(ast::StmtClassDef { body, .. }) => {
are_same_optional(statement, body.first())
}
AnyNodeRef::StmtMatch(ast::StmtMatch { cases, .. }) => {
are_same_optional(statement, cases.first())
}
_ => false,
}
}
/// Handles own-line comments around a body (at the end of the body, at the end of the header
/// preceding the body, or between bodies):
///
@ -610,13 +529,13 @@ fn handle_own_line_comment_between_branches<'a>(
let Some(following) = comment.following_node() else {
return CommentPlacement::Default(comment);
};
if !is_first_statement_in_alternate_body(following, comment.enclosing_node()) {
if !following.is_first_statement_in_alternate_body(comment.enclosing_node()) {
return CommentPlacement::Default(comment);
}
// It depends on the indentation level of the comment if it is a leading comment for the
// following branch or if it a trailing comment of the previous body's last statement.
let comment_indentation = own_line_comment_indentation(preceding, &comment, locator);
let comment_indentation = comment_indentation_after(preceding, comment.range(), locator);
let preceding_indentation = indentation(locator, &preceding)
.unwrap_or_default()
@ -700,7 +619,7 @@ fn handle_own_line_comment_after_branch<'a>(
// We only care about the length because indentations with mixed spaces and tabs are only valid if
// the indent-level doesn't depend on the tab width (the indent level must be the same if the tab width is 1 or 8).
let comment_indentation = own_line_comment_indentation(preceding, &comment, locator);
let comment_indentation = comment_indentation_after(preceding, comment.range(), locator);
// Keep the comment on the entire statement in case it's a trailing comment
// ```python
@ -776,80 +695,6 @@ fn handle_own_line_comment_after_branch<'a>(
}
}
/// Determine the indentation level of an own-line comment, defined as the minimum indentation of
/// all comments between the preceding node and the comment, including the comment itself. In
/// other words, we don't allow successive comments to ident _further_ than any preceding comments.
///
/// For example, given:
/// ```python
/// if True:
/// pass
/// # comment
/// ```
///
/// The indentation would be 4, as the comment is indented by 4 spaces.
///
/// Given:
/// ```python
/// if True:
/// pass
/// # comment
/// else:
/// pass
/// ```
///
/// The indentation would be 0, as the comment is not indented at all.
///
/// Given:
/// ```python
/// if True:
/// pass
/// # comment
/// # comment
/// ```
///
/// Both comments would be marked as indented at 4 spaces, as the indentation of the first comment
/// is used for the second comment.
///
/// This logic avoids pathological cases like:
/// ```python
/// try:
/// if True:
/// if True:
/// pass
///
/// # a
/// # b
/// # c
/// except Exception:
/// pass
/// ```
///
/// If we don't use the minimum indentation of any preceding comments, we would mark `# b` as
/// indented to the same depth as `pass`, which could in turn lead to us treating it as a trailing
/// comment of `pass`, despite there being a comment between them that "resets" the indentation.
fn own_line_comment_indentation(
preceding: AnyNodeRef,
comment: &DecoratedComment,
locator: &Locator,
) -> TextSize {
let tokenizer = SimpleTokenizer::new(
locator.contents(),
TextRange::new(locator.full_line_end(preceding.end()), comment.end()),
);
tokenizer
.filter_map(|token| {
if token.kind() == SimpleTokenKind::Comment {
indentation_at_offset(token.start(), locator).map(TextLen::text_len)
} else {
None
}
})
.min()
.unwrap_or_default()
}
/// Attaches comments for the positional-only parameters separator `/` or the keywords-only
/// parameters separator `*` as dangling comments to the enclosing [`Parameters`] node.
///
@ -2192,40 +2037,6 @@ fn handle_comprehension_comment<'a>(
CommentPlacement::Default(comment)
}
/// Returns `true` if `right` is `Some` and `left` and `right` are referentially equal.
fn are_same_optional<'a, T>(left: AnyNodeRef, right: Option<T>) -> bool
where
T: Into<AnyNodeRef<'a>>,
{
right.is_some_and(|right| left.ptr_eq(right.into()))
}
/// Returns `true` if `statement` is the first statement in an alternate `body` (e.g. the else of an if statement)
fn is_first_statement_in_alternate_body(statement: AnyNodeRef, has_body: AnyNodeRef) -> bool {
match has_body {
AnyNodeRef::StmtFor(ast::StmtFor { orelse, .. })
| AnyNodeRef::StmtWhile(ast::StmtWhile { orelse, .. }) => {
are_same_optional(statement, orelse.first())
}
AnyNodeRef::StmtTry(ast::StmtTry {
handlers,
orelse,
finalbody,
..
}) => {
are_same_optional(statement, handlers.first())
|| are_same_optional(statement, orelse.first())
|| are_same_optional(statement, finalbody.first())
}
AnyNodeRef::StmtIf(ast::StmtIf {
elif_else_clauses, ..
}) => are_same_optional(statement, elif_else_clauses.first()),
_ => false,
}
}
/// Returns `true` if the parameters are parenthesized (as in a function definition), or `false` if
/// not (as in a lambda).
fn are_parameters_parenthesized(parameters: &Parameters, contents: &str) -> bool {

View file

@ -8,13 +8,13 @@ use ruff_python_ast::{Mod, Stmt};
// pre-order.
#[allow(clippy::wildcard_imports)]
use ruff_python_ast::visitor::preorder::*;
use ruff_python_trivia::{is_python_whitespace, CommentRanges};
use ruff_python_trivia::{CommentLinePosition, CommentRanges};
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextRange, TextSize};
use crate::comments::node_key::NodeRefEqualityKey;
use crate::comments::placement::place_comment;
use crate::comments::{CommentLinePosition, CommentsMap, SourceComment};
use crate::comments::{CommentsMap, SourceComment};
/// Collect the preceding, following and enclosing node for each comment without applying
/// [`place_comment`] for debugging.
@ -90,7 +90,10 @@ impl<'ast> PreorderVisitor<'ast> for CommentsVisitor<'ast, '_> {
preceding: self.preceding_node,
following: Some(node),
parent: self.parents.iter().rev().nth(1).copied(),
line_position: text_position(*comment_range, self.source_code),
line_position: CommentLinePosition::for_range(
*comment_range,
self.source_code.as_str(),
),
slice: self.source_code.slice(*comment_range),
};
@ -130,7 +133,10 @@ impl<'ast> PreorderVisitor<'ast> for CommentsVisitor<'ast, '_> {
parent: self.parents.last().copied(),
preceding: self.preceding_node,
following: None,
line_position: text_position(*comment_range, self.source_code),
line_position: CommentLinePosition::for_range(
*comment_range,
self.source_code.as_str(),
),
slice: self.source_code.slice(*comment_range),
};
@ -163,22 +169,6 @@ impl<'ast> PreorderVisitor<'ast> for CommentsVisitor<'ast, '_> {
}
}
fn text_position(comment_range: TextRange, source_code: SourceCode) -> CommentLinePosition {
let before = &source_code.as_str()[TextRange::up_to(comment_range.start())];
for c in before.chars().rev() {
match c {
'\n' | '\r' => {
break;
}
c if is_python_whitespace(c) => continue,
_ => return CommentLinePosition::EndOfLine,
}
}
CommentLinePosition::OwnLine
}
/// A comment decorated with additional information about its surrounding context in the source document.
///
/// Used by [`CommentStyle::place_comment`] to determine if this should become a [leading](self#leading-comments), [dangling](self#dangling-comments), or [trailing](self#trailing-comments) comment.

View file

@ -12,7 +12,8 @@ use ruff_python_trivia::CommentRanges;
use ruff_source_file::Locator;
use crate::comments::{
dangling_comments, leading_comments, trailing_comments, Comments, SourceComment,
dangling_comments, has_skip_comment, leading_comments, trailing_comments, Comments,
SourceComment,
};
pub use crate::context::PyFormatContext;
pub use crate::options::{

View file

@ -1,10 +1,10 @@
use ruff_formatter::write;
use ruff_python_ast::Decorator;
use crate::comments::{SourceComment, SuppressionKind};
use crate::comments::SourceComment;
use crate::expression::maybe_parenthesize_expression;
use crate::expression::parentheses::Parenthesize;
use crate::prelude::*;
use crate::{has_skip_comment, prelude::*};
#[derive(Default)]
pub struct FormatDecorator;
@ -30,6 +30,6 @@ impl FormatNodeRule<Decorator> for FormatDecorator {
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -3,12 +3,12 @@ use std::usize;
use ruff_formatter::{format_args, write, FormatRuleWithOptions};
use ruff_python_ast::Parameters;
use ruff_python_ast::{AnyNodeRef, AstNode};
use ruff_python_trivia::{SimpleToken, SimpleTokenKind, SimpleTokenizer};
use ruff_python_trivia::{CommentLinePosition, SimpleToken, SimpleTokenKind, SimpleTokenizer};
use ruff_text_size::{Ranged, TextRange, TextSize};
use crate::comments::{
dangling_comments, dangling_open_parenthesis_comments, leading_comments, leading_node_comments,
trailing_comments, CommentLinePosition, SourceComment,
trailing_comments, SourceComment,
};
use crate::context::{NodeLevel, WithNodeLevel};
use crate::expression::parentheses::empty_parenthesized;

View file

@ -7,13 +7,11 @@ use ruff_python_ast::{
use ruff_python_trivia::{SimpleToken, SimpleTokenKind, SimpleTokenizer};
use ruff_text_size::{Ranged, TextRange, TextSize};
use crate::comments::{
leading_alternate_branch_comments, trailing_comments, SourceComment, SuppressionKind,
};
use crate::prelude::*;
use crate::comments::{leading_alternate_branch_comments, trailing_comments, SourceComment};
use crate::preview::is_dummy_implementations_enabled;
use crate::statement::suite::{contains_only_an_ellipsis, SuiteKind};
use crate::verbatim::write_suppressed_clause_header;
use crate::{has_skip_comment, prelude::*};
/// The header of a compound statement clause.
///
@ -354,7 +352,7 @@ impl<'ast> Format<PyFormatContext<'ast>> for FormatClauseHeader<'_, 'ast> {
leading_alternate_branch_comments(leading_comments, last_node).fmt(f)?;
}
if SuppressionKind::has_skip_comment(self.trailing_colon_comment, f.context().source()) {
if has_skip_comment(self.trailing_colon_comment, f.context().source()) {
write_suppressed_clause_header(self.header, f)?;
} else {
// Write a source map entry for the colon for range formatting to support formatting the clause header without

View file

@ -1,10 +1,9 @@
use ruff_formatter::write;
use ruff_python_ast::StmtAnnAssign;
use crate::comments::{SourceComment, SuppressionKind};
use crate::comments::SourceComment;
use crate::expression::parentheses::Parentheses;
use crate::expression::{has_parentheses, is_splittable_expression};
use crate::prelude::*;
use crate::preview::{
is_parenthesize_long_type_hints_enabled,
is_prefer_splitting_right_hand_side_of_assignments_enabled,
@ -13,6 +12,7 @@ use crate::statement::stmt_assign::{
AnyAssignmentOperator, AnyBeforeOperator, FormatStatementsLastExpression,
};
use crate::statement::trailing_semicolon;
use crate::{has_skip_comment, prelude::*};
#[derive(Default)]
pub struct FormatStmtAnnAssign;
@ -106,6 +106,6 @@ impl FormatNodeRule<StmtAnnAssign> for FormatStmtAnnAssign {
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -2,11 +2,11 @@ use ruff_formatter::prelude::{space, token};
use ruff_formatter::write;
use ruff_python_ast::StmtAssert;
use crate::comments::{SourceComment, SuppressionKind};
use crate::comments::SourceComment;
use crate::expression::maybe_parenthesize_expression;
use crate::expression::parentheses::Parenthesize;
use crate::prelude::*;
use crate::{has_skip_comment, prelude::*};
#[derive(Default)]
pub struct FormatStmtAssert;
@ -47,6 +47,6 @@ impl FormatNodeRule<StmtAssert> for FormatStmtAssert {
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -5,7 +5,7 @@ use ruff_python_ast::{
use crate::builders::parenthesize_if_expands;
use crate::comments::{
trailing_comments, Comments, LeadingDanglingTrailingComments, SourceComment, SuppressionKind,
trailing_comments, Comments, LeadingDanglingTrailingComments, SourceComment,
};
use crate::context::{NodeLevel, WithNodeLevel};
use crate::expression::parentheses::{
@ -16,12 +16,12 @@ use crate::expression::{
can_omit_optional_parentheses, has_own_parentheses, has_parentheses,
maybe_parenthesize_expression,
};
use crate::prelude::*;
use crate::preview::{
is_parenthesize_long_type_hints_enabled,
is_prefer_splitting_right_hand_side_of_assignments_enabled,
};
use crate::statement::trailing_semicolon;
use crate::{has_skip_comment, prelude::*};
#[derive(Default)]
pub struct FormatStmtAssign;
@ -110,7 +110,7 @@ impl FormatNodeRule<StmtAssign> for FormatStmtAssign {
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -1,15 +1,15 @@
use ruff_formatter::write;
use ruff_python_ast::StmtAugAssign;
use crate::comments::{SourceComment, SuppressionKind};
use crate::comments::SourceComment;
use crate::expression::parentheses::is_expression_parenthesized;
use crate::prelude::*;
use crate::preview::is_prefer_splitting_right_hand_side_of_assignments_enabled;
use crate::statement::stmt_assign::{
has_target_own_parentheses, AnyAssignmentOperator, AnyBeforeOperator,
FormatStatementsLastExpression,
};
use crate::statement::trailing_semicolon;
use crate::{has_skip_comment, prelude::*};
use crate::{AsFormat, FormatNodeRule};
#[derive(Default)]
@ -69,6 +69,6 @@ impl FormatNodeRule<StmtAugAssign> for FormatStmtAugAssign {
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -1,7 +1,7 @@
use ruff_python_ast::StmtBreak;
use crate::comments::{SourceComment, SuppressionKind};
use crate::prelude::*;
use crate::comments::SourceComment;
use crate::{has_skip_comment, prelude::*};
#[derive(Default)]
pub struct FormatStmtBreak;
@ -16,6 +16,6 @@ impl FormatNodeRule<StmtBreak> for FormatStmtBreak {
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -1,7 +1,7 @@
use ruff_python_ast::StmtContinue;
use crate::comments::{SourceComment, SuppressionKind};
use crate::prelude::*;
use crate::comments::SourceComment;
use crate::{has_skip_comment, prelude::*};
#[derive(Default)]
pub struct FormatStmtContinue;
@ -16,6 +16,6 @@ impl FormatNodeRule<StmtContinue> for FormatStmtContinue {
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -3,10 +3,10 @@ use ruff_python_ast::StmtDelete;
use ruff_text_size::Ranged;
use crate::builders::{parenthesize_if_expands, PyFormatterExtensions};
use crate::comments::{dangling_node_comments, SourceComment, SuppressionKind};
use crate::comments::{dangling_node_comments, SourceComment};
use crate::expression::maybe_parenthesize_expression;
use crate::expression::parentheses::Parenthesize;
use crate::prelude::*;
use crate::{has_skip_comment, prelude::*};
#[derive(Default)]
pub struct FormatStmtDelete;
@ -68,6 +68,6 @@ impl FormatNodeRule<StmtDelete> for FormatStmtDelete {
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -1,12 +1,12 @@
use ruff_python_ast as ast;
use ruff_python_ast::{Expr, Operator, StmtExpr};
use crate::comments::{SourceComment, SuppressionKind};
use crate::comments::SourceComment;
use crate::expression::maybe_parenthesize_expression;
use crate::expression::parentheses::Parenthesize;
use crate::prelude::*;
use crate::statement::trailing_semicolon;
use crate::{has_skip_comment, prelude::*};
#[derive(Default)]
pub struct FormatStmtExpr;
@ -36,7 +36,7 @@ impl FormatNodeRule<StmtExpr> for FormatStmtExpr {
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -2,7 +2,8 @@ use ruff_formatter::{format_args, write};
use ruff_python_ast::AstNode;
use ruff_python_ast::StmtGlobal;
use crate::comments::{SourceComment, SuppressionKind};
use crate::comments::SourceComment;
use crate::has_skip_comment;
use crate::prelude::*;
#[derive(Default)]
@ -53,6 +54,6 @@ impl FormatNodeRule<StmtGlobal> for FormatStmtGlobal {
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -1,8 +1,8 @@
use ruff_formatter::{format_args, write};
use ruff_python_ast::StmtImport;
use crate::comments::{SourceComment, SuppressionKind};
use crate::prelude::*;
use crate::comments::SourceComment;
use crate::{has_skip_comment, prelude::*};
#[derive(Default)]
pub struct FormatStmtImport;
@ -23,6 +23,6 @@ impl FormatNodeRule<StmtImport> for FormatStmtImport {
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -4,8 +4,9 @@ use ruff_python_ast::StmtImportFrom;
use ruff_text_size::Ranged;
use crate::builders::{parenthesize_if_expands, PyFormatterExtensions, TrailingComma};
use crate::comments::{SourceComment, SuppressionKind};
use crate::comments::SourceComment;
use crate::expression::parentheses::parenthesized;
use crate::has_skip_comment;
use crate::other::identifier::DotDelimitedIdentifier;
use crate::prelude::*;
@ -86,6 +87,6 @@ impl FormatNodeRule<StmtImportFrom> for FormatStmtImportFrom {
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -1,8 +1,8 @@
use ruff_python_ast::StmtIpyEscapeCommand;
use ruff_text_size::Ranged;
use crate::comments::{SourceComment, SuppressionKind};
use crate::prelude::*;
use crate::comments::SourceComment;
use crate::{has_skip_comment, prelude::*};
#[derive(Default)]
pub struct FormatStmtIpyEscapeCommand;
@ -17,6 +17,6 @@ impl FormatNodeRule<StmtIpyEscapeCommand> for FormatStmtIpyEscapeCommand {
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -2,7 +2,8 @@ use ruff_formatter::{format_args, write};
use ruff_python_ast::AstNode;
use ruff_python_ast::StmtNonlocal;
use crate::comments::{SourceComment, SuppressionKind};
use crate::comments::SourceComment;
use crate::has_skip_comment;
use crate::prelude::*;
#[derive(Default)]
@ -53,6 +54,6 @@ impl FormatNodeRule<StmtNonlocal> for FormatStmtNonlocal {
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -1,7 +1,7 @@
use ruff_python_ast::StmtPass;
use crate::comments::{SourceComment, SuppressionKind};
use crate::prelude::*;
use crate::comments::SourceComment;
use crate::{has_skip_comment, prelude::*};
#[derive(Default)]
pub struct FormatStmtPass;
@ -16,6 +16,6 @@ impl FormatNodeRule<StmtPass> for FormatStmtPass {
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -1,10 +1,10 @@
use ruff_formatter::write;
use ruff_python_ast::StmtRaise;
use crate::comments::{SourceComment, SuppressionKind};
use crate::comments::SourceComment;
use crate::expression::maybe_parenthesize_expression;
use crate::expression::parentheses::Parenthesize;
use crate::prelude::*;
use crate::{has_skip_comment, prelude::*};
#[derive(Default)]
pub struct FormatStmtRaise;
@ -48,6 +48,6 @@ impl FormatNodeRule<StmtRaise> for FormatStmtRaise {
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -1,10 +1,10 @@
use ruff_formatter::write;
use ruff_python_ast::{Expr, StmtReturn};
use crate::comments::{SourceComment, SuppressionKind};
use crate::comments::SourceComment;
use crate::expression::expr_tuple::TupleParentheses;
use crate::prelude::*;
use crate::statement::stmt_assign::FormatStatementsLastExpression;
use crate::{has_skip_comment, prelude::*};
#[derive(Default)]
pub struct FormatStmtReturn;
@ -45,6 +45,6 @@ impl FormatNodeRule<StmtReturn> for FormatStmtReturn {
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -1,12 +1,12 @@
use ruff_formatter::write;
use ruff_python_ast::StmtTypeAlias;
use crate::comments::{SourceComment, SuppressionKind};
use crate::prelude::*;
use crate::comments::SourceComment;
use crate::preview::is_prefer_splitting_right_hand_side_of_assignments_enabled;
use crate::statement::stmt_assign::{
AnyAssignmentOperator, AnyBeforeOperator, FormatStatementsLastExpression,
};
use crate::{has_skip_comment, prelude::*};
#[derive(Default)]
pub struct FormatStmtTypeAlias;
@ -52,6 +52,6 @@ impl FormatNodeRule<StmtTypeAlias> for FormatStmtTypeAlias {
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
has_skip_comment(trailing_comments, context.source())
}
}