Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NodePattern: Use param === node to match params. #31

Merged
merged 1 commit into from Jun 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -7,6 +7,7 @@
* [#4](https://github.com/rubocop-hq/rubocop-ast/issues/4): Add `interpolation?` for `RegexpNode`. ([@tejasbubane][])
* [#20](https://github.com/rubocop-hq/rubocop-ast/pull/20): Add option predicates for `RegexpNode`. ([@owst][])
* [#11](https://github.com/rubocop-hq/rubocop-ast/issues/11): Add `argument_type?` method to make it easy to recognize argument nodes. ([@tejasbubane][])
* [#31](https://github.com/rubocop-hq/rubocop-ast/pull/31): Use `param === node` to match params, which allows Regexp, Proc, Set, etc. ([@marcandre][])

## 0.0.3 (2020-05-15)

Expand Down
35 changes: 35 additions & 0 deletions docs/modules/ROOT/pages/node_pattern.adoc
Expand Up @@ -374,6 +374,41 @@ def_node_matcher :initializing_with_user?, <<~PATTERN
PATTERN
----

== `%` for arguments
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 I was looking for this section today.


Arguments can be passed to matchers, either as external method arguments,
or to be used to compare elements. An example of method argument:

[source,ruby]
----
def multiple_of?(n, factor)
n % factor == 0
end

def_node_matcher :int_node_multiple?, '(int #multiple_of?(%1))'

# ...

int_node_multiple?(node, 10) # => true if node is an 'int' node with a multiple of 10
----

Arguments can be used to match nodes directly:

[source,ruby]
----
def_node_matcher :has_sensitive_data?, '(hash <(pair (_ %1) $_) ...>)'

# ...

has_user_data?(node, :password) # => true if node is a hash with a key +:password+

# matching uses ===, so to match strings or symbols, 'pass' or 'password' one can:
has_user_data?(node, /^pass(word)?$/i)

# one can also pass lambdas...
has_user_data?(node, ->(key) { # return true or false depending on key })
----

== `nil` or `nil?`

Take a special attention to nil behavior:
Expand Down
5 changes: 3 additions & 2 deletions lib/rubocop/ast/node_pattern.rb
Expand Up @@ -70,7 +70,8 @@ module AST
# '(send %1 _)' # % stands for a parameter which must be supplied to
# # #match at matching time
# # it will be compared to the corresponding value in
# # the AST using #==
# # the AST using #=== so you can pass Procs, Regexp
# # in addition to Nodes or literals.
# # a bare '%' is the same as '%1'
# # the number of extra parameters passed to #match
# # must equal the highest % value in the pattern
Expand Down Expand Up @@ -612,7 +613,7 @@ def compile_nodetype(type)
end

def compile_param(number)
"#{CUR_ELEMENT} == #{get_param(number)}"
"#{get_param(number)} === #{CUR_ELEMENT}"
end

def compile_args(tokens)
Expand Down
11 changes: 11 additions & 0 deletions spec/rubocop/ast/node_pattern_spec.rb
Expand Up @@ -1146,6 +1146,17 @@
let(:ruby) { '10' }

it_behaves_like 'matching'

context 'in root position' do
let(:pattern) { '%1' }
let(:matcher) { Object.new }
let(:params) { [matcher] }
let(:ruby) { '10' }

before { expect(matcher).to receive(:===).with(s(:int, 10)).and_return true } # rubocop:todo RSpec/ExpectInHook

it_behaves_like 'matching'
end
end

context 'in a nested sequence' do
Expand Down