Skip to content

Commit be4c041

Browse files
fables-talesclaude
andauthored
make paren stripping work good (#787)
* Revert "Revert "test"" This reverts commit f54d5af. * bees * bees * Re-add paren unwrapping logic lost during rebase The code that calls unwrap_single_arg_paren was lost during the rebase onto trunk. This restores the logic that unwraps simple parenthesized expressions in method arguments (e.g., `foo (1)` -> `foo(1)`) while preserving parens around keyword expressions that would cause syntax errors (e.g., `foo (a if b)` -> `foo((a if b))`). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add recursive unwrapping of nested redundant parentheses When a method call has nested parentheses like `foo ((1))` or `foo (((1)))`, we now recursively unwrap all levels to produce `foo(1)`. Inner parens are still preserved when they contain keyword expressions that would cause syntax errors. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Fix clippy collapsible_if warning Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add ParenExprClose token to prevent blank lines before closing paren Introduces a new token type specifically for closing multiline parenthesized expressions. This prevents the render queue from inserting a blank line between `end` and `)` in constructs like `foo((case x when 1 then :one end))`. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent dc343e5 commit be4c041

10 files changed

+366
-8
lines changed

fixtures/small/paren_expr_calls_expected.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
a((1))
1+
a(1)
22
other_cool_method((a + b).round(4))
33

44
# rubocop:disable PrisonGuard/PrivateModule
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Test cases for nested redundant parentheses
2+
# Multiple levels of unnecessary parens should be stripped
3+
4+
# Double nested
5+
foo ((1))
6+
bar ((a + b))
7+
8+
# Triple nested
9+
foo (((1)))
10+
bar (((x)))
11+
12+
# Mixed with method chains
13+
foo ((bar.baz))
14+
15+
# Nested but inner contains keyword - should preserve inner parens
16+
foo ((a if b))
17+
bar ((x rescue y))
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Test cases for nested redundant parentheses
2+
# Multiple levels of unnecessary parens should be stripped
3+
4+
# Double nested
5+
foo(1)
6+
bar(a + b)
7+
8+
# Triple nested
9+
foo(1)
10+
bar(x)
11+
12+
# Mixed with method chains
13+
foo(bar.baz)
14+
15+
# Nested but inner contains keyword - should preserve inner parens
16+
foo((a if b))
17+
bar((x rescue y))
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Test cases for parentheses that MUST be preserved to maintain correct semantics
2+
# These contain Ruby keywords that would cause syntax errors if parens were removed
3+
4+
# Modifier if - `foo (a if b)` must become `foo((a if b))` not `foo(a if b)` (syntax error)
5+
foo (a if b)
6+
method_call (value if condition)
7+
8+
# Modifier unless
9+
foo (a unless b)
10+
method_call (value unless condition)
11+
12+
# Modifier while
13+
foo (a while b)
14+
15+
# Modifier until
16+
foo (a until b)
17+
18+
# Inline rescue - `foo (x rescue y)` must become `foo((x rescue y))`
19+
foo (risky_call rescue fallback)
20+
method_call (might_fail rescue nil)
21+
22+
# `and` keyword (low precedence) - `foo (a and b)` must become `foo((a and b))`
23+
foo (a and b)
24+
check (condition and action)
25+
26+
# `or` keyword (low precedence) - `foo (a or b)` must become `foo((a or b))`
27+
foo (a or b)
28+
provide (default or fallback)
29+
30+
# case expressions
31+
foo (case x
32+
when 1 then :one
33+
when 2 then :two
34+
else :other
35+
end)
36+
37+
# begin/end blocks
38+
foo (begin
39+
risky_operation
40+
rescue
41+
fallback
42+
end)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Test cases for parentheses that MUST be preserved to maintain correct semantics
2+
# These contain Ruby keywords that would cause syntax errors if parens were removed
3+
4+
# Modifier if - `foo (a if b)` must become `foo((a if b))` not `foo(a if b)` (syntax error)
5+
foo((a if b))
6+
method_call((value if condition))
7+
8+
# Modifier unless
9+
foo((a unless b))
10+
method_call((value unless condition))
11+
12+
# Modifier while
13+
foo((a while b))
14+
15+
# Modifier until
16+
foo((a until b))
17+
18+
# Inline rescue - `foo (x rescue y)` must become `foo((x rescue y))`
19+
foo((risky_call rescue fallback))
20+
method_call((might_fail rescue nil))
21+
22+
# `and` keyword (low precedence) - `foo (a and b)` must become `foo((a and b))`
23+
foo((a and b))
24+
check((condition and action))
25+
26+
# `or` keyword (low precedence) - `foo (a or b)` must become `foo((a or b))`
27+
foo((a or b))
28+
provide((default or fallback))
29+
30+
# case expressions
31+
foo(
32+
(
33+
case x
34+
when 1
35+
:one
36+
when 2
37+
:two
38+
else
39+
:other
40+
end
41+
)
42+
)
43+
44+
# begin/end blocks
45+
foo(
46+
(
47+
begin
48+
risky_operation
49+
rescue
50+
fallback
51+
end
52+
)
53+
)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Test cases for parentheses that CAN be safely unwrapped when used as method arguments
2+
# These are simple expressions where `method (expr)` becomes `method(expr)`
3+
4+
# Simple literals
5+
foo (1)
6+
foo ("hello")
7+
foo (:symbol)
8+
foo (nil)
9+
foo (true)
10+
11+
# Simple variables
12+
foo (bar)
13+
foo (BAR)
14+
foo (@bar)
15+
16+
# Simple method calls
17+
foo (bar.baz)
18+
19+
# Array and hash literals
20+
foo ([1, 2, 3])
21+
foo ({a: 1})
22+
23+
# Arithmetic expressions (these bind tighter than method args)
24+
foo (a + b)
25+
foo (a * b)
26+
27+
# Comparison expressions
28+
foo (a == b)
29+
foo (a < b)
30+
31+
# Logical operators (high precedence &&, ||)
32+
foo (a && b)
33+
foo (a || b)
34+
35+
# Range expressions
36+
foo (1..10)
37+
38+
# Ternary without keywords
39+
foo (condition ? a : b)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Test cases for parentheses that CAN be safely unwrapped when used as method arguments
2+
# These are simple expressions where `method (expr)` becomes `method(expr)`
3+
4+
# Simple literals
5+
foo(1)
6+
foo("hello")
7+
foo(:symbol)
8+
foo(nil)
9+
foo(true)
10+
11+
# Simple variables
12+
foo(bar)
13+
foo(BAR)
14+
foo(@bar)
15+
16+
# Simple method calls
17+
foo(bar.baz)
18+
19+
# Array and hash literals
20+
foo([1, 2, 3])
21+
foo({a: 1})
22+
23+
# Arithmetic expressions (these bind tighter than method args)
24+
foo(a + b)
25+
foo(a * b)
26+
27+
# Comparison expressions
28+
foo(a == b)
29+
foo(a < b)
30+
31+
# Logical operators (high precedence &&, ||)
32+
foo(a && b)
33+
foo(a || b)
34+
35+
# Range expressions
36+
foo(1..10)
37+
38+
# Ternary without keywords
39+
foo(condition ? a : b)

0 commit comments

Comments
 (0)