Introduce an Arguments AST node for function calls and class definitions (#6259)

## Summary

This PR adds a new `Arguments` AST node, which we can use for function
calls and class definitions.

The `Arguments` node spans from the left (open) to right (close)
parentheses inclusive.

In the case of classes, the `Arguments` is an option, to differentiate
between:

```python
# None
class C: ...

# Some, with empty vectors
class C(): ...
```

In this PR, we don't really leverage this change (except that a few
rules get much simpler, since we don't need to lex to find the start and
end ranges of the parentheses, e.g.,
`crates/ruff/src/rules/pyupgrade/rules/lru_cache_without_parameters.rs`,
`crates/ruff/src/rules/pyupgrade/rules/unnecessary_class_parentheses.rs`).

In future PRs, this will be especially helpful for the formatter, since
we can track comments enclosed on the node itself.

## Test Plan

`cargo test`
This commit is contained in:
Charlie Marsh 2023-08-02 10:01:13 -04:00 committed by GitHub
parent 0d62ad2480
commit 981e64f82b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
107 changed files with 24258 additions and 23835 deletions

View file

@ -1,4 +1,4 @@
use ruff_python_ast::{Expr, ExprCall, Ranged};
use ruff_python_ast::{Arguments, Expr, ExprCall, Ranged};
use ruff_text_size::{TextRange, TextSize};
use crate::builders::empty_parenthesized_with_dangling_comments;
@ -21,8 +21,12 @@ impl FormatNodeRule<ExprCall> for FormatExprCall {
let ExprCall {
range: _,
func,
args,
keywords,
arguments:
Arguments {
args,
keywords,
range: _,
},
} = item;
// We have a case with `f()` without any argument, which is a special case because we can

View file

@ -356,8 +356,7 @@ impl<'input> CanOmitOptionalParenthesesVisitor<'input> {
Expr::Call(ast::ExprCall {
range: _,
func,
args: _,
keywords: _,
arguments: _,
}) => {
self.any_parenthesized_expressions = true;
// Only walk the function, the arguments are always parenthesized

View file

@ -1,4 +1,4 @@
use ruff_python_ast::{Ranged, StmtClassDef};
use ruff_python_ast::{Arguments, Ranged, StmtClassDef};
use ruff_text_size::TextRange;
use ruff_formatter::write;
@ -17,8 +17,7 @@ impl FormatNodeRule<StmtClassDef> for FormatStmtClassDef {
let StmtClassDef {
range: _,
name,
bases,
keywords,
arguments,
body,
type_params: _,
decorator_list,
@ -34,7 +33,12 @@ impl FormatNodeRule<StmtClassDef> for FormatStmtClassDef {
write!(f, [text("class"), space(), name.format()])?;
if !(bases.is_empty() && keywords.is_empty()) {
if arguments
.as_ref()
.is_some_and(|Arguments { args, keywords, .. }| {
!(args.is_empty() && keywords.is_empty())
})
{
parenthesized(
"(",
&FormatInheritanceClause {
@ -75,12 +79,19 @@ struct FormatInheritanceClause<'a> {
impl Format<PyFormatContext<'_>> for FormatInheritanceClause<'_> {
fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
let StmtClassDef {
bases,
keywords,
arguments:
Some(Arguments {
args: bases,
keywords,
..
}),
name,
body,
..
} = self.class_definition;
} = self.class_definition
else {
return Ok(());
};
let source = f.context().source();