Skip to content

Commit

Permalink
Remove struct
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Aug 17, 2023
1 parent 658b550 commit 7ca7d7b
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 62 deletions.
8 changes: 4 additions & 4 deletions crates/ruff/src/rules/pycodestyle/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use unicode_width::UnicodeWidthStr;

use ruff_python_ast::node::AnyNodeRef;
use ruff_python_ast::parenthesize::ParenthesizedExpression;
use ruff_python_ast::parenthesize::parenthesized_range;
use ruff_python_ast::{CmpOp, Expr, Ranged};
use ruff_source_file::{Line, Locator};
use ruff_text_size::{TextLen, TextRange};
Expand All @@ -25,7 +25,7 @@ pub(super) fn generate_comparison(

// Add the left side of the comparison.
contents.push_str(locator.slice(
ParenthesizedExpression::from_expr(left.into(), parent, locator.contents()).range(),
parenthesized_range(left.into(), parent, locator.contents()).unwrap_or(left.range()),
));

for (op, comparator) in ops.iter().zip(comparators) {
Expand All @@ -46,8 +46,8 @@ pub(super) fn generate_comparison(
// Add the right side of the comparison.
contents.push_str(
locator.slice(
ParenthesizedExpression::from_expr(comparator.into(), parent, locator.contents())
.range(),
parenthesized_range(comparator.into(), parent, locator.contents())
.unwrap_or(comparator.range()),
),
);
}
Expand Down
3 changes: 2 additions & 1 deletion crates/ruff/src/rules/pycodestyle/rules/not_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ pub(crate) fn not_tests(checker: &mut Checker, unary_op: &ast::ExprUnaryOp) {
ops,
comparators,
range: _,
}) = unary_op.operand.as_ref() else {
}) = unary_op.operand.as_ref()
else {
return;
};

Expand Down
55 changes: 18 additions & 37 deletions crates/ruff_python_ast/src/parenthesize.rs
Original file line number Diff line number Diff line change
@@ -1,48 +1,29 @@
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
use ruff_text_size::{TextRange, TextSize};
use std::ops::Sub;

use crate::node::AnyNodeRef;
use crate::Ranged;

/// A wrapper around an expression that may be parenthesized.
#[derive(Debug)]
pub struct ParenthesizedExpression<'a> {
/// The underlying AST node.
expr: AnyNodeRef<'a>,
/// The range of the expression including parentheses, if the expression is parenthesized;
/// or `None`, if the expression is not parenthesized.
range: Option<TextRange>,
}

impl<'a> ParenthesizedExpression<'a> {
/// Given an expression and its parent, returns a parenthesized expression.
pub fn from_expr(expr: AnyNodeRef<'a>, parent: AnyNodeRef<'a>, contents: &str) -> Self {
Self {
expr,
range: parenthesized_range(expr, parent, contents),
}
}

/// Returns `true` if the expression is parenthesized.
pub fn is_parenthesized(&self) -> bool {
self.range.is_some()
}
}

impl Ranged for ParenthesizedExpression<'_> {
fn range(&self) -> TextRange {
self.range.unwrap_or_else(|| self.expr.range())
}
}
use crate::{ExpressionRef, Ranged};

