Skip to content

Commit 805cf3d

Browse files
committed
Add Constant parameters to NodePattern
1 parent 4816c2e commit 805cf3d

File tree

4 files changed

+66
-1
lines changed

4 files changed

+66
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* [#20](https://github.com/rubocop-hq/rubocop-ast/pull/20): Add option predicates for `RegexpNode`. ([@owst][])
1010
* [#11](https://github.com/rubocop-hq/rubocop-ast/issues/11): Add `argument_type?` method to make it easy to recognize argument nodes. ([@tejasbubane][])
1111
* [#31](https://github.com/rubocop-hq/rubocop-ast/pull/31): NodePattern now uses `param === node` to match params, which allows Regexp, Proc, Set in addition to Nodes and literals. ([@marcandre][])
12+
* [#35](https://github.com/rubocop-hq/rubocop-ast/pull/35): NodePattern now accepts `%named_param` and `%CONST`. The macros `def_node_pattern` and `def_node_search` accept default named parameters. ([@marcandre][])
1213

1314
## 0.0.3 (2020-05-15)
1415

docs/modules/ROOT/pages/node_pattern.adoc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,23 @@ interesting_call?(node, method: /^transform/) # match anything starting with 'tr
436436

437437
Named parameters as arguments to custom methods are also supported.
438438

439+
== `%CONST` for constants
440+
441+
Constants can be included in patterns. They will be matched using `===`, so
442+
+Regexp+ / +Set+ / +Proc+ can be used in addition to literals and +Nodes+:
443+
444+
[source,ruby]
445+
----
446+
SOME_CALLS = Set[:transform_values, :transform_keys,
447+
:transform_values!, :transform_keys!,
448+
:to_h].freeze
449+
450+
def_node_matcher :interesting_call?, '(send _ %SOME_CALLS ...)'
451+
452+
----
453+
454+
Constants as arguments to custom methods are also supported.
455+
439456
== `nil` or `nil?`
440457

441458
Take a special attention to nil behavior:

lib/rubocop/ast/node_pattern.rb

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ module AST
8686
# # parameters (see `%1`)
8787
# # Note that the macros `def_node_pattern` and
8888
# # `def_node_search` accept default values for these.
89+
# '(send _ %CONST)' # the named constant will act like `%1` and `%named`.
8990
# '^^send' # each ^ ascends one level in the AST
9091
# # so this matches against the grandparent node
9192
# '`send' # descends any number of level in the AST
@@ -129,11 +130,12 @@ class Compiler
129130
NUMBER = /-?\d+(?:\.\d+)?/.freeze
130131
STRING = /".+?"/.freeze
131132
METHOD_NAME = /\#?#{IDENTIFIER}[!?]?\(?/.freeze
133+
PARAM_CONST = /%[A-Z:][a-zA-Z_:]+/.freeze
132134
KEYWORD_NAME = /%[a-z_]+/.freeze
133135
PARAM_NUMBER = /%\d*/.freeze
134136

135137
SEPARATORS = /\s+/.freeze
136-
TOKENS = Regexp.union(META, KEYWORD_NAME, PARAM_NUMBER, NUMBER,
138+
TOKENS = Regexp.union(META, PARAM_CONST, KEYWORD_NAME, PARAM_NUMBER, NUMBER,
137139
METHOD_NAME, SYMBOL, STRING)
138140

139141
TOKEN = /\G(?:#{SEPARATORS}|#{TOKENS}|.)/.freeze
@@ -145,6 +147,7 @@ class Compiler
145147
FUNCALL = /\A\##{METHOD_NAME}/.freeze
146148
LITERAL = /\A(?:#{SYMBOL}|#{NUMBER}|#{STRING})\Z/.freeze
147149
PARAM = /\A#{PARAM_NUMBER}\Z/.freeze
150+
CONST = /\A#{PARAM_CONST}\Z/.freeze
148151
KEYWORD = /\A#{KEYWORD_NAME}\Z/.freeze
149152
CLOSING = /\A(?:\)|\}|\])\Z/.freeze
150153

@@ -245,6 +248,7 @@ def compile_expr(token = tokens.shift)
245248
when PREDICATE then compile_predicate(token)
246249
when NODE then compile_nodetype(token)
247250
when KEYWORD then compile_keyword(token[1..-1])
251+
when CONST then compile_const(token[1..-1])
248252
when PARAM then compile_param(token[1..-1])
249253
when CLOSING then fail_due_to("#{token} in invalid position")
250254
when nil then fail_due_to('pattern ended prematurely')
@@ -628,6 +632,10 @@ def compile_param(number)
628632
"#{get_param(number)} === #{CUR_ELEMENT}"
629633
end
630634

635+
def compile_const(const)
636+
"#{get_const(const)} === #{CUR_ELEMENT}"
637+
end
638+
631639
def compile_keyword(keyword)
632640
"#{get_keyword(keyword)} === #{CUR_ELEMENT}"
633641
end
@@ -649,6 +657,7 @@ def compile_arg(token)
649657
access_unify(name) || fail_due_to('invalid in arglist: ' + token)
650658
when LITERAL then token
651659
when KEYWORD then get_keyword(name)
660+
when CONST then get_const(name)
652661
when PARAM then get_param(name)
653662
when CLOSING then fail_due_to("#{token} in invalid position")
654663
when nil then fail_due_to('pattern ended prematurely')
@@ -673,6 +682,10 @@ def get_keyword(name)
673682
name
674683
end
675684

685+
def get_const(const)
686+
const # Output the constant exactly as given
687+
end
688+
676689
def emit_yield_capture(when_no_capture = '')
677690
yield_val = if @captures.zero?
678691
when_no_capture

spec/rubocop/ast/node_pattern_spec.rb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1162,6 +1162,25 @@
11621162
end
11631163
end
11641164

1165+
context 'with a constant argument' do
1166+
let(:pattern) { '(send (int equal?(%CONST)) ...)' }
1167+
let(:ruby) { '1 + 2' }
1168+
1169+
before { stub_const 'CONST', const_value }
1170+
1171+
context 'for which the predicate is true' do
1172+
let(:const_value) { 1 }
1173+
1174+
it_behaves_like 'matching'
1175+
end
1176+
1177+
context 'for which the predicate is false' do
1178+
let(:const_value) { 2 }
1179+
1180+
it_behaves_like 'nonmatching'
1181+
end
1182+
end
1183+
11651184
context 'with multiple arguments' do
11661185
let(:pattern) { '(str between?(%1, %2))' }
11671186
let(:ruby) { '"c"' }
@@ -2103,5 +2122,20 @@ def withargs(foo, bar, qux)
21032122
end
21042123
end
21052124
end
2125+
2126+
context 'with a pattern with a constant' do
2127+
let(:pattern) { '(sym %TEST)' }
2128+
let(:helper_name) { :def_node_matcher }
2129+
2130+
before { defined_class::TEST = hello_matcher }
2131+
2132+
it_behaves_like 'matching'
2133+
2134+
context 'when the value is not in the set' do
2135+
let(:ruby) { ':world' }
2136+
2137+
it_behaves_like 'nonmatching'
2138+
end
2139+
end
21062140
end
21072141
end

0 commit comments

Comments
 (0)