Insert newline after nested function or class statements (#7946)

**Summary** Insert a newline after nested function and class
definitions, unless there is a trailing own line comment.

We need to e.g. format
```python
if platform.system() == "Linux":
    if sys.version > (3, 10):
        def f():
            print("old")
    else:
        def f():
            print("new")
    f()
```
as
```python
if platform.system() == "Linux":
    if sys.version > (3, 10):

        def f():
            print("old")

    else:

        def f():
            print("new")

    f()
```
even though `f()` is directly preceded by an if statement, not a
function or class definition. See the comments and fixtures for trailing
own line comment handling.

**Test Plan** I checked that the new content of `newlines.py` matches
black's formatting.

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
This commit is contained in:
konsti 2023-10-18 11:45:58 +02:00 committed by GitHub
parent dda4ceda71
commit 0c3123e07e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 430 additions and 77 deletions

View file

@ -4817,7 +4817,7 @@ pub enum AnyNodeRef<'a> {
ElifElseClause(&'a ast::ElifElseClause),
}
impl AnyNodeRef<'_> {
impl<'a> AnyNodeRef<'a> {
pub fn as_ptr(&self) -> NonNull<()> {
match self {
AnyNodeRef::ModModule(node) => NonNull::from(*node).cast(),
@ -5456,9 +5456,9 @@ impl AnyNodeRef<'_> {
)
}
pub fn visit_preorder<'a, V>(&'a self, visitor: &mut V)
pub fn visit_preorder<'b, V>(&'b self, visitor: &mut V)
where
V: PreorderVisitor<'a> + ?Sized,
V: PreorderVisitor<'b> + ?Sized,
{
match self {
AnyNodeRef::ModModule(node) => node.visit_preorder(visitor),
@ -5544,6 +5544,66 @@ impl AnyNodeRef<'_> {
AnyNodeRef::ElifElseClause(node) => node.visit_preorder(visitor),
}
}
/// The last child of the last branch, if the node has multiple branches.
pub fn last_child_in_body(&self) -> Option<AnyNodeRef<'a>> {
let body = match self {
AnyNodeRef::StmtFunctionDef(ast::StmtFunctionDef { body, .. })
| AnyNodeRef::StmtClassDef(ast::StmtClassDef { body, .. })
| AnyNodeRef::StmtWith(ast::StmtWith { body, .. })
| AnyNodeRef::MatchCase(MatchCase { body, .. })
| AnyNodeRef::ExceptHandlerExceptHandler(ast::ExceptHandlerExceptHandler {
body,
..
})
| AnyNodeRef::ElifElseClause(ast::ElifElseClause { body, .. }) => body,
AnyNodeRef::StmtIf(ast::StmtIf {
body,
elif_else_clauses,
..
}) => elif_else_clauses.last().map_or(body, |clause| &clause.body),
AnyNodeRef::StmtFor(ast::StmtFor { body, orelse, .. })
| AnyNodeRef::StmtWhile(ast::StmtWhile { body, orelse, .. }) => {
if orelse.is_empty() {
body
} else {
orelse
}
}
AnyNodeRef::StmtMatch(ast::StmtMatch { cases, .. }) => {
return cases.last().map(AnyNodeRef::from);
}
AnyNodeRef::StmtTry(ast::StmtTry {
body,
handlers,
orelse,
finalbody,
..
}) => {
if finalbody.is_empty() {
if orelse.is_empty() {
if handlers.is_empty() {
body
} else {
return handlers.last().map(AnyNodeRef::from);
}
} else {
orelse
}
} else {
finalbody
}
}
// Not a node that contains an indented child node.
_ => return None,
};
body.last().map(AnyNodeRef::from)
}
}
impl<'a> From<&'a ast::ModModule> for AnyNodeRef<'a> {