Class: Ryac::Pipeline::ParenOptimizer
- Defined in:
- lib/ryac/pipeline/paren_optimizer.rb,
sig/ryac/pipeline/paren_optimizer.rbs
Overview
Removes unnecessary parentheses from method calls at statement level. Operates as a source patcher: Prism.parse → walk AST → collect patches → apply. Only removes parens; never adds them.
Instance Method Summary collapse
- #add_paren_removal_patches(open_loc, close_loc, patches) ⇒ void
- #collect(ctx, patches) ⇒ void
- #try_remove_call_parens(node, source, patches) ⇒ void
- #try_remove_yield_parens(node, patches) ⇒ void
- #walk(node, source, patches, statement_level:) ⇒ void
- #walk_call_children(node, source, patches) ⇒ void
- #walk_if(node, source, patches) ⇒ void
Methods inherited from Stage
#analysis, #analysis_options, #call, #finish, #fixpoint?, #needs_analysis?, new
Methods included from SourcePatcher
#apply_patches, #mk, #src, #verify_parses
Instance Method Details
#add_paren_removal_patches(open_loc, close_loc, patches) ⇒ void
This method returns an undefined value.
154 155 156 157 |
# File 'lib/ryac/pipeline/paren_optimizer.rb', line 154 def add_paren_removal_patches(open_loc, close_loc, patches) patches << { start: open_loc.start_offset, end: open_loc.end_offset, replacement: ' ' } patches << { start: close_loc.start_offset, end: close_loc.end_offset, replacement: '' } end |
#collect(ctx, patches) ⇒ void
This method returns an undefined value.
9 10 11 |
# File 'lib/ryac/pipeline/paren_optimizer.rb', line 9 def collect(ctx, patches) walk(ctx.ast, ctx.source, patches, statement_level: true) end |
#try_remove_call_parens(node, source, patches) ⇒ void
This method returns an undefined value.
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/ryac/pipeline/paren_optimizer.rb', line 129 def try_remove_call_parens(node, source, patches) return unless node.opening_loc return unless AstUtils.can_omit_parens?(node) # Don't remove parens from calls with keyword args in modifier context. # After hash shorthand (`a:` for `a:a`), `foo a: if cond` is ambiguous. raw_args = node.arguments&.arguments || [] if raw_args.any? { |a| a.is_a?(Prism::KeywordHashNode) } # opening_loc presence (checked above) implies the paired closing_loc after = source.byteslice(node.closing_loc.end_offset, 10) # steep:ignore NoMethod return if after&.match?(/\A (?:if|unless|while|until) /) end add_paren_removal_patches(node.opening_loc, node.closing_loc, patches) # steep:ignore ArgumentTypeMismatch end |
#try_remove_yield_parens(node, patches) ⇒ void
This method returns an undefined value.
145 146 147 148 149 150 151 152 |
# File 'lib/ryac/pipeline/paren_optimizer.rb', line 145 def try_remove_yield_parens(node, patches) return unless node.lparen_loc args = node.arguments&.arguments || [] return if args.empty? # lparen_loc presence (checked above) implies the paired rparen_loc add_paren_removal_patches(node.lparen_loc, node.rparen_loc, patches) # steep:ignore ArgumentTypeMismatch end |
#walk(node, source, patches, statement_level:) ⇒ void
This method returns an undefined value.
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/ryac/pipeline/paren_optimizer.rb', line 15 def walk(node, source, patches, statement_level:) case node when Prism::ProgramNode walk(node.statements, source, patches, statement_level: true) when Prism::StatementsNode node.body.each { |child| walk(child, source, patches, statement_level: statement_level) } when Prism::CallNode try_remove_call_parens(node, source, patches) if statement_level walk_call_children(node, source, patches) when Prism::YieldNode try_remove_yield_parens(node, patches) if statement_level node.arguments&.arguments&.each { |arg| walk(arg, source, patches, statement_level: false) } when Prism::IfNode walk_if(node, source, patches) when Prism::UnlessNode walk(node.predicate, source, patches, statement_level: false) walk(node.statements, source, patches, statement_level: true) if node.statements walk(node.else_clause, source, patches, statement_level: true) if node.else_clause when Prism::ElseNode walk(node.statements, source, patches, statement_level: statement_level) if node.statements when Prism::WhileNode, Prism::UntilNode walk(node.predicate, source, patches, statement_level: false) walk(node.statements, source, patches, statement_level: true) if node.statements when Prism::ForNode walk(node.collection, source, patches, statement_level: false) walk(node.statements, source, patches, statement_level: true) if node.statements when Prism::DefNode walk(node.body, source, patches, statement_level: true) if node.body when Prism::ClassNode, Prism::ModuleNode, Prism::SingletonClassNode walk(node.body, source, patches, statement_level: true) if node.body when Prism::BeginNode walk(node.statements, source, patches, statement_level: true) if node.statements walk(node.rescue_clause, source, patches, statement_level: true) if node.rescue_clause walk(node.else_clause, source, patches, statement_level: true) if node.else_clause walk(node.ensure_clause, source, patches, statement_level: true) if node.ensure_clause when Prism::RescueNode walk(node.statements, source, patches, statement_level: true) if node.statements walk(node.subsequent, source, patches, statement_level: true) if node.subsequent when Prism::EnsureNode walk(node.statements, source, patches, statement_level: true) if node.statements when Prism::CaseNode walk(node.predicate, source, patches, statement_level: false) if node.predicate node.conditions.each { |cond| walk(cond, source, patches, statement_level: true) } walk(node.else_clause, source, patches, statement_level: true) if node.else_clause when Prism::WhenNode node.conditions.each { |c| walk(c, source, patches, statement_level: false) } walk(node.statements, source, patches, statement_level: true) if node.statements when Prism::CaseMatchNode walk(node.predicate, source, patches, statement_level: false) if node.predicate node.conditions.each { |cond| walk(cond, source, patches, statement_level: true) } walk(node.else_clause, source, patches, statement_level: true) if node.else_clause when Prism::InNode walk(node.statements, source, patches, statement_level: true) if node.statements when Prism::BlockNode, Prism::LambdaNode walk(node.body, source, patches, statement_level: true) if node.body when Prism::ParenthesesNode walk(node.body, source, patches, statement_level: statement_level) if node.body when Prism::LocalVariableWriteNode walk(node.value, source, patches, statement_level: statement_level) when Prism::PostExecutionNode walk(node.statements, source, patches, statement_level: true) if node.statements else node.compact_child_nodes.each { |child| walk(child, source, patches, statement_level: false) } end end |
#walk_call_children(node, source, patches) ⇒ void
This method returns an undefined value.
117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/ryac/pipeline/paren_optimizer.rb', line 117 def walk_call_children(node, source, patches) walk(node.receiver, source, patches, statement_level: false) if node.receiver node.arguments&.arguments&.each { |arg| walk(arg, source, patches, statement_level: false) } if node.block if node.block.is_a?(Prism::BlockNode) walk(node.block, source, patches, statement_level: true) else walk(node.block, source, patches, statement_level: false) end end end |
#walk_if(node, source, patches) ⇒ void
This method returns an undefined value.
103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/ryac/pipeline/paren_optimizer.rb', line 103 def walk_if(node, source, patches) walk(node.predicate, source, patches, statement_level: false) if node.if_keyword_loc # Block-if or modifier-if: body is at statement level walk(node.statements, source, patches, statement_level: true) if node.statements walk(node.subsequent, source, patches, statement_level: true) if node.subsequent else # Ternary (no if_keyword_loc): arms are NOT at statement level walk(node.statements, source, patches, statement_level: false) if node.statements walk(node.subsequent, source, patches, statement_level: false) if node.subsequent end end |