Skip to content

Commit

Permalink
Add ArgNode for arg, kwarg, optarg, restarg, kwoptarg, `kwr…
Browse files Browse the repository at this point in the history
…estarg`, `blockarg`, `forward_arg` and `shadowarg` types.

- Add `ArgsNode#argument_list` to return all descendant argument type nodes.
- Added Procarg0Node for modernized compatibility with ArgNode.
- Expose `ArgsNode#argument_list` on `BlockNode`.
  • Loading branch information
dvandersluis authored and marcandre committed Nov 24, 2020
1 parent 6dad540 commit 5cd306f
Show file tree
Hide file tree
Showing 14 changed files with 507 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* [#154](https://github.com/rubocop-hq/rubocop-ast/pull/154): Add `BlockNode#argument_list` and `BlockNode#argument_names`. ([@dvandersluis][])
2 changes: 2 additions & 0 deletions changelog/new_add_blocknodeargument_names.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
* [#154](https://github.com/rubocop-hq/rubocop-ast/pull/154): Add `ArgNode` and `Procarg0Node` ("modern" mode), and add `ArgsNode#argument_list` to get only argument type nodes. ([@dvandersluis][])

20 changes: 11 additions & 9 deletions docs/modules/ROOT/pages/node_types.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ The following fields are given when relevant to nodes in the source code:

|and_asgn|And-assignment (AND the receiver with the argument and assign it back to receiver).|First child must be an assignment node, second child is the expression node.|a &&= b |N/A

|arg|Required positional argument. Must come inside an `args`.|One child - a symbol, representing the argument name.|def foo(bar)|N/A
|arg|Required positional argument. Must come inside an `args`.|One child - a symbol, representing the argument name.|def foo(bar)|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ArgNode[ArgNode]

|args|Argument list. Must come inside a `def`, `defs`, `def_e`, `defs_e` or `block` node.|Children must be `arg`, `optarg`, `restarg`, `blockarg`, `kwarg`, `kwoptarg`, `kwrestarg`, `kwnilarg`, or `forwardarg`.|def whatever(foo, bar=1, baz: 5)|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ArgsNode[ArgsNode]

Expand All @@ -70,7 +70,7 @@ The following fields are given when relevant to nodes in the source code:

|block_pass|Used when passing a block as an argument.|One child, an expression node representing the block to pass.|foo(a, &my_block)|N/A

|blockarg|Reference to block argument from a function definition. Must come inside an `args`.|One child - a symbol, representing the argument name.|def foo(&bar)|N/A
|blockarg|Reference to block argument from a function definition. Must come inside an `args`.|One child - a symbol, representing the argument name.|def foo(&bar)|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ArgNode[ArgNode]

|break|break keyword|One child with an expression node for the results to be passed through the break.|break 1|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/BreakNode[BreakNode]

Expand Down Expand Up @@ -114,7 +114,7 @@ The following fields are given when relevant to nodes in the source code:

|for|for..in looping condition|Three children. First child is a `lvasgn` or `mlhs` node with the variable(s), second child is an expression node with the array/range to loop over, third child is a body statement.|for a in arr do foo; end|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ForNode[ForNode]

|forward_arg|Forwarding argument, for Ruby 2.8 (when `emit_forward_arg` is true). Must come inside an `args` node.|None|def whatever(foo, ...)|N/A
|forward_arg|Forwarding argument, for Ruby 2.8 (when `emit_forward_arg` is true). Must come inside an `args` node.|None|def whatever(foo, ...)|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ArgNode[ArgNode]

|forward_args|Forwarding argument list, for Ruby 2.7 (when `emit_forward_arg` is false). Must come inside a `def`, `defs`, `def_e`, or `defs_e` node.|None|def (foo(...)|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ForwardArgsNode[ForwardArgsNode]

Expand All @@ -136,25 +136,25 @@ The following fields are given when relevant to nodes in the source code:

|irange|Inclusive range literal.|Two children, the start and end nodes (including `nil` for beginless/endless)|1..2|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/RangeNode[RangeNode]

|kwarg|Required keyword argument. Must come inside an `args`.|One child - a symbol, representing the argument name.|def foo(bar:)|N/A
|kwarg|Required keyword argument. Must come inside an `args`.|One child - a symbol, representing the argument name.|def foo(bar:)|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ArgNode[ArgNode]

|kwbegin|Explicit `begin` block.|Child nodes are body statements.|begin,end|N/A

|kwnilarg|Double splat with nil in function definition, used to specify that the function does not accept keyword args. Must come inside an `args`.|None|def foo(**nil)|N/A

|kwoptarg|Optional keyword argument. Must come inside an `args`.|Two children - a symbol, representing the argument name, and an expression node for the value.|def foo(bar: 5)|N/A
|kwoptarg|Optional keyword argument. Must come inside an `args`.|Two children - a symbol, representing the argument name, and an expression node for the value.|def foo(bar: 5)|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ArgNode[ArgNode]

|kwsplat|Double splat used for keyword arguments inside a function call (as opposed to a function definition).|One child, an expression.|foo(bar, **kwargs)|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/KeywordSplatNode[KeywordSplatNode]

|kwrestargs|Double splat used for keyword arguments inside a function definition (as opposed to a function call). Must come inside an `args`.|One child - a symbol, representing the argument name, if a name is given. If no name given, it has no children..|def foo(**kwargs)|N/A
|kwrestargs|Double splat used for keyword arguments inside a function definition (as opposed to a function call). Must come inside an `args`.|One child - a symbol, representing the argument name, if a name is given. If no name given, it has no children..|def foo(**kwargs)|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ArgNode[ArgNode]

|lvar|Local variable access|One child, the variable name|foo|N/A

|lvasgn|Local variable assignment|Two children: The variable name (symbol) and the expression.|a = some_thing|N/A

|masgn|Multiple assigment.|First set of children are all `mlhs` nodes, and the rest of the children must be expression nodes corresponding to the values in the `mlhs` nodes.|a, b, = [1, 2]|N/A

|mlhs|Multiple left-hand side. Only used inside a `masgn`.|Children must all be assignment nodes. Represents the left side of a multiple assignment (`a, b` in the example).|a, b = 5, 6|N/A
|mlhs|Multiple left-hand side. Used inside a `masgn` and block argument destructuring.|Children must all be assignment nodes. Represents the left side of a multiple assignment (`a, b` in the example).|a, b = 5, 6|N/A

|module|Module definition|Two children. First child is a `const` node for the module name. Second child is a body statement.|module Foo < Bar; end|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ModuleNode[ModuleNode]

Expand All @@ -168,7 +168,7 @@ The following fields are given when relevant to nodes in the source code:

|op_asgn|Operator-assignment - perform an operation and assign the value.|Three children. First child must be an assignment node, second child is the operator (e.g. `:+`) and the third child is the expression node.|a += b|N/A

|opt_arg|Optional positional argument. Must come inside an `args`.|One child - a symbol, representing the argument name.|def foo(bar=1)|N/A
|optarg|Optional positional argument. Must come inside an `args`.|One child - a symbol, representing the argument name.|def foo(bar=1)|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ArgNode[ArgNode]

|or|Or operator|Two children are both expression nodes representing the operands.|a or b|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/OrNode[OrNode]

Expand All @@ -189,7 +189,7 @@ The following fields are given when relevant to nodes in the source code:

|rescue|A rescue statement.May be "top-level" or may be nested inside an `ensure` block (if both rescue and ensure are in the block).|First node is a body statement. Last child is the "else" body statement, or `nil`. Remaining children are `resbody` nodes.|begin; rescue Exception, A => bar; 1; end|

|restarg|Positional splat argument. Must come inside an `args`.|One child - a symbol, representing the argument name (if given). If no name given, there are no children.|def foo(*rest)|N/A
|restarg|Positional splat argument. Must come inside an `args`.|One child - a symbol, representing the argument name (if given). If no name given, there are no children.|def foo(*rest)|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ArgNode[ArgNode]

|return|Return statement|Zero or one child, an expression node for the value to return.|return|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ReturnNode[ReturnNode]

Expand All @@ -200,6 +200,8 @@ The following fields are given when relevant to nodes in the source code:
|send|Non-safe method invocation (i.e. top-level or using a dot)|First child is the receiver node (e.g. `self`), second child is the method name (e.g. `:foo=`) and the remaining children (if any) are the arguments (expression nodes).
a|`foo` or `foo.bar`|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/SendNode[SendNode]

|shadowarg|Shadow argument, aka block-local variable. Must come inside an `args`.|One child - a symbol, representing the argument name.|foo { \|a; b\| b }|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ArgNode[ArgNode]

|splat|Array or function argument * operator|One child, an expression.|*foo|N/A

|str|Non-interpolated string literal. The heredoc version works very differently from the regular version and the location info is totally separate.|One child, the String content.
Expand Down
2 changes: 2 additions & 0 deletions lib/rubocop/ast.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
require_relative 'ast/node/mixin/basic_literal_node'
require_relative 'ast/node/alias_node'
require_relative 'ast/node/and_node'
require_relative 'ast/node/arg_node'
require_relative 'ast/node/args_node'
require_relative 'ast/node/array_node'
require_relative 'ast/node/block_node'
Expand All @@ -62,6 +63,7 @@
require_relative 'ast/node/next_node'
require_relative 'ast/node/or_node'
require_relative 'ast/node/pair_node'
require_relative 'ast/node/procarg0_node'
require_relative 'ast/node/range_node'
require_relative 'ast/node/regexp_node'
require_relative 'ast/node/rescue_node'
Expand Down
10 changes: 10 additions & 0 deletions lib/rubocop/ast/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ class Builder < Parser::Builders::Default
NODE_MAP = {
and: AndNode,
alias: AliasNode,
arg: ArgNode,
blockarg: ArgNode,
forward_arg: ArgNode,
kwarg: ArgNode,
kwoptarg: ArgNode,
kwrestarg: ArgNode,
optarg: ArgNode,
restarg: ArgNode,
shadowarg: ArgNode,
args: ArgsNode,
array: ArrayNode,
block: BlockNode,
Expand Down Expand Up @@ -49,6 +58,7 @@ class Builder < Parser::Builders::Default
next: NextNode,
or: OrNode,
pair: PairNode,
procarg0: Procarg0Node,
regexp: RegexpNode,
rescue: RescueNode,
resbody: ResbodyNode,
Expand Down
3 changes: 2 additions & 1 deletion lib/rubocop/ast/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ class Node < Parser::AST::Node # rubocop:disable Metrics/ClassLength
# @api private
SPECIAL_KEYWORDS = %w[__FILE__ __LINE__ __ENCODING__].to_set.freeze
# @api private
ARGUMENT_TYPES = %i[arg optarg restarg kwarg kwoptarg kwrestarg blockarg].to_set.freeze
ARGUMENT_TYPES = %i[arg optarg restarg kwarg kwoptarg kwrestarg
blockarg forward_arg shadowarg].to_set.freeze

LITERAL_RECURSIVE_METHODS = (COMPARISON_OPERATORS + %i[* ! <=>]).freeze
LITERAL_RECURSIVE_TYPES = (OPERATOR_KEYWORDS + COMPOSITE_LITERALS + %i[begin pair]).freeze
Expand Down
34 changes: 34 additions & 0 deletions lib/rubocop/ast/node/arg_node.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# frozen_string_literal: true

module RuboCop
module AST
# A node extension for `arg`, `optarg`, `restarg`, `kwarg`, `kwoptarg`,
# `kwrestarg`, `blockarg`, `shadowarg` and `forward_arg` nodes.
# This will be used in place of a plain node when the builder constructs
# the AST, making its methods available to all `arg` nodes within RuboCop.
class ArgNode < Node
# Returns the name of an argument.
#
# @return [Symbol, nil] the name of the argument
def name
node_parts[0]
end

# Returns the default value of the argument, if any.
#
# @return [Node, nil] the default value of the argument
def default_value
return unless default?

node_parts[1]
end

# Checks whether the argument has a default value
#
# @return [Boolean] whether the argument has a default value
def default?
optarg_type? || kwoptarg_type?
end
end
end
end
10 changes: 10 additions & 0 deletions lib/rubocop/ast/node/args_node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ class ArgsNode < Node
def empty_and_without_delimiters?
loc.expression.nil?
end

# Yield each argument from the collection.
# Arguments can be inside `mlhs` nodes in the case of destructuring, so this
# flattens the collection to just `arg`, `optarg`, `restarg`, `kwarg`,
# `kwoptarg`, `kwrestarg`, `blockarg`, `forward_arg` and `shadowarg`.
#
# @return [Array<Node>] array of argument nodes.
def argument_list
each_descendant(*ARGUMENT_TYPES).to_a.freeze
end
end
end
end
13 changes: 13 additions & 0 deletions lib/rubocop/ast/node/block_node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ def send_node
end

# The arguments of this block.
# Note that if the block has destructured arguments, `arguments` will
# return a `mlhs` node, whereas `argument_list` will return only
# actual argument nodes.
#
# @return [Array<Node>]
def arguments
Expand All @@ -32,6 +35,16 @@ def arguments
end
end

# Returns a collection of all descendants of this node that are
# argument type nodes. See `ArgsNode#argument_list` for details.
#
# @return [Array<Node>]
def argument_list
return [].freeze unless arguments?

arguments.argument_list
end

# The body of this block.
#
# @return [Node, nil] the body of the `block` node or `nil`
Expand Down
17 changes: 17 additions & 0 deletions lib/rubocop/ast/node/procarg0_node.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# frozen_string_literal: true

module RuboCop
module AST
# A node extension for `procarg0` nodes.
# This will be used in place of a plain node when the builder constructs
# the AST, making its methods available to all `arg` nodes within RuboCop.
class Procarg0Node < ArgNode
# Returns the name of an argument.
#
# @return [Symbol, nil] the name of the argument
def name
node_parts[0].name
end
end
end
end
Loading

0 comments on commit 5cd306f

Please sign in to comment.