querly 0.15.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/rubocop.yml +15 -0
- data/.github/workflows/ruby.yml +22 -0
- data/.rubocop.yml +10 -0
- data/CHANGELOG.md +28 -0
- data/README.md +2 -1
- data/Rakefile +1 -1
- data/bin/setup +1 -2
- data/lib/querly.rb +2 -1
- data/lib/querly/cli.rb +27 -9
- data/lib/querly/cli/console.rb +8 -4
- data/lib/querly/cli/find.rb +8 -4
- data/lib/querly/cli/formatter.rb +6 -1
- data/lib/querly/cli/test.rb +1 -1
- data/lib/querly/node_pair.rb +1 -1
- data/lib/querly/pattern/expr.rb +8 -6
- data/lib/querly/pattern/parser.y +8 -9
- data/lib/querly/pp/cli.rb +20 -1
- data/lib/querly/script.rb +20 -0
- data/lib/querly/script_enumerator.rb +18 -24
- data/lib/querly/version.rb +1 -1
- data/manual/patterns.md +14 -2
- data/querly.gemspec +10 -4
- data/sample.yaml +2 -0
- data/template.yml +3 -1
- metadata +69 -18
- data/.travis.yml +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c9b153bfa5b7a49deb8a6bbff12517452d2b23498b371a4fcbc154fac2330dd9
|
4
|
+
data.tar.gz: 7d08df5b8053cbb9e45d6db6b132e6dcb9171797d5687caf96b3827aed53fae2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c2a5371c77896ce523078b014875744f52a248b1e84f04772e0b1ae7eee3cdf876136915c87de876436dc180e9c0b1cbb6ef487a9aaf4dccaf7faad68223546d
|
7
|
+
data.tar.gz: 2671df7da016eeb25038a504ee5c7e203e6079ead0adcf2cdfeb25c02c3a3157ee5a05347f9cdd33e25289d12b28de5a38a15fb595bb258e49accf5c4d818b3d
|
@@ -0,0 +1,15 @@
|
|
1
|
+
name: RuboCop
|
2
|
+
|
3
|
+
on: pull_request
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
rubocop:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
steps:
|
9
|
+
- uses: actions/checkout@v2
|
10
|
+
- uses: ruby/setup-ruby@v1
|
11
|
+
with:
|
12
|
+
ruby-version: "3.0"
|
13
|
+
- run: gem install rubocop rubocop-rubycw
|
14
|
+
- name: Run RuboCop
|
15
|
+
run: rubocop --format github
|
@@ -0,0 +1,22 @@
|
|
1
|
+
name: Ruby
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- master
|
7
|
+
pull_request: {}
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
test:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
strategy:
|
13
|
+
matrix:
|
14
|
+
ruby: ["2.7", "3.0", head]
|
15
|
+
steps:
|
16
|
+
- uses: actions/checkout@v2
|
17
|
+
- uses: ruby/setup-ruby@v1
|
18
|
+
with:
|
19
|
+
ruby-version: ${{ matrix.ruby }}
|
20
|
+
bundler-cache: true
|
21
|
+
- run: bin/setup
|
22
|
+
- run: bundle exec rake build test
|
data/.rubocop.yml
ADDED
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,34 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 1.3.0 (2021-07-05)
|
6
|
+
|
7
|
+
* Require Ruby 2.7 or 3.0 by @yubiquitous ([#88](https://github.com/soutaro/querly/pull/88))
|
8
|
+
* Use 3.0 compatible parser by @yubiquitous ([#88](https://github.com/soutaro/querly/pull/88))
|
9
|
+
|
10
|
+
## 1.2.0 (2020-12-15)
|
11
|
+
|
12
|
+
* Relax Thor version requirements by @y-yagi ([#85](https://github.com/soutaro/querly/pull/85))
|
13
|
+
* Fix ERB comment preprocessing by @mallowlabs ([#84](https://github.com/soutaro/querly/pull/84))
|
14
|
+
* Better error message for Ruby code syntax error by @ybiquitous ([#83](https://github.com/soutaro/querly/pull/83))
|
15
|
+
|
16
|
+
## 1.1.0 (2020-05-17)
|
17
|
+
|
18
|
+
* Fix invalid bytes sequence in UTF-8 error by @mallowlabs [#75](https://github.com/soutaro/querly/pull/75)
|
19
|
+
* Detect safe navigation operator as a method call by @pocke [#71](https://github.com/soutaro/querly/pull/71)
|
20
|
+
|
21
|
+
## 1.0.0 (2019-7-19)
|
22
|
+
|
23
|
+
* Add `--config` option for `find` and `console` [#67](https://github.com/soutaro/querly/pull/67)
|
24
|
+
* Improve preprocessor performance by processing concurrently [#68](https://github.com/soutaro/querly/pull/68)
|
25
|
+
|
26
|
+
## 0.16.0 (2019-04-23)
|
27
|
+
|
28
|
+
* Support string literal pattern (@pocke) [#64](https://github.com/soutaro/querly/pull/64)
|
29
|
+
* Allow underscore method name pattern (@pocke) [#63](https://github.com/soutaro/querly/pull/63)
|
30
|
+
* Add erb support (@hanachin) [#61](https://github.com/soutaro/querly/pull/61)
|
31
|
+
* Add `exit` command on console (@wata727) [#59](https://github.com/soutaro/querly/pull/59)
|
32
|
+
|
5
33
|
## 0.15.1 (2019-03-12)
|
6
34
|
|
7
35
|
* Relax parser version requirement
|
data/README.md
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|

|
2
|
+
|
2
3
|
# Querly - Pattern Based Checking Tool for Ruby
|
3
4
|
|
4
|
-
|
5
|
+

|
5
6
|
|
6
7
|
Querly is a query language and tool to find out method calls from Ruby programs.
|
7
8
|
Define rules to check your program with patterns to find out *bad* pieces.
|
data/Rakefile
CHANGED
data/bin/setup
CHANGED
data/lib/querly.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
require 'pathname'
|
2
2
|
require "yaml"
|
3
3
|
require "rainbow"
|
4
|
-
require "parser/
|
4
|
+
require "parser/ruby30"
|
5
5
|
require "set"
|
6
6
|
require "open3"
|
7
7
|
require "active_support/inflector"
|
8
|
+
require "parallel"
|
8
9
|
|
9
10
|
require "querly/version"
|
10
11
|
require 'querly/analyzer'
|
data/lib/querly/cli.rb
CHANGED
@@ -12,6 +12,7 @@ module Querly
|
|
12
12
|
option :root
|
13
13
|
option :format, default: "text", type: :string, enum: %w(text json)
|
14
14
|
option :rule, type: :string
|
15
|
+
option :threads, default: Parallel.processor_count, type: :numeric
|
15
16
|
def check(*paths)
|
16
17
|
require 'querly/cli/formatter'
|
17
18
|
|
@@ -23,6 +24,8 @@ module Querly
|
|
23
24
|
end
|
24
25
|
formatter.start
|
25
26
|
|
27
|
+
threads = Integer(options[:threads])
|
28
|
+
|
26
29
|
begin
|
27
30
|
unless config_path.file?
|
28
31
|
STDERR.puts <<-Message
|
@@ -32,20 +35,15 @@ Specify configuration file by --config option.
|
|
32
35
|
exit 1
|
33
36
|
end
|
34
37
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
config = begin
|
39
|
-
yaml = YAML.load(config_path.read)
|
40
|
-
Config.load(yaml, config_path: config_path, root_dir: root_path, stderr: STDERR)
|
38
|
+
begin
|
39
|
+
config = config(root_option: options[:root])
|
41
40
|
rescue => exn
|
42
41
|
formatter.config_error config_path, exn
|
43
|
-
exit 1
|
44
42
|
end
|
45
43
|
|
46
44
|
analyzer = Analyzer.new(config: config, rule: options[:rule])
|
47
45
|
|
48
|
-
ScriptEnumerator.new(paths: paths.empty? ? [Pathname.pwd] : paths.map {|path| Pathname(path) }, config: config).each do |path, script|
|
46
|
+
ScriptEnumerator.new(paths: paths.empty? ? [Pathname.pwd] : paths.map {|path| Pathname(path) }, config: config, threads: threads).each do |path, script|
|
49
47
|
case script
|
50
48
|
when Script
|
51
49
|
analyzer.scripts << script
|
@@ -67,6 +65,8 @@ Specify configuration file by --config option.
|
|
67
65
|
end
|
68
66
|
|
69
67
|
desc "console [paths]", "Start console for given paths"
|
68
|
+
option :config, default: "querly.yml"
|
69
|
+
option :threads, default: Parallel.processor_count, type: :numeric
|
70
70
|
def console(*paths)
|
71
71
|
require 'querly/cli/console'
|
72
72
|
home_path = if (path = ENV["QUERLY_HOME"])
|
@@ -75,21 +75,32 @@ Specify configuration file by --config option.
|
|
75
75
|
Pathname(Dir.home) + ".querly"
|
76
76
|
end
|
77
77
|
home_path.mkdir unless home_path.exist?
|
78
|
+
config = config_path.file? ? config(root_option: nil) : nil
|
79
|
+
threads = Integer(options[:threads])
|
78
80
|
|
79
81
|
Console.new(
|
80
82
|
paths: paths.empty? ? [Pathname.pwd] : paths.map {|path| Pathname(path) },
|
81
83
|
history_path: home_path + "history",
|
82
|
-
history_size: ENV["QUERLY_HISTORY_SIZE"]&.to_i || 1_000_000
|
84
|
+
history_size: ENV["QUERLY_HISTORY_SIZE"]&.to_i || 1_000_000,
|
85
|
+
config: config,
|
86
|
+
threads: threads
|
83
87
|
).start
|
84
88
|
end
|
85
89
|
|
86
90
|
desc "find pattern [paths]", "Find for the pattern in given paths"
|
91
|
+
option :config, default: "querly.yml"
|
92
|
+
option :threads, default: Parallel.processor_count, type: :numeric
|
87
93
|
def find(pattern, *paths)
|
88
94
|
require 'querly/cli/find'
|
89
95
|
|
96
|
+
config = config_path.file? ? config(root_option: nil) : nil
|
97
|
+
threads = Integer(options[:threads])
|
98
|
+
|
90
99
|
Find.new(
|
91
100
|
pattern: pattern,
|
92
101
|
paths: paths.empty? ? [Pathname.pwd] : paths.map {|path| Pathname(path) },
|
102
|
+
config: config,
|
103
|
+
threads: threads
|
93
104
|
).start
|
94
105
|
end
|
95
106
|
|
@@ -125,6 +136,13 @@ Specify configuration file by --config option.
|
|
125
136
|
|
126
137
|
private
|
127
138
|
|
139
|
+
def config(root_option:)
|
140
|
+
root_path = root_option ? Pathname(root_option).realpath : config_path.parent.realpath
|
141
|
+
|
142
|
+
yaml = YAML.load(config_path.read)
|
143
|
+
Config.load(yaml, config_path: config_path, root_dir: root_path, stderr: STDERR)
|
144
|
+
end
|
145
|
+
|
128
146
|
def config_path
|
129
147
|
[Pathname(options[:config]),
|
130
148
|
Pathname("querly.yaml")].compact.find(&:file?) || Pathname(options[:config])
|
data/lib/querly/cli/console.rb
CHANGED
@@ -8,13 +8,17 @@ module Querly
|
|
8
8
|
attr_reader :paths
|
9
9
|
attr_reader :history_path
|
10
10
|
attr_reader :history_size
|
11
|
+
attr_reader :config
|
11
12
|
attr_reader :history
|
13
|
+
attr_reader :threads
|
12
14
|
|
13
|
-
def initialize(paths:, history_path:, history_size:)
|
15
|
+
def initialize(paths:, history_path:, history_size:, config: nil, threads:)
|
14
16
|
@paths = paths
|
15
17
|
@history_path = history_path
|
16
18
|
@history_size = history_size
|
19
|
+
@config = config
|
17
20
|
@history = []
|
21
|
+
@threads = threads
|
18
22
|
end
|
19
23
|
|
20
24
|
def start
|
@@ -42,9 +46,9 @@ Querly #{VERSION}, interactive console
|
|
42
46
|
def analyzer
|
43
47
|
return @analyzer if @analyzer
|
44
48
|
|
45
|
-
@analyzer = Analyzer.new(config:
|
49
|
+
@analyzer = Analyzer.new(config: config, rule: nil)
|
46
50
|
|
47
|
-
ScriptEnumerator.new(paths: paths, config:
|
51
|
+
ScriptEnumerator.new(paths: paths, config: config, threads: threads).each do |path, script|
|
48
52
|
case script
|
49
53
|
when Script
|
50
54
|
@analyzer.scripts << script
|
@@ -60,7 +64,7 @@ Querly #{VERSION}, interactive console
|
|
60
64
|
def start_loop
|
61
65
|
while line = Readline.readline("> ", true)
|
62
66
|
case line
|
63
|
-
when "quit"
|
67
|
+
when "quit", "exit"
|
64
68
|
exit
|
65
69
|
when "reload!"
|
66
70
|
STDOUT.print "reloading..."
|
data/lib/querly/cli/find.rb
CHANGED
@@ -7,10 +7,14 @@ module Querly
|
|
7
7
|
|
8
8
|
attr_reader :pattern_str
|
9
9
|
attr_reader :paths
|
10
|
+
attr_reader :config
|
11
|
+
attr_reader :threads
|
10
12
|
|
11
|
-
def initialize(pattern:, paths:)
|
13
|
+
def initialize(pattern:, paths:, config: nil, threads:)
|
12
14
|
@pattern_str = pattern
|
13
15
|
@paths = paths
|
16
|
+
@config = config
|
17
|
+
@threads = threads
|
14
18
|
end
|
15
19
|
|
16
20
|
def start
|
@@ -36,7 +40,7 @@ module Querly
|
|
36
40
|
puts "#{count} results"
|
37
41
|
rescue => exn
|
38
42
|
STDOUT.puts Rainbow("Error: #{exn}").red
|
39
|
-
|
43
|
+
STDOUT.puts "pattern: #{pattern_str}"
|
40
44
|
STDOUT.puts "Backtrace:"
|
41
45
|
STDOUT.puts format_backtrace(exn.backtrace)
|
42
46
|
end
|
@@ -48,9 +52,9 @@ module Querly
|
|
48
52
|
def analyzer
|
49
53
|
return @analyzer if @analyzer
|
50
54
|
|
51
|
-
@analyzer = Analyzer.new(config:
|
55
|
+
@analyzer = Analyzer.new(config: config, rule: nil)
|
52
56
|
|
53
|
-
ScriptEnumerator.new(paths: paths, config:
|
57
|
+
ScriptEnumerator.new(paths: paths, config: config, threads: threads).each do |path, script|
|
54
58
|
case script
|
55
59
|
when Script
|
56
60
|
@analyzer.scripts << script
|
data/lib/querly/cli/formatter.rb
CHANGED
@@ -46,7 +46,12 @@ module Querly
|
|
46
46
|
|
47
47
|
def script_error(path, error)
|
48
48
|
STDERR.puts Rainbow("Failed to load script: #{path}").red
|
49
|
-
|
49
|
+
|
50
|
+
if error.is_a? Parser::SyntaxError
|
51
|
+
STDERR.puts error.diagnostic.render
|
52
|
+
else
|
53
|
+
STDERR.puts error.inspect
|
54
|
+
end
|
50
55
|
end
|
51
56
|
|
52
57
|
def issue_found(script, rule, pair)
|
data/lib/querly/cli/test.rb
CHANGED
data/lib/querly/node_pair.rb
CHANGED
data/lib/querly/pattern/expr.rb
CHANGED
@@ -118,7 +118,7 @@ module Querly
|
|
118
118
|
|
119
119
|
when :str
|
120
120
|
return false unless type == :string
|
121
|
-
test_value(node.children.first)
|
121
|
+
test_value(node.children.first.scrub)
|
122
122
|
|
123
123
|
when :sym
|
124
124
|
return false unless type == :symbol
|
@@ -147,7 +147,8 @@ module Querly
|
|
147
147
|
|
148
148
|
def =~(pair)
|
149
149
|
# Skip send node with block
|
150
|
-
|
150
|
+
type = pair.node.type
|
151
|
+
if (type == :send || type == :csend) && pair.parent
|
151
152
|
if pair.parent.node.type == :block
|
152
153
|
if pair.parent.node.children.first.equal? pair.node
|
153
154
|
return false
|
@@ -176,7 +177,7 @@ module Querly
|
|
176
177
|
node = node.children.first if node&.type == :block
|
177
178
|
|
178
179
|
case node&.type
|
179
|
-
when :send
|
180
|
+
when :send, :csend
|
180
181
|
return false unless test_name(node)
|
181
182
|
return false unless test_receiver(node.children[0])
|
182
183
|
return false unless test_args(node.children.drop(2), args)
|
@@ -290,7 +291,8 @@ module Querly
|
|
290
291
|
if receiver.test_node(node)
|
291
292
|
true
|
292
293
|
else
|
293
|
-
|
294
|
+
type = node&.type
|
295
|
+
(type == :send || type == :csend) && test_node(node.children[0])
|
294
296
|
end
|
295
297
|
end
|
296
298
|
end
|
@@ -315,7 +317,7 @@ module Querly
|
|
315
317
|
# We don't want lvar without method call
|
316
318
|
# Skips when the node is not receiver of :send
|
317
319
|
parent_node = pair.parent&.node
|
318
|
-
if parent_node && parent_node.type == :send && parent_node.children.first.equal?(node)
|
320
|
+
if parent_node && (parent_node.type == :send || parent_node.type == :csend) && parent_node.children.first.equal?(node)
|
319
321
|
test_node(node)
|
320
322
|
end
|
321
323
|
else
|
@@ -325,7 +327,7 @@ module Querly
|
|
325
327
|
|
326
328
|
def test_node(node)
|
327
329
|
case node&.type
|
328
|
-
when :send
|
330
|
+
when :send, :csend
|
329
331
|
node.children[1] == name
|
330
332
|
when :lvar
|
331
333
|
node.children.first == name
|
data/lib/querly/pattern/parser.y
CHANGED
@@ -72,7 +72,7 @@ keyword: LIDENT | UIDENT
|
|
72
72
|
constant: UIDENT { result = [val[0]] }
|
73
73
|
| UIDENT COLONCOLON constant { result = [val[0]] + val[2] }
|
74
74
|
|
75
|
-
send: LIDENT block { result = val[1] != nil ? Expr::Send.new(receiver: nil, name: val[0],
|
75
|
+
send: LIDENT block { result = val[1] != nil ? Expr::Send.new(receiver: nil, name: val[0], block: val[1]) : Expr::Vcall.new(name: val[0]) }
|
76
76
|
| UIDENT block { result = Expr::Send.new(receiver: nil, name: val[0], block: val[1]) }
|
77
77
|
| method_name { result = Expr::Send.new(receiver: nil, name: val[0], block: nil) }
|
78
78
|
| method_name_or_ident LPAREN args RPAREN block { result = Expr::Send.new(receiver: nil,
|
@@ -83,18 +83,15 @@ send: LIDENT block { result = val[1] != nil ? Expr::Send.new(receiver: nil, name
|
|
83
83
|
name: val[1],
|
84
84
|
args: Argument::AnySeq.new,
|
85
85
|
block: val[2]) }
|
86
|
-
| receiver method_name_or_ident block { result = Expr::Send.new(receiver: val[0],
|
87
|
-
name: val[1],
|
88
|
-
args: Argument::AnySeq.new,
|
89
|
-
block: val[2]) }
|
90
|
-
| receiver method_name_or_ident LPAREN args RPAREN block { result = Expr::Send.new(receiver: val[0],
|
91
|
-
name: val[1],
|
92
|
-
args: val[3],
|
93
|
-
block: val[5]) }
|
94
86
|
| receiver method_name_or_ident LPAREN args RPAREN block { result = Expr::Send.new(receiver: val[0],
|
95
87
|
name: val[1],
|
96
88
|
args: val[3],
|
97
89
|
block: val[5]) }
|
90
|
+
| receiver UNDERBAR block { result = Expr::Send.new(receiver: val[0], name: /.+/, block: val[2]) }
|
91
|
+
| receiver UNDERBAR LPAREN args RPAREN block { result = Expr::Send.new(receiver: val[0],
|
92
|
+
name: /.+/,
|
93
|
+
args: val[3],
|
94
|
+
block: val[5]) }
|
98
95
|
|
99
96
|
receiver: expr DOT { result = val[0] }
|
100
97
|
| expr DOTDOTDOT { result = Expr::ReceiverContext.new(receiver: val[0]) }
|
@@ -136,6 +133,8 @@ def next_token
|
|
136
133
|
[:NIL, false]
|
137
134
|
when input.scan(/:string:/)
|
138
135
|
[:STRING, nil]
|
136
|
+
when input.scan(/"([^"]+)"/)
|
137
|
+
[:STRING, input[1]]
|
139
138
|
when input.scan(/:dstr:/)
|
140
139
|
[:DSTR, nil]
|
141
140
|
when input.scan(/:int:/)
|
data/lib/querly/pp/cli.rb
CHANGED
@@ -42,7 +42,7 @@ module Querly
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def run
|
45
|
-
available_commands = [:haml]
|
45
|
+
available_commands = [:haml, :erb]
|
46
46
|
|
47
47
|
if available_commands.include?(command)
|
48
48
|
send :"run_#{command}"
|
@@ -70,6 +70,25 @@ module Querly
|
|
70
70
|
stdout.print compiler.precompiled
|
71
71
|
end
|
72
72
|
end
|
73
|
+
|
74
|
+
def run_erb
|
75
|
+
require 'better_html'
|
76
|
+
require 'better_html/parser'
|
77
|
+
load_libs
|
78
|
+
source = stdin.read
|
79
|
+
source_buffer = Parser::Source::Buffer.new('(erb)')
|
80
|
+
source_buffer.source = source
|
81
|
+
parser = BetterHtml::Parser.new(source_buffer, template_language: :html)
|
82
|
+
|
83
|
+
new_source = source.gsub(/./, ' ')
|
84
|
+
parser.ast.descendants(:erb).each do |erb_node|
|
85
|
+
indicator_node, _, code_node, = *erb_node
|
86
|
+
next if indicator_node&.loc&.source == '#'
|
87
|
+
new_source[code_node.loc.range] = code_node.loc.source
|
88
|
+
new_source[code_node.loc.range.end] = ';'
|
89
|
+
end
|
90
|
+
stdout.puts new_source
|
91
|
+
end
|
73
92
|
end
|
74
93
|
end
|
75
94
|
end
|
data/lib/querly/script.rb
CHANGED
@@ -3,6 +3,16 @@ module Querly
|
|
3
3
|
attr_reader :path
|
4
4
|
attr_reader :node
|
5
5
|
|
6
|
+
def self.load(path:, source:)
|
7
|
+
parser = Parser::Ruby30.new(Builder.new).tap do |parser|
|
8
|
+
parser.diagnostics.all_errors_are_fatal = true
|
9
|
+
parser.diagnostics.ignore_warnings = true
|
10
|
+
end
|
11
|
+
buffer = Parser::Source::Buffer.new(path.to_s, 1)
|
12
|
+
buffer.source = source
|
13
|
+
self.new(path: path, node: parser.parse(buffer))
|
14
|
+
end
|
15
|
+
|
6
16
|
def initialize(path:, node:)
|
7
17
|
@path = path
|
8
18
|
@node = node
|
@@ -11,5 +21,15 @@ module Querly
|
|
11
21
|
def root_pair
|
12
22
|
NodePair.new(node: node)
|
13
23
|
end
|
24
|
+
|
25
|
+
class Builder < Parser::Builders::Default
|
26
|
+
def string_value(token)
|
27
|
+
value(token)
|
28
|
+
end
|
29
|
+
|
30
|
+
def emit_lambda
|
31
|
+
true
|
32
|
+
end
|
33
|
+
end
|
14
34
|
end
|
15
35
|
end
|
@@ -2,23 +2,36 @@ module Querly
|
|
2
2
|
class ScriptEnumerator
|
3
3
|
attr_reader :paths
|
4
4
|
attr_reader :config
|
5
|
+
attr_reader :threads
|
5
6
|
|
6
|
-
def initialize(paths:, config:)
|
7
|
+
def initialize(paths:, config:, threads:)
|
7
8
|
@paths = paths
|
8
9
|
@config = config
|
10
|
+
@threads = threads
|
9
11
|
end
|
10
12
|
|
13
|
+
# Yields `Script` object concurrently, in different threads.
|
11
14
|
def each(&block)
|
15
|
+
if block_given?
|
16
|
+
Parallel.each(each_path, in_threads: threads) do |path|
|
17
|
+
load_script_from_path path, &block
|
18
|
+
end
|
19
|
+
else
|
20
|
+
self.enum_for :each
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def each_path(&block)
|
12
25
|
if block_given?
|
13
26
|
paths.each do |path|
|
14
27
|
if path.directory?
|
15
28
|
enumerate_files_in_dir(path, &block)
|
16
29
|
else
|
17
|
-
|
30
|
+
yield path
|
18
31
|
end
|
19
32
|
end
|
20
33
|
else
|
21
|
-
|
34
|
+
enum_for :each_path
|
22
35
|
end
|
23
36
|
end
|
24
37
|
|
@@ -45,9 +58,7 @@ module Querly
|
|
45
58
|
path.read
|
46
59
|
end
|
47
60
|
|
48
|
-
|
49
|
-
buffer.source = source
|
50
|
-
script = Script.new(path: path, node: parser.parse(buffer))
|
61
|
+
script = Script.load(path: path, source: source)
|
51
62
|
rescue StandardError, LoadError, Preprocessor::Error => exn
|
52
63
|
script = exn
|
53
64
|
end
|
@@ -55,13 +66,6 @@ module Querly
|
|
55
66
|
yield(path, script)
|
56
67
|
end
|
57
68
|
|
58
|
-
def parser
|
59
|
-
Parser::Ruby25.new(Builder.new).tap do |parser|
|
60
|
-
parser.diagnostics.all_errors_are_fatal = true
|
61
|
-
parser.diagnostics.ignore_warnings = true
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
69
|
def preprocessors
|
66
70
|
config&.preprocessors || {}
|
67
71
|
end
|
@@ -98,17 +102,7 @@ module Querly
|
|
98
102
|
preprocessors.key?(path.extname)
|
99
103
|
end
|
100
104
|
|
101
|
-
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
class Builder < Parser::Builders::Default
|
106
|
-
def string_value(token)
|
107
|
-
value(token)
|
108
|
-
end
|
109
|
-
|
110
|
-
def emit_lambda
|
111
|
-
true
|
105
|
+
yield path if should_load_file
|
112
106
|
end
|
113
107
|
end
|
114
108
|
end
|
data/lib/querly/version.rb
CHANGED
data/manual/patterns.md
CHANGED
@@ -71,6 +71,8 @@ bar.foo.baz # foo...bar...baz does not match
|
|
71
71
|
* `1.23` (float)
|
72
72
|
* `:foobar` (symbol)
|
73
73
|
* `:symbol:` (any symbol literal)
|
74
|
+
* `"foobar"` (string)
|
75
|
+
* NOTE: It only supports double quotation.
|
74
76
|
* `:string:` (any string literal)
|
75
77
|
* `:dstr:` (any dstr `"hi #{name}"`)
|
76
78
|
* `true`, `false` (true and false)
|
@@ -145,9 +147,9 @@ end
|
|
145
147
|
|
146
148
|
# Interpolation Syntax
|
147
149
|
|
148
|
-
If you want to describe a pattern that can not be described with
|
150
|
+
If you want to describe a pattern that can not be described with above syntax, you can use interpolation as follows:
|
149
151
|
|
150
|
-
```
|
152
|
+
```yaml
|
151
153
|
id: find_by_abc_and_def
|
152
154
|
pattern:
|
153
155
|
subject: "'finder(...)"
|
@@ -164,6 +166,16 @@ It matches with `find_by_email_and_name(...)`.
|
|
164
166
|
- If value of meta var is a string `foo`, it matches send nodes with exactly same method name
|
165
167
|
- If value of meta var is a regexp `/foo/`, it matches send nodes with method name which `=~` the regexp
|
166
168
|
|
169
|
+
You can also use `as` syntax with `:symbol:` and so on.
|
170
|
+
|
171
|
+
```yaml
|
172
|
+
id: migration_references
|
173
|
+
pattern:
|
174
|
+
subject: "t.integer(:symbol: as 'column, ...)"
|
175
|
+
where:
|
176
|
+
column: '/.+_id/'
|
177
|
+
```
|
178
|
+
|
167
179
|
# Difference from Ruby
|
168
180
|
|
169
181
|
* Method call parenthesis cannot be omitted (if omitted, it means *any arguments*)
|
data/querly.gemspec
CHANGED
@@ -22,14 +22,20 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
23
|
spec.require_paths = ["lib"]
|
24
24
|
|
25
|
+
spec.required_ruby_version = ">= 2.7"
|
26
|
+
|
25
27
|
spec.add_development_dependency "bundler", ">= 1.12"
|
26
|
-
spec.add_development_dependency "rake", "~>
|
28
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
27
29
|
spec.add_development_dependency "minitest", "~> 5.0"
|
28
|
-
spec.add_development_dependency "racc", "
|
30
|
+
spec.add_development_dependency "racc", ">= 1.4.14"
|
29
31
|
spec.add_development_dependency "unification_assertion", "0.0.1"
|
32
|
+
spec.add_development_dependency "better_html", "~> 1.0.13"
|
33
|
+
spec.add_development_dependency "slim", "~> 4.0.1"
|
34
|
+
spec.add_development_dependency "haml", "~> 5.0.4"
|
30
35
|
|
31
|
-
spec.add_dependency 'thor', ">= 0.19.0"
|
32
|
-
spec.add_dependency "parser", ">=
|
36
|
+
spec.add_dependency 'thor', ">= 0.19.0"
|
37
|
+
spec.add_dependency "parser", ">= 3.0"
|
33
38
|
spec.add_dependency "rainbow", ">= 2.1"
|
34
39
|
spec.add_dependency "activesupport", ">= 5.0"
|
40
|
+
spec.add_dependency "parallel", "~>1.17"
|
35
41
|
end
|
data/sample.yaml
CHANGED
data/template.yml
CHANGED
@@ -58,7 +58,9 @@ rules:
|
|
58
58
|
assert_empty some.count
|
59
59
|
|
60
60
|
preprocessor:
|
61
|
-
.slim: slimrb --compile
|
61
|
+
# .slim: slimrb --compile # Install `slim` gem for slim support
|
62
|
+
# .erb: querly-pp erb # Install `better_erb` gem for erb support
|
63
|
+
# .haml: querly-pp haml # Install `haml` gem for haml support
|
62
64
|
|
63
65
|
check:
|
64
66
|
- path: /
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: querly
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Soutaro Matsumoto
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-07-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '13.0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '13.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: minitest
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -56,14 +56,14 @@ dependencies:
|
|
56
56
|
name: racc
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: 1.4.14
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 1.4.14
|
69
69
|
- !ruby/object:Gem::Dependency
|
@@ -80,6 +80,48 @@ dependencies:
|
|
80
80
|
- - '='
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 0.0.1
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: better_html
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.0.13
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.0.13
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: slim
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 4.0.1
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 4.0.1
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: haml
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 5.0.4
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 5.0.4
|
83
125
|
- !ruby/object:Gem::Dependency
|
84
126
|
name: thor
|
85
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -87,9 +129,6 @@ dependencies:
|
|
87
129
|
- - ">="
|
88
130
|
- !ruby/object:Gem::Version
|
89
131
|
version: 0.19.0
|
90
|
-
- - "<"
|
91
|
-
- !ruby/object:Gem::Version
|
92
|
-
version: 0.21.0
|
93
132
|
type: :runtime
|
94
133
|
prerelease: false
|
95
134
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -97,23 +136,20 @@ dependencies:
|
|
97
136
|
- - ">="
|
98
137
|
- !ruby/object:Gem::Version
|
99
138
|
version: 0.19.0
|
100
|
-
- - "<"
|
101
|
-
- !ruby/object:Gem::Version
|
102
|
-
version: 0.21.0
|
103
139
|
- !ruby/object:Gem::Dependency
|
104
140
|
name: parser
|
105
141
|
requirement: !ruby/object:Gem::Requirement
|
106
142
|
requirements:
|
107
143
|
- - ">="
|
108
144
|
- !ruby/object:Gem::Version
|
109
|
-
version:
|
145
|
+
version: '3.0'
|
110
146
|
type: :runtime
|
111
147
|
prerelease: false
|
112
148
|
version_requirements: !ruby/object:Gem::Requirement
|
113
149
|
requirements:
|
114
150
|
- - ">="
|
115
151
|
- !ruby/object:Gem::Version
|
116
|
-
version:
|
152
|
+
version: '3.0'
|
117
153
|
- !ruby/object:Gem::Dependency
|
118
154
|
name: rainbow
|
119
155
|
requirement: !ruby/object:Gem::Requirement
|
@@ -142,6 +178,20 @@ dependencies:
|
|
142
178
|
- - ">="
|
143
179
|
- !ruby/object:Gem::Version
|
144
180
|
version: '5.0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: parallel
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - "~>"
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '1.17'
|
188
|
+
type: :runtime
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - "~>"
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '1.17'
|
145
195
|
description: Querly is a query language and tool to find out method calls from Ruby
|
146
196
|
programs. Define rules to check your program with patterns to find out *bad* pieces.
|
147
197
|
Querly finds out matching pieces from your program.
|
@@ -153,8 +203,10 @@ executables:
|
|
153
203
|
extensions: []
|
154
204
|
extra_rdoc_files: []
|
155
205
|
files:
|
206
|
+
- ".github/workflows/rubocop.yml"
|
207
|
+
- ".github/workflows/ruby.yml"
|
156
208
|
- ".gitignore"
|
157
|
-
- ".
|
209
|
+
- ".rubocop.yml"
|
158
210
|
- CHANGELOG.md
|
159
211
|
- Gemfile
|
160
212
|
- LICENSE
|
@@ -212,15 +264,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
212
264
|
requirements:
|
213
265
|
- - ">="
|
214
266
|
- !ruby/object:Gem::Version
|
215
|
-
version: '
|
267
|
+
version: '2.7'
|
216
268
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
217
269
|
requirements:
|
218
270
|
- - ">="
|
219
271
|
- !ruby/object:Gem::Version
|
220
272
|
version: '0'
|
221
273
|
requirements: []
|
222
|
-
|
223
|
-
rubygems_version: 2.7.6
|
274
|
+
rubygems_version: 3.1.2
|
224
275
|
signing_key:
|
225
276
|
specification_version: 4
|
226
277
|
summary: Pattern Based Checking Tool for Ruby
|