syntax_finder 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f396dd84ec243335733edbe3abc5c22c0f32ea6ade48dfc16336ad385f0a2bd8
4
+ data.tar.gz: 3e6c73de9f48e65ed5f645b7c0b8c25c139826202d739ca884545c1ff48a7b40
5
+ SHA512:
6
+ metadata.gz: 0ce254cd70a3952f0a1d976cbd5ba9fe776ebb81137d8387d19b917548e4e3a7c5521ecdece26c70ebd712a67491327d1379855ad4dc058780f845b4bbaef0d0
7
+ data.tar.gz: 78a82aa09159d3cf9e764ea342c71e60a21574f0b55acfb76510ab802bdb8a9e7b5a042c200fdf62704958dce4304aa864f3d9a66211c19f707eb55ab46c58a8
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Koichi Sasada
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # SyntaxFinder
2
+
3
+ Find Ruby syntax patterns with Prism.
4
+
5
+ ## How to use
6
+
7
+ Write your pattern in Ruby.
8
+
9
+ ```ruby
10
+ # samples/if_then_finder.rb
11
+
12
+ require 'syntax_finder'
13
+
14
+ # Count up all `if` statements and `then` keywords.
15
+
16
+ class IfThenFinder < SyntaxFinder
17
+ def look node
18
+ if node.type == :if_node
19
+ inc :if
20
+ inc :then if node.then_keyword_loc
21
+ end
22
+ end
23
+ end
24
+ ```
25
+
26
+ and run the script with Ruby script file names listed in STDIN like this.
27
+
28
+ ```sh
29
+ $ find ruby/ruby -name '*.rb' | ruby samples/if_then_finder.rb
30
+ [[:if, 32153], [:FILES, 9558], [:then, 3627], [:FAILED_PARSE, 5]]
31
+ ```
32
+
33
+ In this case, with *.rb files in ruby/ruby directory, there are
34
+
35
+ * 9,558 files
36
+ * 32,153 if statements
37
+ * 3,627 then keywords
38
+ * 5 files are failed because of parsing.
39
+
40
+ You can specify files with arguments like that:
41
+
42
+ ```sh
43
+ $ ruby samples/if_then_finder.rb samples/*.rb
44
+ [[[:FILES, 16]], [[:if, 26], [:then, 1]]]
45
+ ```
46
+
47
+ You can specify `-j` or `-jN` for parallel processing like that:
48
+
49
+ ```sh
50
+ $ time find ruby/ruby -name '*.rb' | ruby samples/if_then_finder.rb
51
+ [[:if, 32153], [:FILES, 9558], [:then, 3627], [:FAILED_PARSE, 5]]
52
+
53
+ real 0m3.627s
54
+ user 0m3.451s
55
+ sys 0m0.219s
56
+
57
+ $ time find ruby/ruby -name '*.rb' | ruby samples/if_then_finder.rb -j
58
+ [[:if, 32153], [:FILES, 9558], [:then, 3627], [:FAILED_PARSE, 5]]
59
+
60
+ real 0m0.962s
61
+ user 0m7.944s
62
+ sys 0m0.854s
63
+ ```
64
+
65
+ ## How to write a finder
66
+
67
+ 1. Define a class derived with `SyntaxFinder` class.
68
+ 2. Define a `look(node)` method with your expected pattern. `node` is Prism's node.
69
+ 3. Use `inc(key)` if you want to aggregate the statistics in `look()`.
70
+
71
+ See `examples/` for more examples.
72
+
73
+ ## Development
74
+
75
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test-unit` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
76
+
77
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
78
+
79
+ ## Contributing
80
+
81
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ko1/syntax_finder.
82
+
83
+ ## License
84
+
85
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ end
11
+
12
+ task default: :test
@@ -0,0 +1,3 @@
1
+ class SyntaxFinder
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,272 @@
1
+ require 'prism'
2
+ require 'optparse'
3
+ require_relative 'syntax_finder/version'
4
+
5
+ class SyntaxFinder
6
+ def initialize file
7
+ @file = file
8
+ end
9
+
10
+ ### Traversing and looking methods
11
+
12
+ # Please write your pattern here to investigate the node.
13
+ # This method is called for each node.
14
+ def look node
15
+ # override here
16
+ end
17
+
18
+ # If you need to pass some context while traversing,
19
+ # redefine this method with context like `def traverse node, context = nil`.
20
+ def traverse node
21
+ look node
22
+ traverse_rest node
23
+ end
24
+
25
+ def traverse_rest node
26
+ node.child_nodes.compact.each do |child|
27
+ traverse child
28
+ end
29
+ end
30
+
31
+ ### Utility methods for look
32
+
33
+ # node location information
34
+ def nloc node
35
+ "#{@file}:#{node.location.start_line}"
36
+ end
37
+
38
+ # node line (1st line of node lines)
39
+ def nline node
40
+ node.slice_lines.lines.first.chomp
41
+ end
42
+
43
+ # node lines
44
+ def nlines node
45
+ node.slice_lines
46
+ end
47
+
48
+ def success? r
49
+ return true if r.success?
50
+
51
+ if r.errors.size == 1 &&
52
+ r.errors.first.type == :script_not_found
53
+ true
54
+ else
55
+ false
56
+ end
57
+ end
58
+
59
+ # any object can be a key.
60
+ # if key is [A-Z_]+, it's system key.
61
+ # it's returns true if the key is already exists.
62
+ def inc key
63
+ has_key = @@result.has_key? key
64
+ @@result[key] += 1
65
+ has_key
66
+ end
67
+
68
+ def note key, note
69
+ @@result_notes[key] << note
70
+ end
71
+
72
+ def self.inc key
73
+ @@result[key] += 1
74
+ end
75
+
76
+ def self.note key, note
77
+ @@result_notes[key] << note
78
+ end
79
+
80
+ ### Execution management
81
+
82
+ def start_traverse
83
+ puts "Start #{@file}" if $DEBUG
84
+
85
+ @root = Prism.parse_file(@file)
86
+ if success?(@root)
87
+ traverse @ast = @root.value
88
+ else
89
+ # pp [@file, @root.errors]
90
+ # inc :FAILED, @file
91
+ inc :FAILED_PARSE
92
+ end
93
+ rescue Interrupt
94
+ exit
95
+ rescue Exception => e
96
+ pp [e, @file, e.backtrace] if $DEBUG
97
+ inc [:FAILED, e.class]
98
+ end
99
+
100
+ @@finders = [self]
101
+
102
+ def self.inherited klass
103
+ @@finders << klass
104
+ end
105
+
106
+ def self.last_finder
107
+ @@finders.last
108
+ end
109
+
110
+ @@result = Hash.new(0)
111
+ @@result_notes = Hash.new{|h, k| h[k] = []}
112
+ @@opt = {}
113
+
114
+ def self.setup_parallel finder, pn
115
+ taskq = @@opt[:taskq] = Queue.new
116
+ resq = @@opt[:resq] = Queue.new
117
+ term_cmd = '-- terminate'
118
+
119
+ @@opt[:threads] = pn.times.map{
120
+ Thread.new _1 do |ti|
121
+ task_r, task_w = IO.pipe
122
+ res_r, res_w = IO.pipe
123
+
124
+ pid = fork do
125
+ trap(:INT, 'IGNORE')
126
+
127
+ loop do
128
+ file = task_r.gets.chomp
129
+
130
+ case file
131
+ when term_cmd
132
+ @@result_notes.default_proc = nil
133
+ last_result = [@@result, @@result_notes]
134
+ break
135
+ else
136
+ kick finder, file
137
+ last_result = nil
138
+ end
139
+ rescue Interrupt
140
+ STDERR.puts "Worker is interrupted (pid: #{Process.pid})"
141
+ @@result_notes.default_proc = nil
142
+ last_result = [@@result, @@result_notes]
143
+ exit
144
+ ensure
145
+ res_w.puts Marshal.dump(last_result).dump
146
+ end
147
+ end
148
+
149
+ result = nil
150
+
151
+ while true
152
+ file = taskq.pop
153
+ task_w.puts file || term_cmd
154
+ result = Marshal.load(res_r.gets.chomp.undump) # wait for the result
155
+
156
+ if result
157
+ resq << result
158
+ break
159
+ end
160
+ end
161
+ ensure
162
+ p [Thread.current, $!] if $DEBUG
163
+ Process.kill :KILL, pid
164
+ Process.waitpid pid
165
+ end
166
+ }
167
+ end
168
+
169
+ def self.cleanup_parallel
170
+ return unless taskq = @@opt[:taskq]
171
+ trap(:INT){
172
+ trap(:INT, 'DEFAULT')
173
+ }
174
+ taskq.close
175
+
176
+ @@opt[:parallel].times do |i|
177
+ results, notes = @@opt[:resq].pop
178
+ @@result.merge!(results){|k, a, b| a + b}
179
+ @@result_notes.merge!(notes)
180
+ end
181
+ end
182
+
183
+ def self.parse_opt finder, argv
184
+ o = OptionParser.new
185
+ o.on '-v', '--verbose', 'VERBOSE mode' do
186
+ $VERBOSE = true
187
+ end
188
+
189
+ o.on '-d', '--debug' do
190
+ $DEBUG = true
191
+ end
192
+
193
+ o.on '-q', '--quiet' do
194
+ @@opt[:quiet] = true
195
+ end
196
+
197
+ o.on '-j', '--jobs[=N]', 'Parallel mode' do
198
+ require 'etc'
199
+
200
+ pn = @@opt[:parallel] = _1&.to_i || Etc.nprocessors
201
+ setup_parallel finder, pn
202
+ end
203
+
204
+ o.parse! argv
205
+ pp options: @@opt if $DEBUG
206
+ end
207
+
208
+ def self.kick finder, file
209
+ finder.new(file).start_traverse
210
+ end
211
+
212
+ def self.aggregate_results
213
+ if @@result_notes.size > 0
214
+ results = @@result.sort_by{|k, v| -v}.map{|k, v|
215
+ if !(note = @@result_notes[k]).empty?
216
+ [k, v, note]
217
+ else
218
+ [k, v]
219
+ end
220
+ }
221
+ else
222
+ results = @@result.sort_by{|k, v| -v}
223
+ end
224
+
225
+ results.partition{|key,|
226
+ Symbol === key && key.length > 1 && /^[A-Z_]+$/ =~ key
227
+ }
228
+ end
229
+
230
+ def self.print_result
231
+ system_results, user_results = aggregate_results
232
+ pp [system_results, user_results]
233
+ end
234
+
235
+ def self.run finder, argv = ARGV
236
+ @already_run = true
237
+ parse_opt finder, argv
238
+
239
+ files = argv.empty? ? ARGF : argv
240
+ files.each do |f|
241
+ f = f.scrub.strip
242
+ next unless FileTest.file?(f)
243
+
244
+ inc :FILES
245
+
246
+ if q = @@opt[:taskq]
247
+ q << f
248
+ else
249
+ kick finder, f
250
+ end
251
+ end
252
+ rescue Interrupt
253
+ STDERR.puts "-- Interrupted"
254
+ exit
255
+ ensure
256
+ cleanup_parallel
257
+ finder.print_result if finder
258
+ end
259
+
260
+ END{
261
+ if !@already_run && @@finders.size == 2
262
+ self.run last_finder
263
+ end
264
+ }
265
+ end
266
+
267
+ # for pretty integer results
268
+ class Integer
269
+ def inspect
270
+ self.to_s.gsub(/(\d)(?=\d{3}+$)/, '\\1_')
271
+ end
272
+ end
@@ -0,0 +1,27 @@
1
+ require 'syntax_finder'
2
+
3
+ # Cound up all of local variable names.
4
+
5
+ class LVarFinder < SyntaxFinder
6
+ def look node
7
+ if node.respond_to? :locals
8
+ node.locals.each{|lv|
9
+ case lv
10
+ when Symbol
11
+ inc lv
12
+ when Prism::BlockLocalVariableNode
13
+ inc lv.name
14
+ else
15
+ pp [node.type, lv.type, node.locals].inspect
16
+ exit
17
+ end
18
+ }
19
+ end
20
+ end
21
+
22
+ def self.print_result
23
+ @@result.sort_by{|lv, v| [-v, lv]}.each{|lv, v|
24
+ puts "#{lv}\t#{v}"
25
+ }
26
+ end
27
+ end
@@ -0,0 +1,22 @@
1
+ require 'syntax_finder'
2
+
3
+ # Cound up all of method names.
4
+
5
+ class MethodNameFinder < SyntaxFinder
6
+ def look node
7
+ if node.type == :call_node
8
+ inc [:call, node.name]
9
+ # pp [nloc(node),nlines(node).lines.first.chomp] unless @@opt[:quiet]
10
+ elsif node.type == :def_node
11
+ inc [:def, node.name]
12
+ # pp [nloc(node),nlines(node).lines.first.chomp] unless @@opt[:quiet]
13
+ end
14
+ end
15
+
16
+ def self.print_result
17
+ @@result.sort_by{|(t, m), v| [t, -v]}.each{|(t, m), v|
18
+ puts "#{v}\t#{t}\t#{m}"
19
+ }
20
+ end
21
+ end
22
+
@@ -0,0 +1,13 @@
1
+ relative 'syntax_finder'
2
+
3
+ # Cound up all of regexps.
4
+
5
+ class RegexpFinder < SyntaxFinder
6
+ def look node
7
+ if node.type == :regular_expression_node
8
+ # pp [[nloc(node), nlines(node).lines]] unless @@opt[:quiet]
9
+ inc :regexp
10
+ inc node.unescaped
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ require 'syntax_finder'
2
+
3
+ # cound `assert*(params)` and `assert params` patterns.`
4
+
5
+ class AssertEqualParenFinder < SyntaxFinder
6
+ def look node
7
+ if node.type == :call_node &&
8
+ /^assert/ =~ node.name && node.arguments
9
+ then
10
+ if node.opening_loc
11
+ inc :paren
12
+ else
13
+ inc :no_paren
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ require 'syntax_finder'
2
+
3
+ # Check call with paren or no paren
4
+
5
+ class IfThenFinder < SyntaxFinder
6
+ def look node
7
+ if node.type == :call_node
8
+ has_params = !node.arguments.nil?
9
+ has_paren = !node.opening_loc.nil?
10
+ inc paren: has_paren, params: has_params
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ require 'syntax_finder'
2
+
3
+ # Check def with paren or no paren
4
+
5
+ class DefParamsFinder < SyntaxFinder
6
+ def look node
7
+ if node.type == :def_node
8
+ inc :def
9
+ if node.parameters
10
+ params = {
11
+ required: node.parameters.requireds.size,
12
+ optional: node.parameters.optionals.size,
13
+ rest: !node.parameters.rest.nil?,
14
+ posts: node.parameters.posts.size,
15
+ keywords: node.parameters.keywords.size,
16
+ keyword_rest: !node.parameters.keyword_rest.nil?
17
+ }.reject { |_, v| v == 0 || v == false }
18
+ else
19
+ params = {required: 0}
20
+ end
21
+
22
+ unless inc params
23
+ note params, nloc(node)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ require 'syntax_finder'
2
+
3
+ # Check def with paren or no paren
4
+
5
+ class IfThenFinder < SyntaxFinder
6
+ def look node
7
+ if node.type == :def_node
8
+ has_params = !node.parameters.nil?
9
+ has_paren = !node.lparen_loc.nil?
10
+ inc paren: has_paren, params: has_params
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,29 @@
1
+ require 'syntax_finder'
2
+
3
+ # Check indentation with `if` and `and`/`or`
4
+
5
+ class IfCondContIndnetFinder < SyntaxFinder
6
+ def look node
7
+ if node.type == :if_node
8
+ inc :if
9
+ inc :then if node.then_keyword_loc
10
+ cond = node.predicate
11
+
12
+ case cond.type
13
+ when :and_node, :or_node
14
+ inc op: cond.operator_loc.slice
15
+ d = cond.location.end_line - cond.location.start_line
16
+ if d > 0
17
+ base = node.location.start_column
18
+ rest_lines = nlines(cond).lines[1..]
19
+ inc indent: rest_lines.map{|line|
20
+ /^(\s*)/ =~ line
21
+ $1.size - base
22
+ }.min
23
+ # puts nlines(cond)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,12 @@
1
+ require 'syntax_finder'
2
+
3
+ # Count up all `if` statements and `then` keywords.
4
+
5
+ class IfThenFinder < SyntaxFinder
6
+ def look node
7
+ if node.type == :if_node
8
+ inc :if
9
+ inc :then if node.then_keyword_loc
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,18 @@
1
+ require 'syntax_finder'
2
+
3
+ class IntegerRangeFinder < SYntaxFinder
4
+ def look node
5
+ if node.type == :range_node
6
+ left = node.left
7
+ right = node.right
8
+
9
+ if((left.nil? || left.type == :integer_node) &&
10
+ (right.nil? || right.type == :integer_node))
11
+
12
+ inc :integer_range
13
+ # pp [[nloc(node), nlines(node).lines]] unless @@opt[:quiet]
14
+ inc [left&.value, right&.value].inspect
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+ require 'syntax_finder'
2
+
3
+ class IntegerSizeFinder < SyntaxFinder
4
+ def look node
5
+ if node.type == :call_node && node.name == :'size' &&
6
+ node.receiver&.type == :integer_node
7
+ pp [nloc(node), nlines(node).lines.first.chomp]
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,18 @@
1
+ require 'syntax_finder'
2
+
3
+ # count all of given method names via comma separated environment variables,
4
+ # like `NAMES=map,collect`
5
+
6
+ NAMES = ENV['NAMES']&.yield_self{|m| m.split(',').map{|m| m.strip.to_sym}}.to_h{|e| [e, true]} || raise
7
+
8
+ class MethodNameFinder < SyntaxFinder
9
+ def look node
10
+ case t = node.type
11
+ when :call_node, :def_node
12
+ if NAMES[name = node.name]
13
+ inc [t, name]
14
+ pp [nloc(node),nlines(node).lines.first.chomp] if $VERBOSE
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ require 'syntax_finder'
2
+
3
+ class PragmaFinder < SyntaxFinder
4
+ def look node
5
+ @root.magic_comments.each{|c|
6
+ inc key = [c.key, c.value]
7
+ note key, @file if $VERBOSE
8
+ }
9
+
10
+ # No need for recursive travaersal.
11
+ end
12
+
13
+ def traverse node
14
+ look node
15
+ # check only root node
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ require 'syntax_finder'
2
+
3
+ class ReqKWwoParenFinder < SyntaxFinder
4
+ def look node
5
+ if node.type == :call_node &&
6
+ node.opening_loc.nil? &&
7
+ node.arguments &&
8
+ node.arguments.arguments.any?{|n| n.type == :keyword_hash_node && n.elements.any?{|e|
9
+ e.type == :assoc_node &&
10
+ e.operator_loc.nil? &&
11
+ e.key.location.start_line != e.value.location.start_line}}
12
+
13
+ inc :found
14
+ puts "# #{nloc(node)}"
15
+ puts '> ' + nlines(node).lines.join('> ')
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ require 'syntax_finder'
2
+
3
+ class ReqKWwoParenFinder < SyntaxFinder
4
+ def look node
5
+ if node.type == :def_node &&
6
+ node.lparen_loc.nil? &&
7
+ node.parameters&.keywords&.last&.type == :required_keyword_parameter_node &&
8
+ node.parameters&.block.nil?
9
+
10
+ inc :found
11
+ pp [nloc(node), nlines(node).lines.first.chomp]
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ require 'syntax_finder'
2
+
3
+ class SingletonClassInDefFinder < SyntaxFinder
4
+ # use traverse instead of look
5
+ def traverse node, in_def = nil, in_sclass = nil
6
+ if node.type == :class_node && in_sclass && node.constant_path.type != :constant_read_node
7
+ pp [nloc(node), nlines(node).lines.first.chomp]
8
+ end
9
+
10
+ if node.type == :def_node
11
+ in_def = true; in_sclass = false
12
+ elsif node.type == :singleton_class_node && in_def
13
+ in_sclass = true
14
+ end
15
+
16
+ node.child_nodes.compact.each{|n| traverse n, in_def, in_sclass}
17
+ end
18
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: syntax_finder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Koichi Sasada
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-01-14 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Analyze your Ruby scripts with prism.
14
+ email:
15
+ - ko1@atdot.net
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - LICENSE.txt
21
+ - README.md
22
+ - Rakefile
23
+ - lib/syntax_finder.rb
24
+ - lib/syntax_finder/version.rb
25
+ - samples/all_lvar_finder.rb
26
+ - samples/all_method_name_finder.rb
27
+ - samples/all_regexp_finder.rb
28
+ - samples/assert_equal_paren.rb
29
+ - samples/call_paren_finder.rb
30
+ - samples/def_params_finder.rb
31
+ - samples/def_paren_finder.rb
32
+ - samples/if_cond_cont_indent_finder.rb
33
+ - samples/if_then_finder.rb
34
+ - samples/integer_range_finder.rb
35
+ - samples/integer_size_finder.rb
36
+ - samples/method_name_finder.rb
37
+ - samples/pragma_finder.rb
38
+ - samples/required_kwcall_wo_paren_finder.rb
39
+ - samples/required_kwdef_wo_paren_finder.rb
40
+ - samples/singleton_class_in_def_finder.rb
41
+ homepage: https://github.com/ko1/syntax_finder
42
+ licenses:
43
+ - MIT
44
+ metadata:
45
+ homepage_uri: https://github.com/ko1/syntax_finder
46
+ source_code_uri: https://github.com/ko1/syntax_finder
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: 3.0.0
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ requirements: []
62
+ rubygems_version: 3.5.9
63
+ signing_key:
64
+ specification_version: 4
65
+ summary: Analyze your Ruby scripts with prism.
66
+ test_files: []