<commit_before># Node Pattern

Node pattern is a DSL to help find specific nodes in the Abstract Syntax Tree
using a simple string.

It reminds the simplicity of regular expressions but used to find specific
nodes of Ruby code.

## History

The Node Pattern was introduced by [<NAME>](https://github.com/alexdowad)
and solves a problem that RuboCop contributors were facing for a long time:

- Ability to declaratively define rules for node search, matching, and capture.

The code below belongs to [Style/ArrayJoin](https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Style/ArrayJoin)
cop and it's in favor of `Array#join` over `Array#*`. Then it tries to find
code like `%w(one two three) * ", "` and suggest to use `#join` instead.

It can also be an array of integers, and the code doesn't check it. However,
it checks if the argument sent is a string.

```ruby
def on_send(node)
  receiver_node, method_name, *arg_nodes = *node
  return unless receiver_node && receiver_node.array_type? &&
    method_name == :* && arg_nodes.first.str_type?

  add_offense(node, location: :selector)
end
```

This code was replaced in the cop defining a new matcher that does the same as the code above:

```ruby
def_node_matcher :join_candidate?, '(send $array :* $str)'
```

And the `on_send` method is simplified to a method usage:

```ruby
def on_send(node)
  join_candidate?(node) { add_offense(node, location: :selector) }
end
```

## `(` and `)` Navigate deeply with Parens

Parens delimits navigation inside node and its children.

A simple integer like `1` is represented by `(int 1)` in the AST.

```sh
$ ruby-parse -e '1'
(int 1)
```

- `int` will match exactly the node, looking only the node type.
- `(int 1)` will match precisely the node

## `_` for any single node

`_` will check if there's something present in the specific position, no matter the
value:

- `(int _)` will match any number
- `(int _ _)` will not match because `int` types have just one child that
  contains the value.


## `...` for several subsequent nodes

Where `_` matches a single node, `...` eagerly matches one or more subsequent nodes.

It's useful when you want to check some internal nodes but with a
final with the same results. For example, let's use `sum(1,2)`.

We can also have `sum(1,2,3,n)` and the arguments can vary. The objective is
match all. So, let's check how it looks like in the AST:

```sh
$ ruby-parse -e 'sum(1,2)'
(send nil :sum
  (int 1)<commit_msg>Fix node pattern tutorial on `...` matcher<commit_after># Node Pattern

Node pattern is a DSL to help find specific nodes in the Abstract Syntax Tree
using a simple string.

It reminds the simplicity of regular expressions but used to find specific
nodes of Ruby code.

## History

The Node Pattern was introduced by [<NAME>](https://github.com/alexdowad)
and solves a problem that RuboCop contributors were facing for a long time:

- Ability to declaratively define rules for node search, matching, and capture.

The code below belongs to [Style/ArrayJoin](https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Style/ArrayJoin)
cop and it's in favor of `Array#join` over `Array#*`. Then it tries to find
code like `%w(one two three) * ", "` and suggest to use `#join` instead.

It can also be an array of integers, and the code doesn't check it. However,
it checks if the argument sent is a string.

```ruby
def on_send(node)
  receiver_node, method_name, *arg_nodes = *node
  return unless receiver_node && receiver_node.array_type? &&
    method_name == :* && arg_nodes.first.str_type?

  add_offense(node, location: :selector)
end
```

This code was replaced in the cop defining a new matcher that does the same as the code above:

```ruby
def_node_matcher :join_candidate?, '(send $array :* $str)'
```

And the `on_send` method is simplified to a method usage:

```ruby
def on_send(node)
  join_candidate?(node) { add_offense(node, location: :selector) }
end
```

## `(` and `)` Navigate deeply with Parens

Parens delimits navigation inside node and its children.

A simple integer like `1` is represented by `(int 1)` in the AST.

```sh
$ ruby-parse -e '1'
(int 1)
```

- `int` will match exactly the node, looking only the node type.
- `(int 1)` will match precisely the node

## `_` for any single node

`_` will check if there's something present in the specific position, no matter the
value:

- `(int _)` will match any number
- `(int _ _)` will not match because `int` types have just one child that
  contains the value.


## `...` for several subsequent nodes

Where `_` matches a single node, `...` eagerly matches zero or more subsequent nodes.

It's useful when you want to check some internal nodes but with a
final with the same results. For example, let's use `sum(1,2)`.

We can also have `sum(1,2,3,n)` and the arguments can vary. The objective is
match all. So, let's check how it looks like in the AST:

```sh
$ ruby-parse -e 'sum(1,2)'
(send nil :sum
  (int 1)
{'fraction_non_alphanumeric': 0.10356708552898257, 'fraction_numerical': 0.00526955816781516, 'mean_word_length': 4.151356993736952, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 4, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5287714', 'n_tokens_mistral': 1588, 'n_tokens_neox': 1508, 'n_words': 696}