From 3080ea1b42b9cbaa0d70886d9bff19169905a2f3 Mon Sep 17 00:00:00 2001 From: Marc-Andre Lafortune Date: Thu, 11 Jun 2020 12:31:10 -0400 Subject: [PATCH] NodePattern: Use `param === node` to match params. --- CHANGELOG.md | 1 + docs/modules/ROOT/pages/node_pattern.adoc | 35 +++++++++++++++++++++++ lib/rubocop/ast/node_pattern.rb | 5 ++-- spec/rubocop/ast/node_pattern_spec.rb | 11 +++++++ 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ead5aea4c..66822baa3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/docs/modules/ROOT/pages/node_pattern.adoc b/docs/modules/ROOT/pages/node_pattern.adoc index 6aa8cfe19..b0bf3e227 100644 --- a/docs/modules/ROOT/pages/node_pattern.adoc +++ b/docs/modules/ROOT/pages/node_pattern.adoc @@ -374,6 +374,41 @@ def_node_matcher :initializing_with_user?, <<~PATTERN PATTERN ---- +== `%` for arguments + +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: diff --git a/lib/rubocop/ast/node_pattern.rb b/lib/rubocop/ast/node_pattern.rb index eef631934..e3bc4bbbe 100644 --- a/lib/rubocop/ast/node_pattern.rb +++ b/lib/rubocop/ast/node_pattern.rb @@ -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 @@ -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) diff --git a/spec/rubocop/ast/node_pattern_spec.rb b/spec/rubocop/ast/node_pattern_spec.rb index 7ccfae628..b32856561 100644 --- a/spec/rubocop/ast/node_pattern_spec.rb +++ b/spec/rubocop/ast/node_pattern_spec.rb @@ -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