diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/fstring.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/fstring.py new file mode 100644 index 0000000000..2769203823 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/fstring.py @@ -0,0 +1,26 @@ +( + f'{one}' + f'{two}' +) + + +rf"Not-so-tricky \"quote" + +# Regression test for fstrings dropping comments +result_f = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n' + ' f()\n' + f' File "{__file__}", line {lineno_f+1}, in f\n' + ' f()\n' + f' File "{__file__}", line {lineno_f+1}, in f\n' + ' f()\n' + f' File "{__file__}", line {lineno_f+1}, in f\n' + ' f()\n' + # XXX: The following line changes depending on whether the tests + # are run through the interactive interpreter or with -m + # It also varies depending on the platform (stack size) + # Fortunately, we don't care about exactness here, so we use regex + r' \[Previous line repeated (\d+) more times\]' '\n' + 'RecursionError: maximum recursion depth exceeded\n' +) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/joined_string.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/joined_string.py deleted file mode 100644 index 8b6af32d0b..0000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/joined_string.py +++ /dev/null @@ -1,7 +0,0 @@ -( - f'{one}' - f'{two}' -) - - -rf"Not-so-tricky \"quote" diff --git a/crates/ruff_python_formatter/src/comments/placement.rs b/crates/ruff_python_formatter/src/comments/placement.rs index 686415efc9..855930ba7c 100644 --- a/crates/ruff_python_formatter/src/comments/placement.rs +++ b/crates/ruff_python_formatter/src/comments/placement.rs @@ -73,6 +73,13 @@ pub(super) fn place_comment<'a>( handle_leading_class_with_decorators_comment(comment, class_def) } AnyNodeRef::StmtImportFrom(import_from) => handle_import_from_comment(comment, import_from), + AnyNodeRef::ExprConstant(_) => { + if let Some(AnyNodeRef::ExprFString(fstring)) = comment.enclosing_parent() { + CommentPlacement::dangling(fstring, comment) + } else { + CommentPlacement::Default(comment) + } + } AnyNodeRef::ExprList(_) | AnyNodeRef::ExprSet(_) | AnyNodeRef::ExprGeneratorExp(_) diff --git a/crates/ruff_python_formatter/src/comments/visitor.rs b/crates/ruff_python_formatter/src/comments/visitor.rs index a70b62d0fd..f779820a35 100644 --- a/crates/ruff_python_formatter/src/comments/visitor.rs +++ b/crates/ruff_python_formatter/src/comments/visitor.rs @@ -77,6 +77,7 @@ impl<'ast> PreorderVisitor<'ast> for CommentsVisitor<'ast> { enclosing: enclosing_node, preceding: self.preceding_node, following: Some(node), + parent: self.parents.iter().rev().nth(1).copied(), line_position: text_position(*comment_range, self.source_code), slice: self.source_code.slice(*comment_range), }; @@ -117,6 +118,7 @@ impl<'ast> PreorderVisitor<'ast> for CommentsVisitor<'ast> { let comment = DecoratedComment { enclosing: node, + parent: self.parents.last().copied(), preceding: self.preceding_node, following: None, line_position: text_position(*comment_range, self.source_code), @@ -179,6 +181,7 @@ pub(super) struct DecoratedComment<'a> { enclosing: AnyNodeRef<'a>, preceding: Option>, following: Option>, + parent: Option>, line_position: CommentLinePosition, slice: SourceCodeSlice, } @@ -204,6 +207,11 @@ impl<'a> DecoratedComment<'a> { self.enclosing } + /// Returns the parent of the enclosing node, if any + pub(super) fn enclosing_parent(&self) -> Option> { + self.parent + } + /// Returns the slice into the source code. pub(super) fn slice(&self) -> &SourceCodeSlice { &self.slice diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring.py.snap new file mode 100644 index 0000000000..90a4b1e396 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring.py.snap @@ -0,0 +1,64 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/fstring.py +--- +## Input +```py +( + f'{one}' + f'{two}' +) + + +rf"Not-so-tricky \"quote" + +# Regression test for fstrings dropping comments +result_f = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n' + ' f()\n' + f' File "{__file__}", line {lineno_f+1}, in f\n' + ' f()\n' + f' File "{__file__}", line {lineno_f+1}, in f\n' + ' f()\n' + f' File "{__file__}", line {lineno_f+1}, in f\n' + ' f()\n' + # XXX: The following line changes depending on whether the tests + # are run through the interactive interpreter or with -m + # It also varies depending on the platform (stack size) + # Fortunately, we don't care about exactness here, so we use regex + r' \[Previous line repeated (\d+) more times\]' '\n' + 'RecursionError: maximum recursion depth exceeded\n' +) +``` + +## Output +```py +(f"{one}" f"{two}") + + +rf'Not-so-tricky "quote' + +# Regression test for fstrings dropping comments +result_f = ( + "Traceback (most recent call last):\n" + f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n' + " f()\n" + f' File "{__file__}", line {lineno_f+1}, in f\n' + " f()\n" + f' File "{__file__}", line {lineno_f+1}, in f\n' + " f()\n" + f' File "{__file__}", line {lineno_f+1}, in f\n' + " f()\n" + # XXX: The following line changes depending on whether the tests + # are run through the interactive interpreter or with -m + # It also varies depending on the platform (stack size) + # Fortunately, we don't care about exactness here, so we use regex + r" \[Previous line repeated (\d+) more times\]" + "\n" + "RecursionError: maximum recursion depth exceeded\n" +) +``` + + + diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__joined_string.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__joined_string.py.snap deleted file mode 100644 index d9f8f4313d..0000000000 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__joined_string.py.snap +++ /dev/null @@ -1,25 +0,0 @@ ---- -source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/joined_string.py ---- -## Input -```py -( - f'{one}' - f'{two}' -) - - -rf"Not-so-tricky \"quote" -``` - -## Output -```py -(f"{one}" f"{two}") - - -rf'Not-so-tricky "quote' -``` - - -