Introduce ExpressionRef (#6637)

## Summary

This PR revives the `ExpressionRef` concept introduced in
https://github.com/astral-sh/ruff/pull/5644, motivated by the change we
want to make in https://github.com/astral-sh/ruff/pull/6575 to narrow
the type of the expression that can be passed to `parenthesized_range`.

## Test Plan

`cargo test`
This commit is contained in:
Charlie Marsh 2023-08-17 10:07:16 -04:00 committed by GitHub
parent fa7442da2f
commit 1334232168
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 317 additions and 29 deletions

View file

@ -1,13 +1,13 @@
use std::iter;
use smallvec::SmallVec;
use ruff_formatter::{format_args, write, FormatOwnedWithRule, FormatRefWithRule};
use ruff_python_ast::node::AnyNodeRef;
use ruff_python_ast::{
Constant, Expr, ExprAttribute, ExprBinOp, ExprConstant, ExprUnaryOp, Operator, StringConstant,
UnaryOp,
};
use smallvec::SmallVec;
use ruff_formatter::{format_args, write, FormatOwnedWithRule, FormatRefWithRule};
use ruff_python_ast::node::{AnyNodeRef, AstNode};
use crate::comments::{trailing_comments, trailing_node_comments, SourceComment};
use crate::expression::expr_constant::ExprConstantLayout;
@ -73,10 +73,7 @@ impl FormatNodeRule<ExprBinOp> for FormatExprBinOp {
let binary_chain: SmallVec<[&ExprBinOp; 4]> =
iter::successors(Some(item), |parent| {
parent.left.as_bin_op_expr().and_then(|bin_expression| {
if is_expression_parenthesized(
bin_expression.as_any_node_ref(),
source,
) {
if is_expression_parenthesized(bin_expression.into(), source) {
None
} else {
Some(bin_expression)

View file

@ -1,12 +1,13 @@
use ruff_formatter::{write, FormatOwnedWithRule, FormatRefWithRule, FormatRuleWithOptions};
use ruff_python_ast::node::AnyNodeRef;
use ruff_python_ast::{BoolOp, Expr, ExprBoolOp};
use crate::comments::leading_comments;
use crate::expression::parentheses::{
in_parentheses_only_group, in_parentheses_only_soft_line_break_or_space, NeedsParentheses,
OptionalParentheses,
};
use crate::prelude::*;
use ruff_formatter::{write, FormatOwnedWithRule, FormatRefWithRule, FormatRuleWithOptions};
use ruff_python_ast::node::{AnyNodeRef, AstNode};
use ruff_python_ast::{BoolOp, Expr, ExprBoolOp};
use super::parentheses::is_expression_parenthesized;
@ -95,10 +96,7 @@ impl Format<PyFormatContext<'_>> for FormatValue<'_> {
fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> {
match self.value {
Expr::BoolOp(bool_op)
if !is_expression_parenthesized(
bool_op.as_any_node_ref(),
f.context().source(),
) =>
if !is_expression_parenthesized(bool_op.into(), f.context().source()) =>
{
// Mark chained boolean operations e.g. `x and y or z` and avoid creating a new group
write!(f, [bool_op.format().with_options(BoolOpLayout::Chained)])

View file

@ -6,7 +6,7 @@ use ruff_formatter::{
use ruff_python_ast as ast;
use ruff_python_ast::node::AnyNodeRef;
use ruff_python_ast::visitor::preorder::{walk_expr, PreorderVisitor};
use ruff_python_ast::{Expr, Operator};
use ruff_python_ast::{Expr, ExpressionRef, Operator};
use crate::builders::parenthesize_if_expands;
use crate::context::{NodeLevel, WithNodeLevel};
@ -472,7 +472,7 @@ impl<'input> PreorderVisitor<'input> for CanOmitOptionalParenthesesVisitor<'inpu
self.last = Some(expr);
// Rule only applies for non-parenthesized expressions.
if is_expression_parenthesized(AnyNodeRef::from(expr), self.context.source()) {
if is_expression_parenthesized(expr.into(), self.context.source()) {
self.any_parenthesized_expressions = true;
} else {
self.visit_subexpression(expr);
@ -526,12 +526,12 @@ pub enum CallChainLayout {
}
impl CallChainLayout {
pub(crate) fn from_expression(mut expr: AnyNodeRef, source: &str) -> Self {
pub(crate) fn from_expression(mut expr: ExpressionRef, source: &str) -> Self {
let mut attributes_after_parentheses = 0;
loop {
match expr {
AnyNodeRef::ExprAttribute(ast::ExprAttribute { value, .. }) => {
expr = AnyNodeRef::from(value.as_ref());
ExpressionRef::Attribute(ast::ExprAttribute { value, .. }) => {
expr = ExpressionRef::from(value.as_ref());
// ```
// f().g
// ^^^ value
@ -554,9 +554,9 @@ impl CallChainLayout {
// ^^^^^^^^^^ expr
// ^^^^ value
// ```
AnyNodeRef::ExprCall(ast::ExprCall { func: inner, .. })
| AnyNodeRef::ExprSubscript(ast::ExprSubscript { value: inner, .. }) => {
expr = AnyNodeRef::from(inner.as_ref());
ExpressionRef::Call(ast::ExprCall { func: inner, .. })
| ExpressionRef::Subscript(ast::ExprSubscript { value: inner, .. }) => {
expr = ExpressionRef::from(inner.as_ref());
}
_ => {
// We to format the following in fluent style:
@ -586,7 +586,7 @@ impl CallChainLayout {
/// formatting
pub(crate) fn apply_in_node<'a>(
self,
item: impl Into<AnyNodeRef<'a>>,
item: impl Into<ExpressionRef<'a>>,
f: &mut PyFormatter,
) -> CallChainLayout {
match self {
@ -627,7 +627,7 @@ fn has_parentheses(expr: &Expr, context: &PyFormatContext) -> Option<OwnParenthe
// Otherwise, if the node lacks parentheses (e.g., `(1)`) or only contains empty parentheses
// (e.g., `([])`), we need to check for surrounding parentheses.
if is_expression_parenthesized(AnyNodeRef::from(expr), context.source()) {
if is_expression_parenthesized(expr.into(), context.source()) {
return Some(OwnParentheses::NonEmpty);
}

View file

@ -1,7 +1,7 @@
use ruff_formatter::prelude::tag::Condition;
use ruff_formatter::{format_args, write, Argument, Arguments};
use ruff_python_ast::node::AnyNodeRef;
use ruff_python_ast::Ranged;
use ruff_python_ast::{ExpressionRef, Ranged};
use ruff_python_trivia::{first_non_trivia_token, SimpleToken, SimpleTokenKind, SimpleTokenizer};
use crate::comments::{
@ -80,7 +80,7 @@ pub enum Parentheses {
Never,
}
pub(crate) fn is_expression_parenthesized(expr: AnyNodeRef, contents: &str) -> bool {
pub(crate) fn is_expression_parenthesized(expr: ExpressionRef, contents: &str) -> bool {
// First test if there's a closing parentheses because it tends to be cheaper.
if matches!(
first_non_trivia_token(expr.end(), contents),
@ -378,7 +378,7 @@ impl Format<PyFormatContext<'_>> for FormatEmptyParenthesized<'_> {
#[cfg(test)]
mod tests {
use ruff_python_ast::node::AnyNodeRef;
use ruff_python_ast::ExpressionRef;
use ruff_python_parser::parse_expression;
use crate::expression::parentheses::is_expression_parenthesized;
@ -388,7 +388,7 @@ mod tests {
let expression = r#"(b().c("")).d()"#;
let expr = parse_expression(expression, "<filename>").unwrap();
assert!(!is_expression_parenthesized(
AnyNodeRef::from(&expr),
ExpressionRef::from(&expr),
expression
));
}