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

@ -273,8 +273,7 @@ impl<'a> Generator<'a> {
}
Stmt::ClassDef(ast::StmtClassDef {
name,
bases,
keywords,
arguments,
body,
decorator_list,
type_params,
@ -291,24 +290,25 @@ impl<'a> Generator<'a> {
self.p("class ");
self.p_id(name);
self.unparse_type_params(type_params);
let mut first = true;
for base in bases {
self.p_if(first, "(");
self.p_delim(&mut first, ", ");
self.unparse_expr(base, precedence::MAX);
}
for keyword in keywords {
self.p_if(first, "(");
self.p_delim(&mut first, ", ");
if let Some(arg) = &keyword.arg {
self.p_id(arg);
self.p("=");
} else {
self.p("**");
if let Some(arguments) = arguments {
self.p("(");
let mut first = true;
for base in &arguments.args {
self.p_delim(&mut first, ", ");
self.unparse_expr(base, precedence::MAX);
}
self.unparse_expr(&keyword.value, precedence::MAX);
for keyword in &arguments.keywords {
self.p_delim(&mut first, ", ");
if let Some(arg) = &keyword.arg {
self.p_id(arg);
self.p("=");
} else {
self.p("**");
}
self.unparse_expr(&keyword.value, precedence::MAX);
}
self.p(")");
}
self.p_if(!first, ")");
self.p(":");
});
self.body(body);
@ -1149,8 +1149,7 @@ impl<'a> Generator<'a> {
}
Expr::Call(ast::ExprCall {
func,
args,
keywords,
arguments,
range: _range,
}) => {
self.unparse_expr(func, precedence::MAX);
@ -1162,18 +1161,18 @@ impl<'a> Generator<'a> {
range: _range,
})],
[],
) = (args.as_slice(), keywords.as_slice())
) = (arguments.args.as_slice(), arguments.keywords.as_slice())
{
// Ensure that a single generator doesn't get double-parenthesized.
self.unparse_expr(elt, precedence::COMMA);
self.unparse_comp(generators);
} else {
let mut first = true;
for arg in args {
for arg in &arguments.args {
self.p_delim(&mut first, ", ");
self.unparse_expr(arg, precedence::COMMA);
}
for kw in keywords {
for kw in &arguments.keywords {
self.p_delim(&mut first, ", ");
if let Some(arg) = &kw.arg {
self.p_id(arg);