querly 0.15.0 → 1.2.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 +18 -0
- data/.github/workflows/ruby.yml +22 -0
- data/.rubocop.yml +10 -0
- data/CHANGELOG.md +27 -0
- data/README.md +3 -1
- data/Rakefile +1 -1
- data/bin/setup +1 -2
- data/lib/querly.rb +1 -0
- 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/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/logo/Querly horizontal.pdf +793 -0
- data/logo/Querly horizontal.png +0 -0
- data/logo/Querly horizontal.svg +55 -0
- data/logo/Querly logo.png +0 -0
- data/logo/Querly vertical.png +0 -0
- data/manual/patterns.md +14 -2
- data/querly.gemspec +8 -4
- data/sample.yaml +2 -0
- data/template.yml +3 -1
- metadata +73 -16
- 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: 3604de13f36852d57290140b9bf4e4bfeb5811ffb8dbfef054a3e0ce8e9aa47a
|
4
|
+
data.tar.gz: '09038f2030b2c6bc859cfbd7087ae62da8efd2ac4ae1e3b945741d3f65d3146b'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8c2dbbd840b5eb4fa65418c4d2ab81af6154dbbac94c96d083c4f7f9d88e5447666f7d583b54f4c266f52c4f4f15d2f38210c353762c2f8c281785da161d0aec
|
7
|
+
data.tar.gz: 4fd40dc297b03b7de10e3f1bb348a553d1af355f3e6310feed4b9b8e1fe5324757684e443cb8a699952bc0ccfedcf95f072e13890b5cc838f4c2850b7ef09d58
|
@@ -0,0 +1,18 @@
|
|
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: 2.6
|
13
|
+
- run: gem install rubocop rubocop-rubycw --no-document
|
14
|
+
- name: Run RuboCop
|
15
|
+
shell: bash
|
16
|
+
run: |
|
17
|
+
# https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message
|
18
|
+
rubocop | ruby -pe 'sub(/^(.+):(\d+):(\d+): (.): (.+)$/, %q{::error file=\1,line=\2,col=\3::\5})'
|
@@ -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.6, 2.7, head]
|
15
|
+
steps:
|
16
|
+
- uses: actions/checkout@v2
|
17
|
+
- uses: ruby/setup-ruby@v1
|
18
|
+
with:
|
19
|
+
ruby-version: ${{ matrix.ruby }}
|
20
|
+
- run: bundle install --jobs 4 --retry 3
|
21
|
+
- run: bin/setup
|
22
|
+
- run: bundle exec rake build test
|
data/.rubocop.yml
ADDED
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,33 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 1.2.0 (2020-12-15)
|
6
|
+
|
7
|
+
* Relax Thor version requirements by @y-yagi ([#85](https://github.com/soutaro/querly/pull/85))
|
8
|
+
* Fix ERB comment preprocessing by @mallowlabs ([#84](https://github.com/soutaro/querly/pull/84))
|
9
|
+
* Better error message for Ruby code syntax error by @ybiquitous ([#83](https://github.com/soutaro/querly/pull/83))
|
10
|
+
|
11
|
+
## 1.1.0 (2020-05-17)
|
12
|
+
|
13
|
+
* Fix invalid bytes sequence in UTF-8 error by @mallowlabs [#75](https://github.com/soutaro/querly/pull/75)
|
14
|
+
* Detect safe navigation operator as a method call by @pocke [#71](https://github.com/soutaro/querly/pull/71)
|
15
|
+
|
16
|
+
## 1.0.0 (2019-7-19)
|
17
|
+
|
18
|
+
* Add `--config` option for `find` and `console` [#67](https://github.com/soutaro/querly/pull/67)
|
19
|
+
* Improve preprocessor performance by processing concurrently [#68](https://github.com/soutaro/querly/pull/68)
|
20
|
+
|
21
|
+
## 0.16.0 (2019-04-23)
|
22
|
+
|
23
|
+
* Support string literal pattern (@pocke) [#64](https://github.com/soutaro/querly/pull/64)
|
24
|
+
* Allow underscore method name pattern (@pocke) [#63](https://github.com/soutaro/querly/pull/63)
|
25
|
+
* Add erb support (@hanachin) [#61](https://github.com/soutaro/querly/pull/61)
|
26
|
+
* Add `exit` command on console (@wata727) [#59](https://github.com/soutaro/querly/pull/59)
|
27
|
+
|
28
|
+
## 0.15.1 (2019-03-12)
|
29
|
+
|
30
|
+
* Relax parser version requirement
|
31
|
+
|
5
32
|
## 0.15.0 (2019-02-13)
|
6
33
|
|
7
34
|
* Fix broken `querly init` template (@ybiquitous) #56
|
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+

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

|
4
6
|
|
5
7
|
Querly is a query language and tool to find out method calls from Ruby programs.
|
6
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
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/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
|