diff --git a/CHANGELOG.md b/CHANGELOG.md index 45a06c13..642e7866 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,21 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a ## [Unreleased] +### Changed + +- Nodes no longer have a `comments:` keyword on their initializers. By default, they initialize to an empty array. If you were previously passing comments into the initializer, you should now create the node first, then call `node.comments.concat` to add your comments. +- A lot of nodes have been folded into other nodes to make it easier to interact with the AST. This means that a lot of visit methods have been removed from the visitor and a lot of class definitions are no longer present. This also means that the nodes that received more function now have additional methods or fields to be able to differentiate them. Note that none of these changes have resulted in different formatting. The changes are listed below: + - `IfMod`, `UnlessMod`, `WhileMod`, `UntilMod` have been folded into `If`, `Unless`, `While`, and `Until`. Each of the nodes now have a `modifier?` method to tell if it was originally in the modifier form. Consequently, the `visit_if_mod`, `visit_unless_mod`, `visit_while_mod`, and `visit_until_mod` methods have been removed from the visitor. + - `VarAlias` is no longer a node. Instead it has been folded into the `Alias` node. The `Alias` node now has a `var_alias?` method to tell you if it is aliasing a global variable. Consequently, the `visit_var_alias` method has been removed from the visitor interface. If you were previously using this method, you should now use `visit_alias` instead. + - `Yield0` is no longer a node. Instead if has been folded into the `Yield` node. The `Yield` node can now have its `arguments` field be `nil`. Consequently, the `visit_yield0` method has been removed from the visitor interface. If you were previously using this method, you should now use `visit_yield` instead. + - `FCall` is no longer a node. Instead it has been folded into the `Call` node. The `Call` node can now have its `receiver` and `operator` fields be `nil`. Consequently, the `visit_fcall` method has been removed from the visitor interface. If you were previously using this method, you should now use `visit_call` instead. + - `Dot2` and `Dot3` are no longer nodes. Instead they have become a single new `RangeLiteral` node. This node looks the same as `Dot2` and `Dot3`, except that it additionally has an `operator` field that contains the operator that created the node. Consequently, the `visit_dot2` and `visit_dot3` methods have been removed from the visitor interface. If you were previously using these methods, you should now use `visit_range_literal` instead. + - `DefEndless` and `Defs` have both been folded into the `Def` node. The `Def` node now has the `target` and `operator` fields which originally came from `Defs` which can both be `nil`. It also now has an `endless?` method on it to tell if the original node was found in the endless form. Finally the `bodystmt` field can now either be a `BodyStmt` as it was or any other kind of node since that was the body of the `DefEndless` node. The `visit_defs` and `visit_def_endless` methods on the visitor have therefore been removed. + - `DoBlock` and `BraceBlock` have now been folded into a `Block` node. The `Block` node now has a `keywords?` method on it that returns true if the block was constructed with the `do`..`end` keywords. The `visit_do_block` and `visit_brace_block` methods on the visitor have therefore been removed and replaced with the `visit_block` method. + - `Return0` is no longer a node. Instead if has been folded into the `Return` node. The `Return` node can now have its `arguments` field be `nil`. Consequently, the `visit_return0` method has been removed from the visitor interface. If you were previously using this method, you should now use `visit_return` instead. +- The `ArgsForward`, `Redo`, `Retry`, and `ZSuper` nodes no longer have `value` fields associated with them (which were always string literals corresponding to the keyword being used). +- The `Command` and `CommandCall` nodes now has `block` attributes on them. These attributes are used in the place where you would previously have had a `MethodAddBlock` structure. Where before the `MethodAddBlock` would have the command and block as its two children, you now just have one command node with the `block` attribute set to the `Block` node. + ## [4.3.0] - 2022-10-28 ### Added diff --git a/lib/syntax_tree/node.rb b/lib/syntax_tree/node.rb index aa133b7f..97bec379 100644 --- a/lib/syntax_tree/node.rb +++ b/lib/syntax_tree/node.rb @@ -146,11 +146,11 @@ class BEGINBlock < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(lbrace:, statements:, location:, comments: []) + def initialize(lbrace:, statements:, location:) @lbrace = lbrace @statements = statements @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -199,10 +199,10 @@ class CHAR < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -249,11 +249,11 @@ class ENDBlock < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(lbrace:, statements:, location:, comments: []) + def initialize(lbrace:, statements:, location:) @lbrace = lbrace @statements = statements @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -305,10 +305,10 @@ class EndContent < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -359,7 +359,8 @@ class Alias < Node # Formats an argument to the alias keyword. For symbol literals it uses the # value of the symbol directly to look like bare words. class AliasArgumentFormatter - # [DynaSymbol | SymbolLiteral] the argument being passed to alias + # [Backref | DynaSymbol | GVar | SymbolLiteral] the argument being passed + # to alias attr_reader :argument def initialize(argument) @@ -383,20 +384,20 @@ def format(q) end end - # [DynaSymbol | SymbolLiteral] the new name of the method + # [DynaSymbol | GVar | SymbolLiteral] the new name of the method attr_reader :left - # [DynaSymbol | SymbolLiteral] the old name of the method + # [Backref | DynaSymbol | GVar | SymbolLiteral] the old name of the method attr_reader :right # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(left:, right:, location:, comments: []) + def initialize(left:, right:, location:) @left = left @right = right @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -428,6 +429,10 @@ def format(q) end end end + + def var_alias? + left.is_a?(GVar) + end end # ARef represents when you're pulling a value out of a collection at a @@ -453,11 +458,11 @@ class ARef < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(collection:, index:, location:, comments: []) + def initialize(collection:, index:, location:) @collection = collection @index = index @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -514,11 +519,11 @@ class ARefField < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(collection:, index:, location:, comments: []) + def initialize(collection:, index:, location:) @collection = collection @index = index @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -577,10 +582,10 @@ class ArgParen < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(arguments:, location:, comments: []) + def initialize(arguments:, location:) @arguments = arguments @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -650,10 +655,10 @@ class Args < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(parts:, location:, comments: []) + def initialize(parts:, location:) @parts = parts @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -686,10 +691,10 @@ class ArgBlock < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -723,10 +728,10 @@ class ArgStar < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -767,16 +772,12 @@ def format(q) # The ArgsForward node appears in both the caller (the request method calls) # and the callee (the get and post definitions). class ArgsForward < Node - # [String] the value of the operator - attr_reader :value - # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) - @value = value + def initialize(location:) @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -790,11 +791,11 @@ def child_nodes alias deconstruct child_nodes def deconstruct_keys(_keys) - { value: value, location: location, comments: comments } + { location: location, comments: comments } end def format(q) - q.text(value) + q.text("...") end end @@ -955,11 +956,11 @@ def format(q) # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(lbracket:, contents:, location:, comments: []) + def initialize(lbracket:, contents:, location:) @lbracket = lbracket @contents = contents @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -1132,7 +1133,7 @@ def initialize( @rest = rest @posts = posts @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -1209,11 +1210,11 @@ class Assign < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(target:, value:, location:, comments: []) + def initialize(target:, value:, location:) @target = target @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -1271,11 +1272,11 @@ class Assoc < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(key:, value:, location:, comments: []) + def initialize(key:, value:, location:) @key = key @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -1330,10 +1331,10 @@ class AssocSplat < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -1368,10 +1369,10 @@ class Backref < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -1403,10 +1404,10 @@ class Backtick < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -1515,10 +1516,10 @@ class BareAssocHash < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(assocs:, location:, comments: []) + def initialize(assocs:, location:) @assocs = assocs @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -1557,10 +1558,10 @@ class Begin < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(bodystmt:, location:, comments: []) + def initialize(bodystmt:, location:) @bodystmt = bodystmt @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -1605,10 +1606,10 @@ class PinnedBegin < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(statement:, location:, comments: []) + def initialize(statement:, location:) @statement = statement @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -1677,12 +1678,12 @@ def name # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(left:, operator:, right:, location:, comments: []) + def initialize(left:, operator:, right:, location:) @left = left @operator = operator @right = right @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -1745,11 +1746,11 @@ class BlockVar < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(params:, locals:, location:, comments: []) + def initialize(params:, locals:, location:) @params = params @locals = locals @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -1803,10 +1804,10 @@ class BlockArg < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(name:, location:, comments: []) + def initialize(name:, location:) @name = name @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -1866,7 +1867,7 @@ def initialize( @else_clause = else_clause @ensure_clause = ensure_clause @location = location - @comments = comments + @comments = [] end def bind(start_char, start_column, end_char, end_column) @@ -1957,223 +1958,6 @@ def format(q) end end - # Responsible for formatting either a BraceBlock or a DoBlock. - class BlockFormatter - # Formats the opening brace or keyword of a block. - class BlockOpenFormatter - # [String] the actual output that should be printed - attr_reader :text - - # [LBrace | Keyword] the node that is being represented - attr_reader :node - - def initialize(text, node) - @text = text - @node = node - end - - def comments - node.comments - end - - def format(q) - q.text(text) - end - end - - # [BraceBlock | DoBlock] the block node to be formatted - attr_reader :node - - # [LBrace | Keyword] the node that opens the block - attr_reader :block_open - - # [String] the string that closes the block - attr_reader :block_close - - # [BodyStmt | Statements] the statements inside the block - attr_reader :statements - - def initialize(node, block_open, block_close, statements) - @node = node - @block_open = block_open - @block_close = block_close - @statements = statements - end - - def format(q) - # If this is nested anywhere inside of a Command or CommandCall node, then - # we can't change which operators we're using for the bounds of the block. - break_opening, break_closing, flat_opening, flat_closing = - if unchangeable_bounds?(q) - [block_open.value, block_close, block_open.value, block_close] - elsif forced_do_end_bounds?(q) - %w[do end do end] - elsif forced_brace_bounds?(q) - %w[{ } { }] - else - %w[do end { }] - end - - # If the receiver of this block a Command or CommandCall node, then there - # are no parentheses around the arguments to that command, so we need to - # break the block. - case q.parent.call - when Command, CommandCall - q.break_parent - format_break(q, break_opening, break_closing) - return - end - - q.group do - q - .if_break { format_break(q, break_opening, break_closing) } - .if_flat { format_flat(q, flat_opening, flat_closing) } - end - end - - private - - # If this is nested anywhere inside certain nodes, then we can't change - # which operators/keywords we're using for the bounds of the block. - def unchangeable_bounds?(q) - q.parents.any? do |parent| - # If we hit a statements, then we're safe to use whatever since we - # know for certain we're going to get split over multiple lines - # anyway. - case parent - when Statements, ArgParen - break false - when Command, CommandCall - true - else - false - end - end - end - - # If we're a sibling of a control-flow keyword, then we're going to have to - # use the do..end bounds. - def forced_do_end_bounds?(q) - case q.parent.call - when Break, Next, Return, Super - true - else - false - end - end - - # If we're the predicate of a loop or conditional, then we're going to have - # to go with the {..} bounds. - def forced_brace_bounds?(q) - previous = nil - q.parents.any? do |parent| - case parent - when Paren, Statements - # If we hit certain breakpoints then we know we're safe. - return false - when If, IfMod, IfOp, Unless, UnlessMod, While, WhileMod, Until, - UntilMod - return true if parent.predicate == previous - end - - previous = parent - false - end - end - - def format_break(q, opening, closing) - q.text(" ") - q.format(BlockOpenFormatter.new(opening, block_open), stackable: false) - - if node.block_var - q.text(" ") - q.format(node.block_var) - end - - unless statements.empty? - q.indent do - q.breakable_space - q.format(statements) - end - end - - q.breakable_space - q.text(closing) - end - - def format_flat(q, opening, closing) - q.text(" ") - q.format(BlockOpenFormatter.new(opening, block_open), stackable: false) - - if node.block_var - q.breakable_space - q.format(node.block_var) - q.breakable_space - end - - if statements.empty? - q.text(" ") if opening == "do" - else - q.breakable_space unless node.block_var - q.format(statements) - q.breakable_space - end - - q.text(closing) - end - end - - # BraceBlock represents passing a block to a method call using the { } - # operators. - # - # method { |variable| variable + 1 } - # - class BraceBlock < Node - # [LBrace] the left brace that opens this block - attr_reader :lbrace - - # [nil | BlockVar] the optional set of parameters to the block - attr_reader :block_var - - # [Statements] the list of expressions to evaluate within the block - attr_reader :statements - - # [Array[ Comment | EmbDoc ]] the comments attached to this node - attr_reader :comments - - def initialize(lbrace:, block_var:, statements:, location:, comments: []) - @lbrace = lbrace - @block_var = block_var - @statements = statements - @location = location - @comments = comments - end - - def accept(visitor) - visitor.visit_brace_block(self) - end - - def child_nodes - [lbrace, block_var, statements] - end - - alias deconstruct child_nodes - - def deconstruct_keys(_keys) - { - lbrace: lbrace, - block_var: block_var, - statements: statements, - location: location, - comments: comments - } - end - - def format(q) - BlockFormatter.new(self, lbrace, "}", statements).format(q) - end - end - # Formats either a Break, Next, or Return node. class FlowControlFormatter # [String] the keyword to print @@ -2188,6 +1972,13 @@ def initialize(keyword, node) end def format(q) + # If there are no arguments associated with this flow control, then we can + # safely just print the keyword and return. + if node.arguments.nil? + q.text(keyword) + return + end + q.group do q.text(keyword) @@ -2371,10 +2162,10 @@ class Break < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(arguments:, location:, comments: []) + def initialize(arguments:, location:) @arguments = arguments @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -2455,14 +2246,26 @@ def format(q) when Call case (receiver = child.receiver) when Call - children << receiver + if receiver.receiver.nil? + break + else + children << receiver + end when MethodAddBlock - receiver.call.is_a?(Call) ? children << receiver : break + if receiver.call.is_a?(Call) && !receiver.call.receiver.nil? + children << receiver + else + break + end else break end when MethodAddBlock - child.call.is_a?(Call) ? children << child.call : break + if child.call.is_a?(Call) && !child.call.receiver.nil? + children << child.call + else + break + end else break end @@ -2476,13 +2279,13 @@ def format(q) # https://github.com/prettier/plugin-ruby/issues/863. parents = q.parents.take(4) if (parent = parents[2]) - # If we're at a do_block, then we want to go one more level up. This is - # because do blocks have BodyStmt nodes instead of just Statements - # nodes. - parent = parents[3] if parent.is_a?(DoBlock) + # If we're at a block with the `do` keywords, then we want to go one + # more level up. This is because do blocks have BodyStmt nodes instead + # of just Statements nodes. + parent = parents[3] if parent.is_a?(Block) && parent.keywords? - if parent.is_a?(MethodAddBlock) && parent.call.is_a?(FCall) && - parent.call.value.value == "sig" + if parent.is_a?(MethodAddBlock) && parent.call.is_a?(Call) && + parent.call.message.value == "sig" threshold = 2 end end @@ -2511,7 +2314,7 @@ def format_chain(q, children) # formatter so it's as if we had descending normally into them. This is # necessary so they can check their parents as normal. q.stack.concat(children) - q.format(children.last.receiver) + q.format(children.last.receiver) if children.last.receiver q.group do if attach_directly?(children.last) @@ -2554,7 +2357,8 @@ def format_chain(q, children) # If the parent call node has a comment on the message then we need # to print the operator trailing in order to keep it working. last_child = children.last - if last_child.is_a?(Call) && last_child.message.comments.any? + if last_child.is_a?(Call) && last_child.message.comments.any? && + last_child.operator q.format(CallOperatorFormatter.new(last_child.operator)) skip_operator = true else @@ -2583,9 +2387,9 @@ def self.chained?(node) case node when Call - true + !node.receiver.nil? when MethodAddBlock - node.call.is_a?(Call) + node.call.is_a?(Call) && !node.call.receiver.nil? else false end @@ -2616,7 +2420,7 @@ def format_child( case child when Call q.group do - unless skip_operator + if !skip_operator && child.operator q.format(CallOperatorFormatter.new(child.operator)) end q.format(child.message) if child.message != :call @@ -2644,10 +2448,10 @@ def format_child( # receiver.message # class Call < Node - # [untyped] the receiver of the method call + # [nil | untyped] the receiver of the method call attr_reader :receiver - # [:"::" | Op | Period] the operator being used to send the message + # [nil | :"::" | Op | Period] the operator being used to send the message attr_reader :operator # [:call | Backtick | Const | Ident | Op] the message being sent @@ -2672,7 +2476,7 @@ def initialize( @message = message @arguments = arguments @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -2702,23 +2506,38 @@ def deconstruct_keys(_keys) end def format(q) - # If we're at the top of a call chain, then we're going to do some - # specialized printing in case we can print it nicely. We _only_ do this - # at the top of the chain to avoid weird recursion issues. - if CallChainFormatter.chained?(receiver) && - !CallChainFormatter.chained?(q.parent) - q.group do - q - .if_break { CallChainFormatter.new(self).format(q) } - .if_flat { format_contents(q) } + if receiver + # If we're at the top of a call chain, then we're going to do some + # specialized printing in case we can print it nicely. We _only_ do this + # at the top of the chain to avoid weird recursion issues. + if CallChainFormatter.chained?(receiver) && + !CallChainFormatter.chained?(q.parent) + q.group do + q + .if_break { CallChainFormatter.new(self).format(q) } + .if_flat { format_contents(q) } + end + else + format_contents(q) end else - format_contents(q) + q.format(message) + + if arguments.is_a?(ArgParen) && arguments.arguments.nil? && + !message.is_a?(Const) + # If you're using an explicit set of parentheses on something that + # looks like a constant, then we need to match that in order to + # maintain valid Ruby. For example, you could do something like Foo(), + # on which we would need to keep the parentheses to make it look like + # a method call. + else + q.format(arguments) + end end end # Print out the arguments to this call. If there are no arguments, then do - #nothing. + # nothing. def format_arguments(q) case arguments when ArgParen @@ -2782,12 +2601,12 @@ class Case < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(keyword:, value:, consequent:, location:, comments: []) + def initialize(keyword:, value:, consequent:, location:) @keyword = keyword @value = value @consequent = consequent @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -2847,12 +2666,12 @@ class RAssign < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, operator:, pattern:, location:, comments: []) + def initialize(value:, operator:, pattern:, location:) @value = value @operator = operator @pattern = pattern @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -2943,12 +2762,12 @@ class ClassDeclaration < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(constant:, superclass:, bodystmt:, location:, comments: []) + def initialize(constant:, superclass:, bodystmt:, location:) @constant = constant @superclass = superclass @bodystmt = bodystmt @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -3046,14 +2865,18 @@ class Command < Node # [Args] the arguments being sent with the message attr_reader :arguments + # [nil | Block] the optional block being passed to the method + attr_reader :block + # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(message:, arguments:, location:, comments: []) + def initialize(message:, arguments:, block:, location:) @message = message @arguments = arguments + @block = block @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -3061,7 +2884,7 @@ def accept(visitor) end def child_nodes - [message, arguments] + [message, arguments, block] end alias deconstruct child_nodes @@ -3070,6 +2893,7 @@ def deconstruct_keys(_keys) { message: message, arguments: arguments, + block: block, location: location, comments: comments } @@ -3080,6 +2904,8 @@ def format(q) q.format(message) align(q, self) { q.format(arguments) } end + + q.format(block) if block end private @@ -3094,7 +2920,7 @@ def align(q, node, &block) part = parts.first case part - when Def, Defs, DefEndless + when Def q.text(" ") yield when IfOp @@ -3135,6 +2961,9 @@ class CommandCall < Node # [nil | Args] the arguments going along with the message attr_reader :arguments + # [nil | Block] the block associated with this method call + attr_reader :block + # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments @@ -3143,6 +2972,7 @@ def initialize( operator:, message:, arguments:, + block:, location:, comments: [] ) @@ -3150,8 +2980,9 @@ def initialize( @operator = operator @message = message @arguments = arguments + @block = block @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -3159,7 +2990,7 @@ def accept(visitor) end def child_nodes - [receiver, message, arguments] + [receiver, message, arguments, block] end alias deconstruct child_nodes @@ -3170,6 +3001,7 @@ def deconstruct_keys(_keys) operator: operator, message: message, arguments: arguments, + block: block, location: location, comments: comments } @@ -3210,6 +3042,8 @@ def format(q) end end end + + q.format(block) if block end private @@ -3323,10 +3157,10 @@ class Const < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -3364,11 +3198,11 @@ class ConstPathField < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(parent:, constant:, location:, comments: []) + def initialize(parent:, constant:, location:) @parent = parent @constant = constant @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -3411,11 +3245,11 @@ class ConstPathRef < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(parent:, constant:, location:, comments: []) + def initialize(parent:, constant:, location:) @parent = parent @constant = constant @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -3457,10 +3291,10 @@ class ConstRef < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(constant:, location:, comments: []) + def initialize(constant:, location:) @constant = constant @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -3493,10 +3327,10 @@ class CVar < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -3521,26 +3355,35 @@ def format(q) # Def represents defining a regular method on the current self object. # # def method(param) result end + # def object.method(param) result end # class Def < Node + # [nil | untyped] the target where the method is being defined + attr_reader :target + + # [nil | Op | Period] the operator being used to declare the method + attr_reader :operator + # [Backtick | Const | Ident | Kw | Op] the name of the method attr_reader :name - # [Params | Paren] the parameter declaration for the method + # [nil | Params | Paren] the parameter declaration for the method attr_reader :params - # [BodyStmt] the expressions to be executed by the method + # [BodyStmt | untyped] the expressions to be executed by the method attr_reader :bodystmt # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(name:, params:, bodystmt:, location:, comments: []) + def initialize(target:, operator:, name:, params:, bodystmt:, location:) + @target = target + @operator = operator @name = name @params = params @bodystmt = bodystmt @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -3548,13 +3391,15 @@ def accept(visitor) end def child_nodes - [name, params, bodystmt] + [target, operator, name, params, bodystmt] end alias deconstruct child_nodes def deconstruct_keys(_keys) { + target: target, + operator: operator, name: name, params: params, bodystmt: bodystmt, @@ -3567,114 +3412,49 @@ def format(q) q.group do q.group do q.text("def ") + + if target + q.format(target) + q.format(CallOperatorFormatter.new(operator), stackable: false) + end + q.format(name) - if !params.is_a?(Params) || !params.empty? || params.comments.any? + case params + when Paren q.format(params) + when Params + q.format(params) if !params.empty? || params.comments.any? end end - unless bodystmt.empty? - q.indent do - q.breakable_force - q.format(bodystmt) - end - end + if endless? + q.text(" =") + q.group do + q.indent do + q.breakable_space + q.format(bodystmt) + end + end + else + unless bodystmt.empty? + q.indent do + q.breakable_force + q.format(bodystmt) + end + end - q.breakable_force - q.text("end") + q.breakable_force + q.text("end") + end end end - end - - # DefEndless represents defining a single-line method since Ruby 3.0+. - # - # def method = result - # - class DefEndless < Node - # [untyped] the target where the method is being defined - attr_reader :target - - # [Op | Period] the operator being used to declare the method - attr_reader :operator - - # [Backtick | Const | Ident | Kw | Op] the name of the method - attr_reader :name - - # [nil | Params | Paren] the parameter declaration for the method - attr_reader :paren - - # [untyped] the expression to be executed by the method - attr_reader :statement - - # [Array[ Comment | EmbDoc ]] the comments attached to this node - attr_reader :comments - - def initialize( - target:, - operator:, - name:, - paren:, - statement:, - location:, - comments: [] - ) - @target = target - @operator = operator - @name = name - @paren = paren - @statement = statement - @location = location - @comments = comments - end - - def accept(visitor) - visitor.visit_def_endless(self) - end - - def child_nodes - [target, operator, name, paren, statement] - end - - alias deconstruct child_nodes - - def deconstruct_keys(_keys) - { - target: target, - operator: operator, - name: name, - paren: paren, - statement: statement, - location: location, - comments: comments - } - end - def format(q) - q.group do - q.text("def ") - - if target - q.format(target) - q.format(CallOperatorFormatter.new(operator), stackable: false) - end - - q.format(name) - - if paren - params = paren - params = params.contents if params.is_a?(Paren) - q.format(paren) unless params.empty? - end - - q.text(" =") - q.group do - q.indent do - q.breakable_space - q.format(statement) - end - end - end + # Returns true if the method was found in the source in the "endless" form, + # i.e. where the method body is defined using the `=` operator after the + # method name and parameters. + def endless? + !bodystmt.is_a?(BodyStmt) end end @@ -3690,10 +3470,10 @@ class Defined < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -3723,63 +3503,71 @@ def format(q) end end - # Defs represents defining a singleton method on an object. + # Block represents passing a block to a method call using the +do+ and +end+ + # keywords or the +{+ and +}+ operators. # - # def object.method(param) result end + # method do |value| + # end # - class Defs < Node - # [untyped] the target where the method is being defined - attr_reader :target + # method { |value| } + # + class Block < Node + # Formats the opening brace or keyword of a block. + class BlockOpenFormatter + # [String] the actual output that should be printed + attr_reader :text - # [Op | Period] the operator being used to declare the method - attr_reader :operator + # [LBrace | Keyword] the node that is being represented + attr_reader :node - # [Backtick | Const | Ident | Kw | Op] the name of the method - attr_reader :name + def initialize(text, node) + @text = text + @node = node + end - # [Params | Paren] the parameter declaration for the method - attr_reader :params + def comments + node.comments + end - # [BodyStmt] the expressions to be executed by the method + def format(q) + q.text(text) + end + end + + # [LBrace | Kw] the left brace or the do keyword that opens this block + attr_reader :opening + + # [nil | BlockVar] the optional variable declaration within this block + attr_reader :block_var + + # [BodyStmt | Statements] the expressions to be executed within this block attr_reader :bodystmt # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize( - target:, - operator:, - name:, - params:, - bodystmt:, - location:, - comments: [] - ) - @target = target - @operator = operator - @name = name - @params = params + def initialize(opening:, block_var:, bodystmt:, location:) + @opening = opening + @block_var = block_var @bodystmt = bodystmt @location = location - @comments = comments + @comments = [] end def accept(visitor) - visitor.visit_defs(self) + visitor.visit_block(self) end def child_nodes - [target, operator, name, params, bodystmt] + [opening, block_var, bodystmt] end alias deconstruct child_nodes def deconstruct_keys(_keys) { - target: target, - operator: operator, - name: name, - params: params, + opening: opening, + block_var: block_var, bodystmt: bodystmt, location: location, comments: comments @@ -3787,115 +3575,134 @@ def deconstruct_keys(_keys) end def format(q) - q.group do - q.group do - q.text("def ") - q.format(target) - q.format(CallOperatorFormatter.new(operator), stackable: false) - q.format(name) - - if !params.is_a?(Params) || !params.empty? || params.comments.any? - q.format(params) - end + # If this is nested anywhere inside of a Command or CommandCall node, then + # we can't change which operators we're using for the bounds of the block. + break_opening, break_closing, flat_opening, flat_closing = + if unchangeable_bounds?(q) + block_close = keywords? ? "end" : "}" + [opening.value, block_close, opening.value, block_close] + elsif forced_do_end_bounds?(q) + %w[do end do end] + elsif forced_brace_bounds?(q) + %w[{ } { }] + else + %w[do end { }] end - unless bodystmt.empty? - q.indent do - q.breakable_force - q.format(bodystmt) - end - end + # If the receiver of this block a Command or CommandCall node, then there + # are no parentheses around the arguments to that command, so we need to + # break the block. + case q.parent + when Command, CommandCall + q.break_parent + format_break(q, break_opening, break_closing) + return + end - q.breakable_force - q.text("end") + q.group do + q + .if_break { format_break(q, break_opening, break_closing) } + .if_flat { format_flat(q, flat_opening, flat_closing) } end end - end - - # DoBlock represents passing a block to a method call using the +do+ and +end+ - # keywords. - # - # method do |value| - # end - # - class DoBlock < Node - # [Kw] the do keyword that opens this block - attr_reader :keyword - - # [nil | BlockVar] the optional variable declaration within this block - attr_reader :block_var - - # [BodyStmt] the expressions to be executed within this block - attr_reader :bodystmt - # [Array[ Comment | EmbDoc ]] the comments attached to this node - attr_reader :comments - - def initialize(keyword:, block_var:, bodystmt:, location:, comments: []) - @keyword = keyword - @block_var = block_var - @bodystmt = bodystmt - @location = location - @comments = comments + def keywords? + opening.is_a?(Kw) end - def accept(visitor) - visitor.visit_do_block(self) + private + + # If this is nested anywhere inside certain nodes, then we can't change + # which operators/keywords we're using for the bounds of the block. + def unchangeable_bounds?(q) + q.parents.any? do |parent| + # If we hit a statements, then we're safe to use whatever since we + # know for certain we're going to get split over multiple lines + # anyway. + case parent + when Statements, ArgParen + break false + when Command, CommandCall + true + else + false + end + end end - def child_nodes - [keyword, block_var, bodystmt] + # If we're a sibling of a control-flow keyword, then we're going to have to + # use the do..end bounds. + def forced_do_end_bounds?(q) + case q.parent.call + when Break, Next, Return, Super + true + else + false + end end - alias deconstruct child_nodes + # If we're the predicate of a loop or conditional, then we're going to have + # to go with the {..} bounds. + def forced_brace_bounds?(q) + previous = nil + q.parents.any? do |parent| + case parent + when Paren, Statements + # If we hit certain breakpoints then we know we're safe. + return false + when If, IfOp, Unless, While, Until + return true if parent.predicate == previous + end - def deconstruct_keys(_keys) - { - keyword: keyword, - block_var: block_var, - bodystmt: bodystmt, - location: location, - comments: comments - } + previous = parent + false + end end - def format(q) - BlockFormatter.new(self, keyword, "end", bodystmt).format(q) - end - end + def format_break(q, break_opening, break_closing) + q.text(" ") + q.format(BlockOpenFormatter.new(break_opening, opening), stackable: false) - # Responsible for formatting Dot2 and Dot3 nodes. - class DotFormatter - # [String] the operator to display - attr_reader :operator + if block_var + q.text(" ") + q.format(block_var) + end - # [Dot2 | Dot3] the node that is being formatter - attr_reader :node + unless bodystmt.empty? + q.indent do + q.breakable_space + q.format(bodystmt) + end + end - def initialize(operator, node) - @operator = operator - @node = node + q.breakable_space + q.text(break_closing) end - def format(q) - left = node.left - right = node.right + def format_flat(q, flat_opening, flat_closing) + q.text(" ") + q.format(BlockOpenFormatter.new(flat_opening, opening), stackable: false) - q.format(left) if left + if block_var + q.breakable_space + q.format(block_var) + q.breakable_space + end - case q.parent - when If, IfMod, Unless, UnlessMod - q.text(" #{operator} ") + if bodystmt.empty? + q.text(" ") if flat_opening == "do" else - q.text(operator) + q.breakable_space unless block_var + q.format(bodystmt) + q.breakable_space end - q.format(right) if right + q.text(flat_closing) end end - # Dot2 represents using the .. operator between two expressions. Usually this - # is to create a range object. + # RangeLiteral represents using the .. or the ... operator between two + # expressions. Usually this is to create a range object. # # 1..2 # @@ -3905,25 +3712,29 @@ def format(q) # end # # One of the sides of the expression may be nil, but not both. - class Dot2 < Node + class RangeLiteral < Node # [nil | untyped] the left side of the expression attr_reader :left + # [Op] the operator used for this range + attr_reader :operator + # [nil | untyped] the right side of the expression attr_reader :right # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(left:, right:, location:, comments: []) + def initialize(left:, operator:, right:, location:) @left = left + @operator = operator @right = right @location = location - @comments = comments + @comments = [] end def accept(visitor) - visitor.visit_dot2(self) + visitor.visit_range_literal(self) end def child_nodes @@ -3933,59 +3744,26 @@ def child_nodes alias deconstruct child_nodes def deconstruct_keys(_keys) - { left: left, right: right, location: location, comments: comments } + { + left: left, + operator: operator, + right: right, + location: location, + comments: comments + } end def format(q) - DotFormatter.new("..", self).format(q) - end - end - - # Dot3 represents using the ... operator between two expressions. Usually this - # is to create a range object. It's effectively the same event as the Dot2 - # node but with this operator you're asking Ruby to omit the final value. - # - # 1...2 - # - # Like Dot2 it can also be used to create a flip-flop. - # - # if value == 5 ... value == 10 - # end - # - # One of the sides of the expression may be nil, but not both. - class Dot3 < Node - # [nil | untyped] the left side of the expression - attr_reader :left - - # [nil | untyped] the right side of the expression - attr_reader :right - - # [Array[ Comment | EmbDoc ]] the comments attached to this node - attr_reader :comments - - def initialize(left:, right:, location:, comments: []) - @left = left - @right = right - @location = location - @comments = comments - end - - def accept(visitor) - visitor.visit_dot3(self) - end - - def child_nodes - [left, right] - end - - alias deconstruct child_nodes + q.format(left) if left - def deconstruct_keys(_keys) - { left: left, right: right, location: location, comments: comments } - end + case q.parent + when If, Unless + q.text(" #{operator.value} ") + else + q.text(operator.value) + end - def format(q) - DotFormatter.new("...", self).format(q) + q.format(right) if right end end @@ -4050,11 +3828,11 @@ class DynaSymbol < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(parts:, quote:, location:, comments: []) + def initialize(parts:, quote:, location:) @parts = parts @quote = quote @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -4161,11 +3939,11 @@ class Else < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(keyword:, statements:, location:, comments: []) + def initialize(keyword:, statements:, location:) @keyword = keyword @statements = statements @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -4231,7 +4009,7 @@ def initialize( @statements = statements @consequent = consequent @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -4435,11 +4213,11 @@ class Ensure < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(keyword:, statements:, location:, comments: []) + def initialize(keyword:, statements:, location:) @keyword = keyword @statements = statements @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -4490,10 +4268,10 @@ class ExcessedComma < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -4515,64 +4293,6 @@ def format(q) end end - # FCall represents the piece of a method call that comes before any arguments - # (i.e., just the name of the method). It is used in places where the parser - # is sure that it is a method call and not potentially a local variable. - # - # method(argument) - # - # In the above example, it's referring to the +method+ segment. - class FCall < Node - # [Const | Ident] the name of the method - attr_reader :value - - # [nil | ArgParen | Args] the arguments to the method call - attr_reader :arguments - - # [Array[ Comment | EmbDoc ]] the comments attached to this node - attr_reader :comments - - def initialize(value:, arguments:, location:, comments: []) - @value = value - @arguments = arguments - @location = location - @comments = comments - end - - def accept(visitor) - visitor.visit_fcall(self) - end - - def child_nodes - [value, arguments] - end - - alias deconstruct child_nodes - - def deconstruct_keys(_keys) - { - value: value, - arguments: arguments, - location: location, - comments: comments - } - end - - def format(q) - q.format(value) - - if arguments.is_a?(ArgParen) && arguments.arguments.nil? && - !value.is_a?(Const) - # If you're using an explicit set of parentheses on something that looks - # like a constant, then we need to match that in order to maintain valid - # Ruby. For example, you could do something like Foo(), on which we - # would need to keep the parentheses to make it look like a method call. - else - q.format(arguments) - end - end - end - # Field is always the child of an assignment. It represents assigning to a # “field” on an object. # @@ -4591,12 +4311,12 @@ class Field < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(parent:, operator:, name:, location:, comments: []) + def initialize(parent:, operator:, name:, location:) @parent = parent @operator = operator @name = name @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -4639,10 +4359,10 @@ class FloatLiteral < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -4688,13 +4408,13 @@ class FndPtn < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(constant:, left:, values:, right:, location:, comments: []) + def initialize(constant:, left:, values:, right:, location:) @constant = constant @left = left @values = values @right = right @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -4763,12 +4483,12 @@ class For < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(index:, collection:, statements:, location:, comments: []) + def initialize(index:, collection:, statements:, location:) @index = index @collection = collection @statements = statements @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -4822,10 +4542,10 @@ class GVar < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -4887,11 +4607,11 @@ def format(q) # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(lbrace:, assocs:, location:, comments: []) + def initialize(lbrace:, assocs:, location:) @lbrace = lbrace @assocs = assocs @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -4987,7 +4707,7 @@ def initialize( @dedent = dedent @parts = parts @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -5064,10 +4784,10 @@ class HeredocBeg < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -5103,10 +4823,10 @@ class HeredocEnd < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -5195,12 +4915,12 @@ def format(q) # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(constant:, keywords:, keyword_rest:, location:, comments: []) + def initialize(constant:, keywords:, keyword_rest:, location:) @constant = constant @keywords = keywords @keyword_rest = keyword_rest @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -5305,10 +5025,10 @@ class Ident < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -5398,10 +5118,9 @@ def call(q, node) # and default instead to breaking them into multiple lines. def ternaryable?(statement) case statement - when Alias, Assign, Break, Command, CommandCall, Heredoc, If, IfMod, - IfOp, Lambda, MAssign, Next, OpAssign, RescueMod, Return, Return0, - Super, Undef, Unless, UnlessMod, Until, UntilMod, VarAlias, - VoidStmt, While, WhileMod, Yield, Yield0, ZSuper + when Alias, Assign, Break, Command, CommandCall, Heredoc, If, IfOp, + Lambda, MAssign, Next, OpAssign, RescueMod, Return, Super, Undef, + Unless, Until, VoidStmt, While, Yield, ZSuper # This is a list of nodes that should not be allowed to be a part of a # ternary clause. false @@ -5432,41 +5151,64 @@ def initialize(keyword, node) end def format(q) - # If we can transform this node into a ternary, then we're going to print - # a special version that uses the ternary operator if it fits on one line. - if Ternaryable.call(q, node) - format_ternary(q) - return - end + if node.modifier? + statement = node.statements.body[0] - # If the predicate of the conditional contains an assignment (in which - # case we can't know for certain that that assignment doesn't impact the - # statements inside the conditional) then we can't use the modifier form - # and we must use the block form. - if ContainsAssignment.call(node.predicate) - format_break(q, force: true) - return - end - - if node.consequent || node.statements.empty? || contains_conditional? - q.group { format_break(q, force: true) } + if ContainsAssignment.call(statement) || q.parent.is_a?(In) + q.group { format_flat(q) } + else + q.group do + q + .if_break { format_break(q, force: false) } + .if_flat { format_flat(q) } + end + end else - q.group do - q - .if_break { format_break(q, force: false) } - .if_flat do - Parentheses.flat(q) do - q.format(node.statements) - q.text(" #{keyword} ") - q.format(node.predicate) + # If we can transform this node into a ternary, then we're going to + # print a special version that uses the ternary operator if it fits on + # one line. + if Ternaryable.call(q, node) + format_ternary(q) + return + end + + # If the predicate of the conditional contains an assignment (in which + # case we can't know for certain that that assignment doesn't impact the + # statements inside the conditional) then we can't use the modifier form + # and we must use the block form. + if ContainsAssignment.call(node.predicate) + format_break(q, force: true) + return + end + + if node.consequent || node.statements.empty? || contains_conditional? + q.group { format_break(q, force: true) } + else + q.group do + q + .if_break { format_break(q, force: false) } + .if_flat do + Parentheses.flat(q) do + q.format(node.statements) + q.text(" #{keyword} ") + q.format(node.predicate) + end end - end + end end end end private + def format_flat(q) + Parentheses.flat(q) do + q.format(node.statements.body[0]) + q.text(" #{keyword} ") + q.format(node.predicate) + end + end + def format_break(q, force:) q.text("#{keyword} ") q.nest(keyword.length + 1) { q.format(node.predicate) } @@ -5537,7 +5279,7 @@ def contains_conditional? return false if statements.length != 1 case statements.first - when If, IfMod, IfOp, Unless, UnlessMod + when If, IfOp, Unless true else false @@ -5574,7 +5316,7 @@ def initialize( @statements = statements @consequent = consequent @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -5600,6 +5342,11 @@ def deconstruct_keys(_keys) def format(q) ConditionalFormatter.new("if", self).format(q) end + + # Checks if the node was originally found in the modifier form. + def modifier? + predicate.location.start_char > statements.location.start_char + end end # IfOp represents a ternary clause. @@ -5619,12 +5366,12 @@ class IfOp < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(predicate:, truthy:, falsy:, location:, comments: []) + def initialize(predicate:, truthy:, falsy:, location:) @predicate = predicate @truthy = truthy @falsy = falsy @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -5649,10 +5396,9 @@ def deconstruct_keys(_keys) def format(q) force_flat = [ - Alias, Assign, Break, Command, CommandCall, Heredoc, If, IfMod, IfOp, - Lambda, MAssign, Next, OpAssign, RescueMod, Return, Return0, Super, - Undef, Unless, UnlessMod, UntilMod, VarAlias, VoidStmt, WhileMod, Yield, - Yield0, ZSuper + Alias, Assign, Break, Command, CommandCall, Heredoc, If, IfOp, Lambda, + MAssign, Next, OpAssign, RescueMod, Return, Super, Undef, Unless, + VoidStmt, Yield, ZSuper ] if q.parent.is_a?(Paren) || force_flat.include?(truthy.class) || @@ -5684,111 +5430,23 @@ def format_break(q) q.format(falsy) end - q.breakable_space - q.text("end") - end - end - - def format_flat(q) - q.format(predicate) - q.text(" ?") - - q.indent do - q.breakable_space - q.format(truthy) - q.text(" :") - - q.breakable_space - q.format(falsy) - end - end - end - - # Formats an IfMod or UnlessMod node. - class ConditionalModFormatter - # [String] the keyword associated with this conditional - attr_reader :keyword - - # [IfMod | UnlessMod] the node that is being formatted - attr_reader :node - - def initialize(keyword, node) - @keyword = keyword - @node = node - end - - def format(q) - if ContainsAssignment.call(node.statement) || q.parent.is_a?(In) - q.group { format_flat(q) } - else - q.group { q.if_break { format_break(q) }.if_flat { format_flat(q) } } - end - end - - private - - def format_break(q) - q.text("#{keyword} ") - q.nest(keyword.length + 1) { q.format(node.predicate) } - q.indent do - q.breakable_space - q.format(node.statement) - end - q.breakable_space - q.text("end") - end - - def format_flat(q) - Parentheses.flat(q) do - q.format(node.statement) - q.text(" #{keyword} ") - q.format(node.predicate) - end - end - end - - # IfMod represents the modifier form of an +if+ statement. - # - # expression if predicate - # - class IfMod < Node - # [untyped] the expression to be executed - attr_reader :statement - - # [untyped] the expression to be checked - attr_reader :predicate - - # [Array[ Comment | EmbDoc ]] the comments attached to this node - attr_reader :comments - - def initialize(statement:, predicate:, location:, comments: []) - @statement = statement - @predicate = predicate - @location = location - @comments = comments - end - - def accept(visitor) - visitor.visit_if_mod(self) - end - - def child_nodes - [statement, predicate] + q.breakable_space + q.text("end") + end end - alias deconstruct child_nodes + def format_flat(q) + q.format(predicate) + q.text(" ?") - def deconstruct_keys(_keys) - { - statement: statement, - predicate: predicate, - location: location, - comments: comments - } - end + q.indent do + q.breakable_space + q.format(truthy) + q.text(" :") - def format(q) - ConditionalModFormatter.new("if", self).format(q) + q.breakable_space + q.format(falsy) + end end end @@ -5803,10 +5461,10 @@ class Imaginary < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -5848,12 +5506,12 @@ class In < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(pattern:, statements:, consequent:, location:, comments: []) + def initialize(pattern:, statements:, consequent:, location:) @pattern = pattern @statements = statements @consequent = consequent @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -5909,10 +5567,10 @@ class Int < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -5953,10 +5611,10 @@ class IVar < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -6001,11 +5659,11 @@ class Kw < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @name = value.to_sym @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -6039,10 +5697,10 @@ class KwRestParam < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(name:, location:, comments: []) + def initialize(name:, location:) @name = name @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -6085,10 +5743,10 @@ class Label < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -6155,11 +5813,11 @@ class Lambda < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(params:, statements:, location:, comments: []) + def initialize(params:, statements:, location:) @params = params @statements = statements @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -6263,11 +5921,11 @@ class LambdaVar < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(params:, locals:, location:, comments: []) + def initialize(params:, locals:, location:) @params = params @locals = locals @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -6306,10 +5964,10 @@ class LBrace < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -6339,10 +5997,10 @@ class LBracket < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -6372,10 +6030,10 @@ class LParen < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -6421,11 +6079,11 @@ class MAssign < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(target:, value:, location:, comments: []) + def initialize(target:, value:, location:) @target = target @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -6459,20 +6117,20 @@ def format(q) # method {} # class MethodAddBlock < Node - # [Call | Command | CommandCall | FCall] the method call + # [Call | Command | CommandCall] the method call attr_reader :call - # [BraceBlock | DoBlock] the block being sent with the method call + # [Block] the block being sent with the method call attr_reader :block # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(call:, block:, location:, comments: []) + def initialize(call:, block:, location:) @call = call @block = block @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -6529,11 +6187,11 @@ class MLHS < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(parts:, comma: false, location:, comments: []) + def initialize(parts:, comma: false, location:) @parts = parts @comma = comma @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -6573,11 +6231,11 @@ class MLHSParen < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(contents:, comma: false, location:, comments: []) + def initialize(contents:, comma: false, location:) @contents = contents @comma = comma @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -6631,11 +6289,11 @@ class ModuleDeclaration < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(constant:, bodystmt:, location:, comments: []) + def initialize(constant:, bodystmt:, location:) @constant = constant @bodystmt = bodystmt @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -6701,10 +6359,10 @@ class MRHS < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(parts:, location:, comments: []) + def initialize(parts:, location:) @parts = parts @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -6750,10 +6408,10 @@ class Next < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(arguments:, location:, comments: []) + def initialize(arguments:, location:) @arguments = arguments @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -6790,11 +6448,11 @@ class Op < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @name = value.to_sym @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -6835,12 +6493,12 @@ class OpAssign < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(target:, operator:, value:, location:, comments: []) + def initialize(target:, operator:, value:, location:) @target = target @operator = operator @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -7081,7 +6739,7 @@ def initialize( @keyword_rest = keyword_rest @block = block @location = location - @comments = comments + @comments = [] end # Params nodes are the most complicated in the tree. Occasionally you want @@ -7145,8 +6803,7 @@ def format(q) return end - case q.parent - when Def, Defs, DefEndless + if q.parent.is_a?(Def) q.nest(0) do q.text("(") q.group do @@ -7187,11 +6844,11 @@ class Paren < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(lparen:, contents:, location:, comments: []) + def initialize(lparen:, contents:, location:) @lparen = lparen @contents = contents @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -7239,10 +6896,10 @@ class Period < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -7272,10 +6929,10 @@ class Program < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(statements:, location:, comments: []) + def initialize(statements:, location:) @statements = statements @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -7316,11 +6973,11 @@ class QSymbols < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(beginning:, elements:, location:, comments: []) + def initialize(beginning:, elements:, location:) @beginning = beginning @elements = elements @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -7410,11 +7067,11 @@ class QWords < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(beginning:, elements:, location:, comments: []) + def initialize(beginning:, elements:, location:) @beginning = beginning @elements = elements @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -7501,10 +7158,10 @@ class RationalLiteral < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -7581,16 +7238,12 @@ def deconstruct_keys(_keys) # redo # class Redo < Node - # [String] the value of the keyword - attr_reader :value - # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) - @value = value + def initialize(location:) @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -7604,11 +7257,11 @@ def child_nodes alias deconstruct child_nodes def deconstruct_keys(_keys) - { value: value, location: location, comments: comments } + { location: location, comments: comments } end def format(q) - q.text(value) + q.text("redo") end end @@ -7732,12 +7385,12 @@ class RegexpLiteral < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(beginning:, ending:, parts:, location:, comments: []) + def initialize(beginning:, ending:, parts:, location:) @beginning = beginning @ending = ending @parts = parts @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -7843,11 +7496,11 @@ class RescueEx < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(exceptions:, variable:, location:, comments: []) + def initialize(exceptions:, variable:, location:) @exceptions = exceptions @variable = variable @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -7919,7 +7572,7 @@ def initialize( @statements = statements @consequent = consequent @location = location - @comments = comments + @comments = [] end def bind_end(end_char, end_column) @@ -8004,11 +7657,11 @@ class RescueMod < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(statement:, value:, location:, comments: []) + def initialize(statement:, value:, location:) @statement = statement @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -8061,10 +7714,10 @@ class RestParam < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(name:, location:, comments: []) + def initialize(name:, location:) @name = name @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -8092,16 +7745,12 @@ def format(q) # retry # class Retry < Node - # [String] the value of the keyword - attr_reader :value - # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) - @value = value + def initialize(location:) @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -8115,11 +7764,11 @@ def child_nodes alias deconstruct child_nodes def deconstruct_keys(_keys) - { value: value, location: location, comments: comments } + { location: location, comments: comments } end def format(q) - q.text(value) + q.text("retry") end end @@ -8128,16 +7777,16 @@ def format(q) # return value # class Return < Node - # [Args] the arguments being passed to the keyword + # [nil | Args] the arguments being passed to the keyword attr_reader :arguments # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(arguments:, location:, comments: []) + def initialize(arguments:, location:) @arguments = arguments @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -8159,42 +7808,6 @@ def format(q) end end - # Return0 represents the bare +return+ keyword with no arguments. - # - # return - # - class Return0 < Node - # [String] the value of the keyword - attr_reader :value - - # [Array[ Comment | EmbDoc ]] the comments attached to this node - attr_reader :comments - - def initialize(value:, location:, comments: []) - @value = value - @location = location - @comments = comments - end - - def accept(visitor) - visitor.visit_return0(self) - end - - def child_nodes - [] - end - - alias deconstruct child_nodes - - def deconstruct_keys(_keys) - { value: value, location: location, comments: comments } - end - - def format(q) - q.text(value) - end - end - # RParen represents the use of a right parenthesis, i.e., +)+. class RParen < Node # [String] the parenthesis @@ -8237,11 +7850,11 @@ class SClass < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(target:, bodystmt:, location:, comments: []) + def initialize(target:, bodystmt:, location:) @target = target @bodystmt = bodystmt @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -8294,11 +7907,11 @@ class Statements < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(parser, body:, location:, comments: []) + def initialize(parser, body:, location:) @parser = parser @body = body @location = location - @comments = comments + @comments = [] end def bind(start_char, start_column, end_char, end_column) @@ -8498,11 +8111,11 @@ class StringConcat < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(left:, right:, location:, comments: []) + def initialize(left:, right:, location:) @left = left @right = right @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -8544,10 +8157,10 @@ class StringDVar < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(variable:, location:, comments: []) + def initialize(variable:, location:) @variable = variable @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -8584,10 +8197,10 @@ class StringEmbExpr < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(statements:, location:, comments: []) + def initialize(statements:, location:) @statements = statements @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -8646,11 +8259,11 @@ class StringLiteral < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(parts:, quote:, location:, comments: []) + def initialize(parts:, quote:, location:) @parts = parts @quote = quote @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -8721,10 +8334,10 @@ class Super < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(arguments:, location:, comments: []) + def initialize(arguments:, location:) @arguments = arguments @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -8838,10 +8451,10 @@ class SymbolLiteral < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -8878,11 +8491,11 @@ class Symbols < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(beginning:, elements:, location:, comments: []) + def initialize(beginning:, elements:, location:) @beginning = beginning @elements = elements @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -9031,10 +8644,10 @@ class TopConstField < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(constant:, location:, comments: []) + def initialize(constant:, location:) @constant = constant @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -9069,10 +8682,10 @@ class TopConstRef < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(constant:, location:, comments: []) + def initialize(constant:, location:) @constant = constant @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -9144,10 +8757,10 @@ class TStringContent < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def match?(pattern) @@ -9222,11 +8835,11 @@ class Not < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(statement:, parentheses:, location:, comments: []) + def initialize(statement:, parentheses:, location:) @statement = statement @parentheses = parentheses @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -9288,11 +8901,11 @@ class Unary < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(operator:, statement:, location:, comments: []) + def initialize(operator:, statement:, location:) @operator = operator @statement = statement @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -9344,300 +8957,125 @@ def comments end end - def format(q) - node.is_a?(SymbolLiteral) ? q.format(node.value) : q.format(node) - end - end - - # [Array[ DynaSymbol | SymbolLiteral ]] the symbols to undefine - attr_reader :symbols - - # [Array[ Comment | EmbDoc ]] the comments attached to this node - attr_reader :comments - - def initialize(symbols:, location:, comments: []) - @symbols = symbols - @location = location - @comments = comments - end - - def accept(visitor) - visitor.visit_undef(self) - end - - def child_nodes - symbols - end - - alias deconstruct child_nodes - - def deconstruct_keys(_keys) - { symbols: symbols, location: location, comments: comments } - end - - def format(q) - keyword = "undef " - formatters = symbols.map { |symbol| UndefArgumentFormatter.new(symbol) } - - q.group do - q.text(keyword) - q.nest(keyword.length) do - q.seplist(formatters) { |formatter| q.format(formatter) } - end - end - end - end - - # Unless represents the first clause in an +unless+ chain. - # - # unless predicate - # end - # - class Unless < Node - # [untyped] the expression to be checked - attr_reader :predicate - - # [Statements] the expressions to be executed - attr_reader :statements - - # [nil, Elsif, Else] the next clause in the chain - attr_reader :consequent - - # [Array[ Comment | EmbDoc ]] the comments attached to this node - attr_reader :comments - - def initialize( - predicate:, - statements:, - consequent:, - location:, - comments: [] - ) - @predicate = predicate - @statements = statements - @consequent = consequent - @location = location - @comments = comments - end - - def accept(visitor) - visitor.visit_unless(self) - end - - def child_nodes - [predicate, statements, consequent] - end - - alias deconstruct child_nodes - - def deconstruct_keys(_keys) - { - predicate: predicate, - statements: statements, - consequent: consequent, - location: location, - comments: comments - } - end - - def format(q) - ConditionalFormatter.new("unless", self).format(q) - end - end - - # UnlessMod represents the modifier form of an +unless+ statement. - # - # expression unless predicate - # - class UnlessMod < Node - # [untyped] the expression to be executed - attr_reader :statement - - # [untyped] the expression to be checked - attr_reader :predicate - - # [Array[ Comment | EmbDoc ]] the comments attached to this node - attr_reader :comments - - def initialize(statement:, predicate:, location:, comments: []) - @statement = statement - @predicate = predicate - @location = location - @comments = comments - end - - def accept(visitor) - visitor.visit_unless_mod(self) - end - - def child_nodes - [statement, predicate] - end - - alias deconstruct child_nodes - - def deconstruct_keys(_keys) - { - statement: statement, - predicate: predicate, - location: location, - comments: comments - } - end - - def format(q) - ConditionalModFormatter.new("unless", self).format(q) - end - end - - # Formats an Until, UntilMod, While, or WhileMod node. - class LoopFormatter - # [String] the name of the keyword used for this loop - attr_reader :keyword - - # [Until | UntilMod | While | WhileMod] the node that is being formatted - attr_reader :node - - # [untyped] the statements associated with the node - attr_reader :statements - - def initialize(keyword, node, statements) - @keyword = keyword - @node = node - @statements = statements - end - - def format(q) - if ContainsAssignment.call(node.predicate) - format_break(q) - q.break_parent - return - end - - q.group do - q - .if_break { format_break(q) } - .if_flat do - Parentheses.flat(q) do - q.format(statements) - q.text(" #{keyword} ") - q.format(node.predicate) - end - end - end - end - - private - - def format_break(q) - q.text("#{keyword} ") - q.nest(keyword.length + 1) { q.format(node.predicate) } - q.indent do - q.breakable_empty - q.format(statements) - end - q.breakable_empty - q.text("end") - end - end - - # Until represents an +until+ loop. - # - # until predicate - # end - # - class Until < Node - # [untyped] the expression to be checked - attr_reader :predicate + def format(q) + node.is_a?(SymbolLiteral) ? q.format(node.value) : q.format(node) + end + end - # [Statements] the expressions to be executed - attr_reader :statements + # [Array[ DynaSymbol | SymbolLiteral ]] the symbols to undefine + attr_reader :symbols # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(predicate:, statements:, location:, comments: []) - @predicate = predicate - @statements = statements + def initialize(symbols:, location:) + @symbols = symbols @location = location - @comments = comments + @comments = [] end def accept(visitor) - visitor.visit_until(self) + visitor.visit_undef(self) end def child_nodes - [predicate, statements] + symbols end alias deconstruct child_nodes def deconstruct_keys(_keys) - { - predicate: predicate, - statements: statements, - location: location, - comments: comments - } + { symbols: symbols, location: location, comments: comments } end def format(q) - if statements.empty? - keyword = "until " + keyword = "undef " + formatters = symbols.map { |symbol| UndefArgumentFormatter.new(symbol) } - q.group do - q.text(keyword) - q.nest(keyword.length) { q.format(predicate) } - q.breakable_force - q.text("end") + q.group do + q.text(keyword) + q.nest(keyword.length) do + q.seplist(formatters) { |formatter| q.format(formatter) } end - else - LoopFormatter.new("until", self, statements).format(q) end end end - # UntilMod represents the modifier form of a +until+ loop. + # Unless represents the first clause in an +unless+ chain. # - # expression until predicate + # unless predicate + # end # - class UntilMod < Node - # [untyped] the expression to be executed - attr_reader :statement - + class Unless < Node # [untyped] the expression to be checked attr_reader :predicate + # [Statements] the expressions to be executed + attr_reader :statements + + # [nil, Elsif, Else] the next clause in the chain + attr_reader :consequent + # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(statement:, predicate:, location:, comments: []) - @statement = statement + def initialize( + predicate:, + statements:, + consequent:, + location:, + comments: [] + ) @predicate = predicate + @statements = statements + @consequent = consequent @location = location - @comments = comments + @comments = [] end def accept(visitor) - visitor.visit_until_mod(self) + visitor.visit_unless(self) end def child_nodes - [statement, predicate] + [predicate, statements, consequent] end alias deconstruct child_nodes def deconstruct_keys(_keys) { - statement: statement, predicate: predicate, + statements: statements, + consequent: consequent, location: location, comments: comments } end + def format(q) + ConditionalFormatter.new("unless", self).format(q) + end + + # Checks if the node was originally found in the modifier form. + def modifier? + predicate.location.start_char > statements.location.start_char + end + end + + # Formats an Until or While node. + class LoopFormatter + # [String] the name of the keyword used for this loop + attr_reader :keyword + + # [Until | While] the node that is being formatted + attr_reader :node + + def initialize(keyword, node) + @keyword = keyword + @node = node + end + def format(q) # If we're in the modifier form and we're modifying a `begin`, then this # is a special case where we need to explicitly use the modifier form @@ -9645,67 +9083,105 @@ def format(q) # # begin # foo - # end until bar + # end while bar # # Also, if the statement of the modifier includes an assignment, then we # can't know for certain that it won't impact the predicate, so we need to # force it to stay as it is. This looks like: # - # foo = bar until foo + # foo = bar while foo # - if statement.is_a?(Begin) || ContainsAssignment.call(statement) + if node.modifier? && (statement = node.statements.body.first) && + (statement.is_a?(Begin) || ContainsAssignment.call(statement)) q.format(statement) - q.text(" until ") - q.format(predicate) + q.text(" #{keyword} ") + q.format(node.predicate) + elsif node.statements.empty? + q.group do + q.text("#{keyword} ") + q.nest(keyword.length + 1) { q.format(node.predicate) } + q.breakable_force + q.text("end") + end + elsif ContainsAssignment.call(node.predicate) + format_break(q) + q.break_parent else - LoopFormatter.new("until", self, statement).format(q) + q.group do + q + .if_break { format_break(q) } + .if_flat do + Parentheses.flat(q) do + q.format(node.statements) + q.text(" #{keyword} ") + q.format(node.predicate) + end + end + end + end + end + + private + + def format_break(q) + q.text("#{keyword} ") + q.nest(keyword.length + 1) { q.format(node.predicate) } + q.indent do + q.breakable_empty + q.format(node.statements) end + q.breakable_empty + q.text("end") end end - # VarAlias represents when you're using the +alias+ keyword with global - # variable arguments. + # Until represents an +until+ loop. # - # alias $new $old + # until predicate + # end # - class VarAlias < Node - # [GVar] the new alias of the variable - attr_reader :left + class Until < Node + # [untyped] the expression to be checked + attr_reader :predicate - # [Backref | GVar] the current name of the variable to be aliased - attr_reader :right + # [Statements] the expressions to be executed + attr_reader :statements # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(left:, right:, location:, comments: []) - @left = left - @right = right + def initialize(predicate:, statements:, location:) + @predicate = predicate + @statements = statements @location = location - @comments = comments + @comments = [] end def accept(visitor) - visitor.visit_var_alias(self) + visitor.visit_until(self) end def child_nodes - [left, right] + [predicate, statements] end alias deconstruct child_nodes def deconstruct_keys(_keys) - { left: left, right: right, location: location, comments: comments } + { + predicate: predicate, + statements: statements, + location: location, + comments: comments + } end def format(q) - keyword = "alias " + LoopFormatter.new("until", self).format(q) + end - q.text(keyword) - q.format(left) - q.text(" ") - q.format(right) + def modifier? + predicate.location.start_char > statements.location.start_char end end @@ -9722,10 +9198,10 @@ class VarField < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -9766,10 +9242,10 @@ class VarRef < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -9830,10 +9306,10 @@ class PinnedVarRef < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -9870,10 +9346,10 @@ class VCall < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) + def initialize(value:, location:) @value = value @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -9910,9 +9386,9 @@ class VoidStmt < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(location:, comments: []) + def initialize(location:) @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -9963,7 +9439,7 @@ def initialize( @statements = statements @consequent = consequent @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -10019,9 +9495,7 @@ def format(q) # last argument to the predicate is and endless range, then you are # forced to use the "then" keyword to make it parse properly. last = arguments.parts.last - if (last.is_a?(Dot2) || last.is_a?(Dot3)) && !last.right - q.text(" then") - end + q.text(" then") if last.is_a?(RangeLiteral) && !last.right end end @@ -10055,11 +9529,11 @@ class While < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(predicate:, statements:, location:, comments: []) + def initialize(predicate:, statements:, location:) @predicate = predicate @statements = statements @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -10082,83 +9556,11 @@ def deconstruct_keys(_keys) end def format(q) - if statements.empty? - keyword = "while " - - q.group do - q.text(keyword) - q.nest(keyword.length) { q.format(predicate) } - q.breakable_force - q.text("end") - end - else - LoopFormatter.new("while", self, statements).format(q) - end - end - end - - # WhileMod represents the modifier form of a +while+ loop. - # - # expression while predicate - # - class WhileMod < Node - # [untyped] the expression to be executed - attr_reader :statement - - # [untyped] the expression to be checked - attr_reader :predicate - - # [Array[ Comment | EmbDoc ]] the comments attached to this node - attr_reader :comments - - def initialize(statement:, predicate:, location:, comments: []) - @statement = statement - @predicate = predicate - @location = location - @comments = comments - end - - def accept(visitor) - visitor.visit_while_mod(self) - end - - def child_nodes - [statement, predicate] - end - - alias deconstruct child_nodes - - def deconstruct_keys(_keys) - { - statement: statement, - predicate: predicate, - location: location, - comments: comments - } + LoopFormatter.new("while", self).format(q) end - def format(q) - # If we're in the modifier form and we're modifying a `begin`, then this - # is a special case where we need to explicitly use the modifier form - # because otherwise the semantic meaning changes. This looks like: - # - # begin - # foo - # end while bar - # - # Also, if the statement of the modifier includes an assignment, then we - # can't know for certain that it won't impact the predicate, so we need to - # force it to stay as it is. This looks like: - # - # foo = bar while foo - # - if statement.is_a?(Begin) || ContainsAssignment.call(statement) - q.format(statement) - q.text(" while ") - q.format(predicate) - else - LoopFormatter.new("while", self, statement).format(q) - end + def modifier? + predicate.location.start_char > statements.location.start_char end end @@ -10177,10 +9579,10 @@ class Word < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(parts:, location:, comments: []) + def initialize(parts:, location:) @parts = parts @location = location - @comments = comments + @comments = [] end def match?(pattern) @@ -10220,11 +9622,11 @@ class Words < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(beginning:, elements:, location:, comments: []) + def initialize(beginning:, elements:, location:) @beginning = beginning @elements = elements @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -10342,10 +9744,10 @@ class XStringLiteral < Node # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(parts:, location:, comments: []) + def initialize(parts:, location:) @parts = parts @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -10374,16 +9776,16 @@ def format(q) # yield value # class Yield < Node - # [Args | Paren] the arguments passed to the yield + # [nil | Args | Paren] the arguments passed to the yield attr_reader :arguments # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(arguments:, location:, comments: []) + def initialize(arguments:, location:) @arguments = arguments @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -10401,6 +9803,11 @@ def deconstruct_keys(_keys) end def format(q) + if arguments.nil? + q.text("yield") + return + end + q.group do q.text("yield") @@ -10419,57 +9826,17 @@ def format(q) end end - # Yield0 represents the bare +yield+ keyword with no arguments. - # - # yield - # - class Yield0 < Node - # [String] the value of the keyword - attr_reader :value - - # [Array[ Comment | EmbDoc ]] the comments attached to this node - attr_reader :comments - - def initialize(value:, location:, comments: []) - @value = value - @location = location - @comments = comments - end - - def accept(visitor) - visitor.visit_yield0(self) - end - - def child_nodes - [] - end - - alias deconstruct child_nodes - - def deconstruct_keys(_keys) - { value: value, location: location, comments: comments } - end - - def format(q) - q.text(value) - end - end - # ZSuper represents the bare +super+ keyword with no arguments. # # super # class ZSuper < Node - # [String] the value of the keyword - attr_reader :value - # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(value:, location:, comments: []) - @value = value + def initialize(location:) @location = location - @comments = comments + @comments = [] end def accept(visitor) @@ -10483,11 +9850,11 @@ def child_nodes alias deconstruct child_nodes def deconstruct_keys(_keys) - { value: value, location: location, comments: comments } + { location: location, comments: comments } end def format(q) - q.text(value) + q.text("super") end end end diff --git a/lib/syntax_tree/parser.rb b/lib/syntax_tree/parser.rb index 61a7ca57..cd14672e 100644 --- a/lib/syntax_tree/parser.rb +++ b/lib/syntax_tree/parser.rb @@ -575,7 +575,7 @@ def on_args_add_star(arguments, argument) def on_args_forward op = consume_operator(:"...") - ArgsForward.new(value: op.value, location: op.location) + ArgsForward.new(location: op.location) end # :call-seq: @@ -920,7 +920,7 @@ def on_bodystmt(statements, rescue_clause, else_clause, ensure_clause) # on_brace_block: ( # (nil | BlockVar) block_var, # Statements statements - # ) -> BraceBlock + # ) -> Block def on_brace_block(block_var, statements) lbrace = consume_token(LBrace) rbrace = consume_token(RBrace) @@ -947,10 +947,10 @@ def on_brace_block(block_var, statements) end_column: rbrace.location.end_column ) - BraceBlock.new( - lbrace: lbrace, + Block.new( + opening: lbrace, block_var: block_var, - statements: statements, + bodystmt: statements, location: location ) end @@ -1076,6 +1076,7 @@ def on_command(message, arguments) Command.new( message: message, arguments: arguments, + block: nil, location: message.location.to(arguments.location) ) end @@ -1095,6 +1096,7 @@ def on_command_call(receiver, operator, message, arguments) operator: operator, message: message, arguments: arguments, + block: nil, location: receiver.location.to(ending.location) ) end @@ -1181,7 +1183,7 @@ def on_cvar(value) # (Backtick | Const | Ident | Kw | Op) name, # (nil | Params | Paren) params, # untyped bodystmt - # ) -> Def | DefEndless + # ) -> Def def on_def(name, params, bodystmt) # Make sure to delete this token in case you're defining something like # def class which would lead to this being a kw and causing all kinds of @@ -1224,6 +1226,8 @@ def on_def(name, params, bodystmt) ) Def.new( + target: nil, + operator: nil, name: name, params: params, bodystmt: bodystmt, @@ -1234,12 +1238,12 @@ def on_def(name, params, bodystmt) # the statements list. Before, it was just the individual statement. statement = bodystmt.is_a?(BodyStmt) ? bodystmt.statements : bodystmt - DefEndless.new( + Def.new( target: nil, operator: nil, name: name, - paren: params, - statement: statement, + params: params, + bodystmt: statement, location: beginning.location.to(bodystmt.location) ) end @@ -1270,7 +1274,7 @@ def on_defined(value) # (Backtick | Const | Ident | Kw | Op) name, # (Params | Paren) params, # BodyStmt bodystmt - # ) -> Defs + # ) -> Def def on_defs(target, operator, name, params, bodystmt) # Make sure to delete this token in case you're defining something # like def class which would lead to this being a kw and causing all kinds @@ -1309,7 +1313,7 @@ def on_defs(target, operator, name, params, bodystmt) ending.location.start_column ) - Defs.new( + Def.new( target: target, operator: operator, name: name, @@ -1322,19 +1326,19 @@ def on_defs(target, operator, name, params, bodystmt) # the statements list. Before, it was just the individual statement. statement = bodystmt.is_a?(BodyStmt) ? bodystmt.statements : bodystmt - DefEndless.new( + Def.new( target: target, operator: operator, name: name, - paren: params, - statement: statement, + params: params, + bodystmt: statement, location: beginning.location.to(bodystmt.location) ) end end # :call-seq: - # on_do_block: (BlockVar block_var, BodyStmt bodystmt) -> DoBlock + # on_do_block: (BlockVar block_var, BodyStmt bodystmt) -> Block def on_do_block(block_var, bodystmt) beginning = consume_keyword(:do) ending = consume_keyword(:end) @@ -1348,8 +1352,8 @@ def on_do_block(block_var, bodystmt) ending.location.start_column ) - DoBlock.new( - keyword: beginning, + Block.new( + opening: beginning, block_var: block_var, bodystmt: bodystmt, location: beginning.location.to(ending.location) @@ -1357,30 +1361,32 @@ def on_do_block(block_var, bodystmt) end # :call-seq: - # on_dot2: ((nil | untyped) left, (nil | untyped) right) -> Dot2 + # on_dot2: ((nil | untyped) left, (nil | untyped) right) -> RangeLiteral def on_dot2(left, right) operator = consume_operator(:"..") beginning = left || operator ending = right || operator - Dot2.new( + RangeLiteral.new( left: left, + operator: operator, right: right, location: beginning.location.to(ending.location) ) end # :call-seq: - # on_dot3: ((nil | untyped) left, (nil | untyped) right) -> Dot3 + # on_dot3: ((nil | untyped) left, (nil | untyped) right) -> RangeLiteral def on_dot3(left, right) operator = consume_operator(:"...") beginning = left || operator ending = right || operator - Dot3.new( + RangeLiteral.new( left: left, + operator: operator, right: right, location: beginning.location.to(ending.location) ) @@ -1608,9 +1614,15 @@ def on_excessed_comma(*) end # :call-seq: - # on_fcall: ((Const | Ident) value) -> FCall + # on_fcall: ((Const | Ident) value) -> Call def on_fcall(value) - FCall.new(value: value, arguments: nil, location: value.location) + Call.new( + receiver: nil, + operator: nil, + message: value, + arguments: nil, + location: value.location + ) end # :call-seq: @@ -1911,13 +1923,15 @@ def on_ifop(predicate, truthy, falsy) end # :call-seq: - # on_if_mod: (untyped predicate, untyped statement) -> IfMod + # on_if_mod: (untyped predicate, untyped statement) -> If def on_if_mod(predicate, statement) consume_keyword(:if) - IfMod.new( - statement: statement, + If.new( predicate: predicate, + statements: + Statements.new(self, body: [statement], location: statement.location), + consequent: nil, location: statement.location.to(predicate.location) ) end @@ -2104,17 +2118,20 @@ def on_lambda(params, statements) location = params.contents.location location = location.to(locals.last.location) if locals.any? - Paren.new( - lparen: params.lparen, - contents: - LambdaVar.new( - params: params.contents, - locals: locals, - location: location - ), - location: params.location, - comments: params.comments - ) + node = + Paren.new( + lparen: params.lparen, + contents: + LambdaVar.new( + params: params.contents, + locals: locals, + location: location + ), + location: params.location + ) + + node.comments.concat(params.comments) + node when Params # In this case we've gotten to the <3.2 plain set of parameters. In # this case there cannot be lambda locals, so we will wrap the @@ -2302,37 +2319,60 @@ def on_massign(target, value) # :call-seq: # on_method_add_arg: ( - # (Call | FCall) call, + # Call call, # (ArgParen | Args) arguments - # ) -> Call | FCall + # ) -> Call def on_method_add_arg(call, arguments) location = call.location location = location.to(arguments.location) if arguments.is_a?(ArgParen) - if call.is_a?(FCall) - FCall.new(value: call.value, arguments: arguments, location: location) - else - Call.new( - receiver: call.receiver, - operator: call.operator, - message: call.message, - arguments: arguments, - location: location - ) - end + Call.new( + receiver: call.receiver, + operator: call.operator, + message: call.message, + arguments: arguments, + location: location + ) end # :call-seq: # on_method_add_block: ( - # (Call | Command | CommandCall | FCall) call, - # (BraceBlock | DoBlock) block + # (Call | Command | CommandCall) call, + # Block block # ) -> MethodAddBlock def on_method_add_block(call, block) - MethodAddBlock.new( - call: call, - block: block, - location: call.location.to(block.location) - ) + case call + when Command + node = + Command.new( + message: call.message, + arguments: call.arguments, + block: block, + location: call.location.to(block.location) + ) + + node.comments.concat(call.comments) + node + when CommandCall + node = + CommandCall.new( + receiver: call.receiver, + operator: call.operator, + message: call.message, + arguments: call.arguments, + block: block, + location: call.location.to(block.location) + ) + + node.comments.concat(call.comments) + node + else + MethodAddBlock.new( + call: call, + block: block, + location: call.location.to(block.location) + ) + end end # :call-seq: @@ -2900,7 +2940,7 @@ def on_rbracket(value) def on_redo keyword = consume_keyword(:redo) - Redo.new(value: keyword.value, location: keyword.location) + Redo.new(location: keyword.location) end # :call-seq: @@ -3066,7 +3106,7 @@ def on_rest_param(name) def on_retry keyword = consume_keyword(:retry) - Retry.new(value: keyword.value, location: keyword.location) + Retry.new(location: keyword.location) end # :call-seq: @@ -3081,11 +3121,11 @@ def on_return(arguments) end # :call-seq: - # on_return0: () -> Return0 + # on_return0: () -> Return def on_return0 keyword = consume_keyword(:return) - Return0.new(value: keyword.value, location: keyword.location) + Return.new(arguments: nil, location: keyword.location) end # :call-seq: @@ -3584,13 +3624,15 @@ def on_unless(predicate, statements, consequent) end # :call-seq: - # on_unless_mod: (untyped predicate, untyped statement) -> UnlessMod + # on_unless_mod: (untyped predicate, untyped statement) -> Unless def on_unless_mod(predicate, statement) consume_keyword(:unless) - UnlessMod.new( - statement: statement, + Unless.new( predicate: predicate, + statements: + Statements.new(self, body: [statement], location: statement.location), + consequent: nil, location: statement.location.to(predicate.location) ) end @@ -3626,23 +3668,24 @@ def on_until(predicate, statements) end # :call-seq: - # on_until_mod: (untyped predicate, untyped statement) -> UntilMod + # on_until_mod: (untyped predicate, untyped statement) -> Until def on_until_mod(predicate, statement) consume_keyword(:until) - UntilMod.new( - statement: statement, + Until.new( predicate: predicate, + statements: + Statements.new(self, body: [statement], location: statement.location), location: statement.location.to(predicate.location) ) end # :call-seq: - # on_var_alias: (GVar left, (Backref | GVar) right) -> VarAlias + # on_var_alias: (GVar left, (Backref | GVar) right) -> Alias def on_var_alias(left, right) keyword = consume_keyword(:alias) - VarAlias.new( + Alias.new( left: left, right: right, location: keyword.location.to(right.location) @@ -3752,13 +3795,14 @@ def on_while(predicate, statements) end # :call-seq: - # on_while_mod: (untyped predicate, untyped statement) -> WhileMod + # on_while_mod: (untyped predicate, untyped statement) -> While def on_while_mod(predicate, statement) consume_keyword(:while) - WhileMod.new( - statement: statement, + While.new( predicate: predicate, + statements: + Statements.new(self, body: [statement], location: statement.location), location: statement.location.to(predicate.location) ) end @@ -3892,11 +3936,11 @@ def on_yield(arguments) end # :call-seq: - # on_yield0: () -> Yield0 + # on_yield0: () -> Yield def on_yield0 keyword = consume_keyword(:yield) - Yield0.new(value: keyword.value, location: keyword.location) + Yield.new(arguments: nil, location: keyword.location) end # :call-seq: @@ -3904,7 +3948,7 @@ def on_yield0 def on_zsuper keyword = consume_keyword(:super) - ZSuper.new(value: keyword.value, location: keyword.location) + ZSuper.new(location: keyword.location) end end end diff --git a/lib/syntax_tree/visitor.rb b/lib/syntax_tree/visitor.rb index e3b52077..57aca619 100644 --- a/lib/syntax_tree/visitor.rb +++ b/lib/syntax_tree/visitor.rb @@ -62,6 +62,9 @@ class Visitor < BasicVisitor # Visit a Binary node. alias visit_binary visit_child_nodes + # Visit a Block node. + alias visit_block visit_child_nodes + # Visit a BlockArg node. alias visit_blockarg visit_child_nodes @@ -71,9 +74,6 @@ class Visitor < BasicVisitor # Visit a BodyStmt node. alias visit_bodystmt visit_child_nodes - # Visit a BraceBlock node. - alias visit_brace_block visit_child_nodes - # Visit a Break node. alias visit_break visit_child_nodes @@ -119,24 +119,9 @@ class Visitor < BasicVisitor # Visit a Def node. alias visit_def visit_child_nodes - # Visit a DefEndless node. - alias visit_def_endless visit_child_nodes - # Visit a Defined node. alias visit_defined visit_child_nodes - # Visit a Defs node. - alias visit_defs visit_child_nodes - - # Visit a DoBlock node. - alias visit_do_block visit_child_nodes - - # Visit a Dot2 node. - alias visit_dot2 visit_child_nodes - - # Visit a Dot3 node. - alias visit_dot3 visit_child_nodes - # Visit a DynaSymbol node. alias visit_dyna_symbol visit_child_nodes @@ -167,9 +152,6 @@ class Visitor < BasicVisitor # Visit an ExcessedComma node. alias visit_excessed_comma visit_child_nodes - # Visit a FCall node. - alias visit_fcall visit_child_nodes - # Visit a Field node. alias visit_field visit_child_nodes @@ -206,9 +188,6 @@ class Visitor < BasicVisitor # Visit an If node. alias visit_if visit_child_nodes - # Visit an IfMod node. - alias visit_if_mod visit_child_nodes - # Visit an IfOp node. alias visit_if_op visit_child_nodes @@ -311,6 +290,9 @@ class Visitor < BasicVisitor # Visit a QWordsBeg node. alias visit_qwords_beg visit_child_nodes + # Visit a RangeLiteral node + alias visit_range_literal visit_child_nodes + # Visit a RAssign node. alias visit_rassign visit_child_nodes @@ -356,9 +338,6 @@ class Visitor < BasicVisitor # Visit a Return node. alias visit_return visit_child_nodes - # Visit a Return0 node. - alias visit_return0 visit_child_nodes - # Visit a RParen node. alias visit_rparen visit_child_nodes @@ -431,18 +410,9 @@ class Visitor < BasicVisitor # Visit an Unless node. alias visit_unless visit_child_nodes - # Visit an UnlessMod node. - alias visit_unless_mod visit_child_nodes - # Visit an Until node. alias visit_until visit_child_nodes - # Visit an UntilMod node. - alias visit_until_mod visit_child_nodes - - # Visit a VarAlias node. - alias visit_var_alias visit_child_nodes - # Visit a VarField node. alias visit_var_field visit_child_nodes @@ -461,9 +431,6 @@ class Visitor < BasicVisitor # Visit a While node. alias visit_while visit_child_nodes - # Visit a WhileMod node. - alias visit_while_mod visit_child_nodes - # Visit a Word node. alias visit_word visit_child_nodes @@ -482,9 +449,6 @@ class Visitor < BasicVisitor # Visit a Yield node. alias visit_yield visit_child_nodes - # Visit a Yield0 node. - alias visit_yield0 visit_child_nodes - # Visit a ZSuper node. alias visit_zsuper visit_child_nodes diff --git a/lib/syntax_tree/visitor/field_visitor.rb b/lib/syntax_tree/visitor/field_visitor.rb index 6c5c6139..01c7de4e 100644 --- a/lib/syntax_tree/visitor/field_visitor.rb +++ b/lib/syntax_tree/visitor/field_visitor.rb @@ -103,7 +103,7 @@ def visit_args(node) end def visit_args_forward(node) - visit_token(node, "args_forward") + node(node, "args_forward") { comments(node) } end def visit_array(node) @@ -184,6 +184,14 @@ def visit_binary(node) end end + def visit_block(node) + node(node, "block") do + field("block_var", node.block_var) if node.block_var + field("bodystmt", node.bodystmt) + comments(node) + end + end + def visit_blockarg(node) node(node, "blockarg") do field("name", node.name) if node.name @@ -209,14 +217,6 @@ def visit_bodystmt(node) end end - def visit_brace_block(node) - node(node, "brace_block") do - field("block_var", node.block_var) if node.block_var - field("statements", node.statements) - comments(node) - end - end - def visit_break(node) node(node, "break") do field("arguments", node.arguments) @@ -315,6 +315,8 @@ def visit_cvar(node) def visit_def(node) node(node, "def") do + field("target", node.target) + field("operator", node.operator) field("name", node.name) field("params", node.params) field("bodystmt", node.bodystmt) @@ -322,20 +324,6 @@ def visit_def(node) end end - def visit_def_endless(node) - node(node, "def_endless") do - if node.target - field("target", node.target) - field("operator", node.operator) - end - - field("name", node.name) - field("paren", node.paren) if node.paren - field("statement", node.statement) - comments(node) - end - end - def visit_defined(node) node(node, "defined") do field("value", node.value) @@ -343,41 +331,6 @@ def visit_defined(node) end end - def visit_defs(node) - node(node, "defs") do - field("target", node.target) - field("operator", node.operator) - field("name", node.name) - field("params", node.params) - field("bodystmt", node.bodystmt) - comments(node) - end - end - - def visit_do_block(node) - node(node, "do_block") do - field("block_var", node.block_var) if node.block_var - field("bodystmt", node.bodystmt) - comments(node) - end - end - - def visit_dot2(node) - node(node, "dot2") do - field("left", node.left) if node.left - field("right", node.right) if node.right - comments(node) - end - end - - def visit_dot3(node) - node(node, "dot3") do - field("left", node.left) if node.left - field("right", node.right) if node.right - comments(node) - end - end - def visit_dyna_symbol(node) node(node, "dyna_symbol") do list("parts", node.parts) @@ -523,14 +476,6 @@ def visit_if(node) end end - def visit_if_mod(node) - node(node, "if_mod") do - field("statement", node.statement) - field("predicate", node.predicate) - comments(node) - end - end - def visit_if_op(node) node(node, "if_op") do field("predicate", node.predicate) @@ -747,6 +692,15 @@ def visit_qwords_beg(node) node(node, "qwords_beg") { field("value", node.value) } end + def visit_range_literal(node) + node(node, "range_literal") do + field("left", node.left) if node.left + field("operator", node.operator) + field("right", node.right) if node.right + comments(node) + end + end + def visit_rassign(node) node(node, "rassign") do field("value", node.value) @@ -769,7 +723,7 @@ def visit_rbracket(node) end def visit_redo(node) - visit_token(node, "redo") + node(node, "redo") { comments(node) } end def visit_regexp_beg(node) @@ -825,7 +779,7 @@ def visit_rest_param(node) end def visit_retry(node) - visit_token(node, "retry") + node(node, "retry") { comments(node) } end def visit_return(node) @@ -835,10 +789,6 @@ def visit_return(node) end end - def visit_return0(node) - visit_token(node, "return0") - end - def visit_rparen(node) node(node, "rparen") { field("value", node.value) } end @@ -982,14 +932,6 @@ def visit_unless(node) end end - def visit_unless_mod(node) - node(node, "unless_mod") do - field("statement", node.statement) - field("predicate", node.predicate) - comments(node) - end - end - def visit_until(node) node(node, "until") do field("predicate", node.predicate) @@ -998,22 +940,6 @@ def visit_until(node) end end - def visit_until_mod(node) - node(node, "until_mod") do - field("statement", node.statement) - field("predicate", node.predicate) - comments(node) - end - end - - def visit_var_alias(node) - node(node, "var_alias") do - field("left", node.left) - field("right", node.right) - comments(node) - end - end - def visit_var_field(node) node(node, "var_field") do field("value", node.value) @@ -1056,14 +982,6 @@ def visit_while(node) end end - def visit_while_mod(node) - node(node, "while_mod") do - field("statement", node.statement) - field("predicate", node.predicate) - comments(node) - end - end - def visit_word(node) node(node, "word") do list("parts", node.parts) @@ -1100,12 +1018,8 @@ def visit_yield(node) end end - def visit_yield0(node) - visit_token(node, "yield0") - end - def visit_zsuper(node) - visit_token(node, "zsuper") + node(node, "zsuper") { comments(node) } end def visit___end__(node) diff --git a/lib/syntax_tree/visitor/with_environment.rb b/lib/syntax_tree/visitor/with_environment.rb index 043cbd4c..59033d50 100644 --- a/lib/syntax_tree/visitor/with_environment.rb +++ b/lib/syntax_tree/visitor/with_environment.rb @@ -56,14 +56,6 @@ def visit_def(node) with_new_environment { super } end - def visit_defs(node) - with_new_environment { super } - end - - def visit_def_endless(node) - with_new_environment { super } - end - # Visit for keeping track of local arguments, such as method and block # arguments def visit_params(node) diff --git a/test/fixtures/def_endless.rb b/test/fixtures/def_endless.rb index 15ea518b..4595fba9 100644 --- a/test/fixtures/def_endless.rb +++ b/test/fixtures/def_endless.rb @@ -4,8 +4,6 @@ def foo = bar def foo(bar) = baz % def foo() = bar -- -def foo = bar % # >= 3.1.0 def foo = bar baz % # >= 3.1.0 @@ -14,8 +12,6 @@ def self.foo = bar def self.foo(bar) = baz % # >= 3.1.0 def self.foo() = bar -- -def self.foo = bar % # >= 3.1.0 def self.foo = bar baz % diff --git a/test/node_test.rb b/test/node_test.rb index ce26f9ea..cbfc6173 100644 --- a/test/node_test.rb +++ b/test/node_test.rb @@ -276,7 +276,7 @@ def test_brace_block source = "method { |variable| variable + 1 }" at = location(chars: 7..34) - assert_node(BraceBlock, source, at: at, &:block) + assert_node(Block, source, at: at, &:block) end def test_break @@ -379,13 +379,13 @@ def method guard_version("3.0.0") do def test_def_endless - assert_node(DefEndless, "def method = result") + assert_node(Def, "def method = result") end end guard_version("3.1.0") do def test_def_endless_command - assert_node(DefEndless, "def method = result argument") + assert_node(Def, "def method = result argument") end end @@ -394,7 +394,7 @@ def test_defined end def test_defs - assert_node(Defs, "def object.method(param) result end") + assert_node(Def, "def object.method(param) result end") end def test_defs_paramless @@ -403,22 +403,22 @@ def object.method end SOURCE - assert_node(Defs, source) + assert_node(Def, source) end def test_do_block source = "method do |variable| variable + 1 end" at = location(chars: 7..37) - assert_node(DoBlock, source, at: at, &:block) + assert_node(Block, source, at: at, &:block) end def test_dot2 - assert_node(Dot2, "1..3") + assert_node(RangeLiteral, "1..3") end def test_dot3 - assert_node(Dot3, "1...3") + assert_node(RangeLiteral, "1...3") end def test_dyna_symbol @@ -487,7 +487,7 @@ def test_excessed_comma end def test_fcall - assert_node(FCall, "method(argument)") + assert_node(Call, "method(argument)") end def test_field @@ -583,7 +583,7 @@ def test_if_op end def test_if_mod - assert_node(IfMod, "expression if predicate") + assert_node(If, "expression if predicate") end def test_imaginary @@ -647,7 +647,7 @@ def test_lbrace source = "method {}" at = location(chars: 7..8) - assert_node(LBrace, source, at: at) { |node| node.block.lbrace } + assert_node(LBrace, source, at: at) { |node| node.block.opening } end def test_lparen @@ -841,7 +841,7 @@ def test_return end def test_return0 - assert_node(Return0, "return") + assert_node(Return, "return") end def test_sclass @@ -926,20 +926,16 @@ def test_unless assert_node(Unless, "unless value then else end") end - def test_unless_mod - assert_node(UnlessMod, "expression unless predicate") - end - def test_until assert_node(Until, "until value do end") end def test_until_mod - assert_node(UntilMod, "expression until predicate") + assert_node(Until, "expression until predicate") end def test_var_alias - assert_node(VarAlias, "alias $new $old") + assert_node(Alias, "alias $new $old") end def test_var_field @@ -985,7 +981,7 @@ def test_while end def test_while_mod - assert_node(WhileMod, "expression while predicate") + assert_node(While, "expression while predicate") end def test_word @@ -1017,7 +1013,7 @@ def test_yield end def test_yield0 - assert_node(Yield0, "yield") + assert_node(Yield, "yield") end def test_zsuper