/// Returns the [`TextRange`] of a given expression including parentheses, if the expression is
/// parenthesized; or `None`, if the expression is not parenthesized.
fn parenthesized_range(expr: AnyNodeRef, parent: AnyNodeRef, contents: &str) -> Option<TextRange> {
// If the parent is an `arguments` node, then the range of the expression includes the closing
// parenthesis, so exclude it from our test range.
pub fn parenthesized_range(
expr: ExpressionRef,
parent: AnyNodeRef,
contents: &str,
) -> Option<TextRange> {
// If the parent is a node that brings its own parentheses, exclude the closing parenthesis
// from our search range. Otherwise, we risk matching on calls, like `func(x)`, for which
// the open and close parentheses are part of the `Arguments` node.
//
// There are a few other nodes that may have their own parentheses, but are fine to exclude:
// - `Parameters`: The parameters to a function definition. Any expressions would represent
// default arguments, and so must be preceded by _at least_ the parameter name. As such,
// we won't mistake any parentheses for the opening and closing parentheses on the
// `Parameters` node itself.
// - `Tuple`: The elements of a tuple. The only risk is a single-element tuple (e.g., `(x,)`),
// which must have a trailing comma anyway.
let exclusive_parent_end = if parent.is_arguments() {
parent.end().sub(TextSize::new(1))
parent.end() - TextSize::new(1)
} else {
parent.end()
};
Expand Down
36 changes: 16 additions & 20 deletions crates/ruff_python_ast/tests/parenthesize.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use ruff_python_ast::parenthesize::ParenthesizedExpression;
use ruff_python_ast::parenthesize::parenthesized_range;
use ruff_python_parser::parse_expression;

#[test]
Expand All @@ -9,20 +9,20 @@ fn test_parenthesized_name() {
let bin_op = expr.as_bin_op_expr().unwrap();
let name = bin_op.left.as_ref();

let parenthesized = ParenthesizedExpression::from_expr(name.into(), bin_op.into(), source_code);
assert!(parenthesized.is_parenthesized());
let parenthesized = parenthesized_range(name.into(), bin_op.into(), source_code);
assert!(parenthesized.is_some());
}

#[test]
fn test_un_parenthesized_name() {
fn test_non_parenthesized_name() {
let source_code = r#"x + 1"#;
let expr = parse_expression(source_code, "<filename>").unwrap();

let bin_op = expr.as_bin_op_expr().unwrap();
let name = bin_op.left.as_ref();

let parenthesized = ParenthesizedExpression::from_expr(name.into(), bin_op.into(), source_code);
assert!(!parenthesized.is_parenthesized());
let parenthesized = parenthesized_range(name.into(), bin_op.into(), source_code);
assert!(parenthesized.is_none());
}

#[test]
Expand All @@ -34,23 +34,21 @@ fn test_parenthesized_argument() {
let arguments = &call.arguments;
let argument = arguments.args.first().unwrap();

let parenthesized =
ParenthesizedExpression::from_expr(argument.into(), arguments.into(), source_code);
assert!(parenthesized.is_parenthesized());
let parenthesized = parenthesized_range(argument.into(), arguments.into(), source_code);
assert!(parenthesized.is_some());
}

#[test]
fn test_unparenthesized_argument() {
fn test_non_parenthesized_argument() {
let source_code = r#"f(a)"#;
let expr = parse_expression(source_code, "<filename>").unwrap();

let call = expr.as_call_expr().unwrap();
let arguments = &call.arguments;
let argument = arguments.args.first().unwrap();

let parenthesized =
ParenthesizedExpression::from_expr(argument.into(), arguments.into(), source_code);
assert!(!parenthesized.is_parenthesized());
let parenthesized = parenthesized_range(argument.into(), arguments.into(), source_code);
assert!(parenthesized.is_none());
}

#[test]
Expand All @@ -61,20 +59,18 @@ fn test_parenthesized_tuple_member() {
let tuple = expr.as_tuple_expr().unwrap();
let member = tuple.elts.last().unwrap();

let parenthesized =
ParenthesizedExpression::from_expr(member.into(), tuple.into(), source_code);
assert!(parenthesized.is_parenthesized());
let parenthesized = parenthesized_range(member.into(), tuple.into(), source_code);
assert!(parenthesized.is_some());
}

#[test]
fn test_unparenthesized_tuple_member() {
fn test_non_parenthesized_tuple_member() {
let source_code = r#"(a, b)"#;
let expr = parse_expression(source_code, "<filename>").unwrap();

let tuple = expr.as_tuple_expr().unwrap();
let member = tuple.elts.last().unwrap();

let parenthesized =
ParenthesizedExpression::from_expr(member.into(), tuple.into(), source_code);
assert!(!parenthesized.is_parenthesized());
let parenthesized = parenthesized_range(member.into(), tuple.into(), source_code);
assert!(parenthesized.is_none());
}

0 comments on commit 7ca7d7b

Please sign in to comment.