From 726a5c9f59f9c499a56ea58ede8db13f88e6fe62 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. --- docs/modules/ROOT/pages/node_pattern.adoc | 35 +++++++++++++++++++++++ lib/rubocop/ast/node_pattern.rb | 5 ++-- spec/rubocop/ast/node_pattern_spec.rb | 10 +++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/node_pattern.adoc b/docs/modules/ROOT/pages/node_pattern.adoc index 6aa8cfe19..1b238298e 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..790d9498c 100644 --- a/spec/rubocop/ast/node_pattern_spec.rb +++ b/spec/rubocop/ast/node_pattern_spec.rb @@ -1146,6 +1146,16 @@ 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 } + + it_behaves_like 'matching' + end end context 'in a nested sequence' do