rubocop-yast 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/README.md +122 -0
- data/Rakefile +24 -0
- data/lib/rubocop/cop/yast/builtins.rb +39 -30
- data/lib/rubocop/cop/yast/ops.rb +3 -157
- data/lib/rubocop/yast/builtins/builtin.rb +38 -0
- data/lib/rubocop/yast/builtins/getenv.rb +19 -0
- data/lib/rubocop/yast/builtins/time.rb +16 -0
- data/lib/rubocop/yast/builtins/y2log.rb +138 -0
- data/lib/rubocop/yast/node_helpers.rb +22 -0
- data/lib/rubocop/yast/reformatter.rb +43 -0
- data/lib/rubocop/yast/track_variable_scope.rb +184 -0
- data/lib/rubocop/yast/variable_scope.rb +2 -0
- data/lib/rubocop/yast/version.rb +1 -1
- data/rubocop-yast.gemspec +1 -0
- data/spec/builtins_spec.md +614 -0
- data/spec/builtins_spec.rb +543 -41
- data/spec/ops_spec.rb +12 -7
- data/spec/rspec_code.rb +47 -0
- data/spec/rspec_renderer.rb +206 -0
- data/spec/spec_helper.rb +21 -13
- metadata +29 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8f14f2792588ff28f55867a356603deeeafe6deb
|
4
|
+
data.tar.gz: 03433493409f9d50a333391b4e05c8782a525064
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3fed38028da0bd94e103e5a5091e1723857c3c5b1995216fa175f8dc6fadaefc81f77f6182abf80a6cbe7c5e7b41fda716f44150ce6a4dc3aa2a599b7cec01f6
|
7
|
+
data.tar.gz: 32e08248270ec322da28aabf4ff9fa27fc8be17729b3b64d92336106e92cf946a65d3462c4634cca7467ace025eba6ffe41b593aff467af02d09f38c16bd3d8c
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -9,3 +9,125 @@ rubocop-yast
|
|
9
9
|
[![Inline docs](http://inch-ci.org/github/lslezak/rubocop-yast.svg?branch=master)](http://inch-ci.org/github/lslezak/rubocop-yast)
|
10
10
|
|
11
11
|
|
12
|
+
This is a plugin for [RuboCop](https://github.com/bbatsov/rubocop)
|
13
|
+
a Ruby static code analyzer. It was inspired by [Rubocop-Rspec](https://github.com/nevir/rubocop-rspec)
|
14
|
+
and [YCP Zombie Killer](https://github.com/yast/zombie-killer).
|
15
|
+
|
16
|
+
The goal is to create a Rubocop plugin which can check for
|
17
|
+
[YaST](http://yast.github.io/) specific issues. Optionally it should allow to
|
18
|
+
covert some ugly code parts introduced by the automatic code conversion done by
|
19
|
+
[YCP Killer](https://github.com/yast/ycp-killer) (conversion from YCP to Ruby).
|
20
|
+
|
21
|
+
Check [the test descriptions](spec/builtins_spec.md) to see the examples of offense
|
22
|
+
detection and code conversion.
|
23
|
+
|
24
|
+
*The plugin is currently in early development, always manually check the chages
|
25
|
+
done by the plugin! It can eat your code... ;-)*
|
26
|
+
|
27
|
+
|
28
|
+
Installation
|
29
|
+
------------
|
30
|
+
|
31
|
+
The plugin is published at [rubygems.org](https://rubygems.org/gems/rubocop-yast),
|
32
|
+
you can install it using the `gem` command:
|
33
|
+
|
34
|
+
```shell
|
35
|
+
sudo gem install rubocop-yast
|
36
|
+
```
|
37
|
+
|
38
|
+
Usage
|
39
|
+
-----
|
40
|
+
|
41
|
+
You need to manually load the Yast plugin into RuboCop to run the extra checks.
|
42
|
+
There are two options:
|
43
|
+
|
44
|
+
- Use `--require rubocop-yast` command line option when invoking `rubocop`
|
45
|
+
- Enable the plugin in `.rubocop.yml` file:
|
46
|
+
```yaml
|
47
|
+
require:
|
48
|
+
- rubocop-yast
|
49
|
+
```
|
50
|
+
|
51
|
+
See the [RuboCop documentation](https://github.com/bbatsov/rubocop#loading-extensions).
|
52
|
+
|
53
|
+
Configuration
|
54
|
+
-------------
|
55
|
+
|
56
|
+
You can configure Rubocop-Yast the same way as the standard RuboCop checks
|
57
|
+
(see the [RuboCop configuration](https://github.com/bbatsov/rubocop#configuration)):
|
58
|
+
|
59
|
+
```yaml
|
60
|
+
# Check for obsolete Builtins.* calls
|
61
|
+
Yast/Builtins:
|
62
|
+
Enabled: true
|
63
|
+
|
64
|
+
# Check for obsolete Ops.* calls
|
65
|
+
Yast/Ops:
|
66
|
+
Enabled: true
|
67
|
+
# in the safe mode only safe places are reported and fixed
|
68
|
+
SafeMode: true
|
69
|
+
```
|
70
|
+
|
71
|
+
Development
|
72
|
+
-----------
|
73
|
+
|
74
|
+
### Prerequisites
|
75
|
+
|
76
|
+
For development you need some extra development gems. The best way is to install them with [Bundler](http://bundler.io/). To avoid a possible collision with system gems it is recommended
|
77
|
+
to install the gems into a local subdirectory using
|
78
|
+
|
79
|
+
```shell
|
80
|
+
bundle install --path vendor/bundle
|
81
|
+
```
|
82
|
+
|
83
|
+
### Source Directories
|
84
|
+
|
85
|
+
* [`config/default.yml`](config/default.yml) contains the default Cop configurations
|
86
|
+
* [`lib/rubocop/cop/yast`](lib/rubocop/yast) contains Yast Cops (the checks which are called
|
87
|
+
from the main rubocop script)
|
88
|
+
* [`lib/rubocop/yast`](lib/rubocop/yast) contains libraries used by the Cops
|
89
|
+
* [`spec`](spec) contains tests, some tests are automatically generated from a MarkDown
|
90
|
+
documentation
|
91
|
+
|
92
|
+
### Running Tests
|
93
|
+
|
94
|
+
```
|
95
|
+
bundle exec rake
|
96
|
+
```
|
97
|
+
|
98
|
+
By default the tests check the code coverage, if it is below 95% the test fails although
|
99
|
+
there was no test failure.
|
100
|
+
|
101
|
+
### Autocorrecting Rubocop Issues
|
102
|
+
|
103
|
+
```
|
104
|
+
bundle exec rake rubocop:auto_correct
|
105
|
+
```
|
106
|
+
|
107
|
+
You can also load the plugin itself to verify that plugin loading works correctly.
|
108
|
+
(Plugin loading is not covered by tests as it needs the base Rubocop framework.)
|
109
|
+
|
110
|
+
```
|
111
|
+
bundle exec rubocop -r rubocop-yast
|
112
|
+
```
|
113
|
+
|
114
|
+
### Building a Gem
|
115
|
+
|
116
|
+
```
|
117
|
+
bundle exec rake build
|
118
|
+
```
|
119
|
+
|
120
|
+
This builds `pkg/rubocop-yast-<version>.gem` gem file, it can be installed locally using
|
121
|
+
```
|
122
|
+
sudo gem install --local pkg/rubocop-yast-<version>.gem
|
123
|
+
```
|
124
|
+
|
125
|
+
### Publishing the Gem to Rubygems.org
|
126
|
+
|
127
|
+
Increase the version in [`lib/rubocop/yast/version.rb`](lib/rubocop/yast/version.rb) file
|
128
|
+
and then run:
|
129
|
+
|
130
|
+
```
|
131
|
+
bundle exec rake release
|
132
|
+
```
|
133
|
+
|
data/Rakefile
CHANGED
@@ -10,8 +10,32 @@ rescue Bundler::BundlerError => e
|
|
10
10
|
exit e.status_code
|
11
11
|
end
|
12
12
|
|
13
|
+
require "redcarpet"
|
14
|
+
require_relative "spec/rspec_renderer"
|
15
|
+
|
16
|
+
def render_markdown(renderer_class, task)
|
17
|
+
puts "Rendering file: #{task.name}"
|
18
|
+
markdown = Redcarpet::Markdown.new(renderer_class, fenced_code_blocks: true)
|
19
|
+
|
20
|
+
string = markdown.render(File.read(task.prerequisites[0]))
|
21
|
+
File.write(task.name, string)
|
22
|
+
end
|
23
|
+
|
24
|
+
renderer = "spec/rspec_renderer.rb"
|
25
|
+
|
26
|
+
file "spec/builtins_spec.rb" => ["spec/builtins_spec.md", renderer] do |t|
|
27
|
+
render_markdown(RSpecRenderer, t)
|
28
|
+
end
|
29
|
+
|
30
|
+
file "spec/builtins_spec.html" => ["spec/builtins_spec.md", renderer] do |t|
|
31
|
+
render_markdown(Redcarpet::Render::HTML, t)
|
32
|
+
end
|
33
|
+
desc "Render the specification to HTML locally"
|
34
|
+
task html: ["spec/builtins_spec.html"]
|
35
|
+
|
13
36
|
require "rspec/core/rake_task"
|
14
37
|
RSpec::Core::RakeTask.new(:spec)
|
38
|
+
task spec: ["spec/builtins_spec.rb"]
|
15
39
|
|
16
40
|
require "rubocop/rake_task"
|
17
41
|
RuboCop::RakeTask.new(:rubocop)
|
@@ -1,5 +1,10 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
require "rubocop/yast/builtins/builtin"
|
4
|
+
require "rubocop/yast/builtins/getenv"
|
5
|
+
require "rubocop/yast/builtins/time"
|
6
|
+
require "rubocop/yast/builtins/y2log"
|
7
|
+
|
3
8
|
module RuboCop
|
4
9
|
module Cop
|
5
10
|
module Yast
|
@@ -7,23 +12,9 @@ module RuboCop
|
|
7
12
|
class Builtins < Cop
|
8
13
|
include AST::Sexp
|
9
14
|
|
10
|
-
|
15
|
+
attr_reader :handlers, :default_handler
|
11
16
|
|
12
|
-
|
13
|
-
ALLOWED_FUNCTIONS = [
|
14
|
-
# locale dependent sorting in not available in Ruby stdlib
|
15
|
-
:lsort,
|
16
|
-
# gettext helpers
|
17
|
-
:dgettext,
|
18
|
-
:dngettext,
|
19
|
-
:dpgettext,
|
20
|
-
# crypt* helpers
|
21
|
-
:crypt,
|
22
|
-
:cryptmd5,
|
23
|
-
:cryptblowfish,
|
24
|
-
:cryptsha256,
|
25
|
-
:cryptsha512
|
26
|
-
]
|
17
|
+
MSG = "Builtin call `%s` is obsolete, use native Ruby function instead."
|
27
18
|
|
28
19
|
BUILTINS_NODES = [
|
29
20
|
# Builtins.*
|
@@ -34,34 +25,52 @@ module RuboCop
|
|
34
25
|
s(:const, s(:const, s(:cbase), :Yast), :Builtins)
|
35
26
|
]
|
36
27
|
|
28
|
+
def initialize(config = nil, options = nil)
|
29
|
+
super(config, options)
|
30
|
+
|
31
|
+
@handlers = builtin_mapping
|
32
|
+
@default_handler = RuboCop::Yast::Builtins::Builtin.new
|
33
|
+
end
|
34
|
+
|
37
35
|
def on_send(node)
|
38
36
|
receiver, method_name, *_args = *node
|
39
37
|
|
38
|
+
# not an Yast builtin call or not an offense
|
40
39
|
return if !BUILTINS_NODES.include?(receiver) ||
|
41
|
-
|
40
|
+
!builtin_handler(method_name).offense?(node)
|
42
41
|
|
43
42
|
add_offense(node, :selector, format(MSG, method_name))
|
44
43
|
end
|
45
44
|
|
46
45
|
def autocorrect(node)
|
47
|
-
|
48
|
-
_builtins, message, args = *node
|
49
|
-
|
50
|
-
new_code = builtins_replacement(message, args)
|
46
|
+
_builtins, method_name, *_args = *node
|
51
47
|
|
52
|
-
|
53
|
-
end
|
48
|
+
@corrections << builtin_handler(method_name).correction(node)
|
54
49
|
end
|
55
50
|
|
56
51
|
private
|
57
52
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
53
|
+
# rubocop:disable Metrics/MethodLength
|
54
|
+
def builtin_mapping
|
55
|
+
# use the same instance for all logging functions
|
56
|
+
logging = RuboCop::Yast::Builtins::Y2log.new
|
57
|
+
|
58
|
+
{
|
59
|
+
y2debug: logging,
|
60
|
+
y2milestone: logging,
|
61
|
+
y2warning: logging,
|
62
|
+
y2error: logging,
|
63
|
+
y2security: logging,
|
64
|
+
y2internal: logging,
|
65
|
+
|
66
|
+
getenv: RuboCop::Yast::Builtins::Getenv.new,
|
67
|
+
time: RuboCop::Yast::Builtins::Time.new
|
68
|
+
}
|
69
|
+
end
|
70
|
+
# rubocop:enable Metrics/MethodLength
|
71
|
+
|
72
|
+
def builtin_handler(method)
|
73
|
+
handlers[method] || default_handler
|
65
74
|
end
|
66
75
|
end
|
67
76
|
end
|
data/lib/rubocop/cop/yast/ops.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require "rubocop/yast/
|
4
|
-
require "rubocop/yast/variable_scope"
|
3
|
+
require "rubocop/yast/track_variable_scope"
|
5
4
|
|
6
5
|
# We have encountered code that does satisfy our simplifying assumptions,
|
7
6
|
# translating it would not be correct.
|
@@ -14,7 +13,7 @@ module RuboCop
|
|
14
13
|
# This cop checks for Ops.* calls, it can autocorrect safe places or
|
15
14
|
# all places in unsafe mode
|
16
15
|
class Ops < Cop
|
17
|
-
include
|
16
|
+
include RuboCop::Yast::TrackVariableScope
|
18
17
|
|
19
18
|
# Ops replacement mapping
|
20
19
|
REPLACEMENT = {
|
@@ -26,96 +25,10 @@ module RuboCop
|
|
26
25
|
def initialize(config = nil, options = nil)
|
27
26
|
super(config, options)
|
28
27
|
|
29
|
-
@scopes = VariableScopeStack.new
|
30
28
|
@safe_mode = cop_config["SafeMode"]
|
31
29
|
@replaced_nodes = []
|
32
30
|
end
|
33
31
|
|
34
|
-
# FIXME
|
35
|
-
def process(node)
|
36
|
-
return if node.nil?
|
37
|
-
# if ! @unsafe
|
38
|
-
# oops(node, RuntimeError.new("Unknown node type #{node.type}")) \
|
39
|
-
# unless HANDLED_NODE_TYPES.include? node.type
|
40
|
-
# end
|
41
|
-
end
|
42
|
-
|
43
|
-
# currently visible scope
|
44
|
-
def scope
|
45
|
-
scopes.innermost
|
46
|
-
end
|
47
|
-
|
48
|
-
def with_new_scope_rescuing_oops(node, &block)
|
49
|
-
scopes.with_new do
|
50
|
-
block.call if block_given?
|
51
|
-
end
|
52
|
-
rescue => e
|
53
|
-
oops(node, e)
|
54
|
-
end
|
55
|
-
|
56
|
-
def on_def(node)
|
57
|
-
with_new_scope_rescuing_oops(node)
|
58
|
-
end
|
59
|
-
|
60
|
-
def on_defs(node)
|
61
|
-
with_new_scope_rescuing_oops(node)
|
62
|
-
end
|
63
|
-
|
64
|
-
def on_module(node)
|
65
|
-
with_new_scope_rescuing_oops(node)
|
66
|
-
end
|
67
|
-
|
68
|
-
def on_class(node)
|
69
|
-
with_new_scope_rescuing_oops(node)
|
70
|
-
end
|
71
|
-
|
72
|
-
def on_sclass(node)
|
73
|
-
with_new_scope_rescuing_oops(node)
|
74
|
-
end
|
75
|
-
|
76
|
-
# def on_unless
|
77
|
-
# Does not exist.
|
78
|
-
# `unless` is parsed as an `if` with then_body and else_body swapped.
|
79
|
-
# Compare with `while` and `until` which cannot do that and thus need
|
80
|
-
# distinct node types.
|
81
|
-
# end
|
82
|
-
|
83
|
-
def on_case(node)
|
84
|
-
expr, *cases = *node
|
85
|
-
process(expr)
|
86
|
-
|
87
|
-
cases.each do |case_|
|
88
|
-
scopes.with_copy do
|
89
|
-
process(case_)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
# clean slate
|
94
|
-
scope.clear
|
95
|
-
end
|
96
|
-
|
97
|
-
def on_lvasgn(node)
|
98
|
-
name, value = * node
|
99
|
-
return if value.nil? # and-asgn, or-asgn, resbody do this
|
100
|
-
scope[name].nice = nice(value)
|
101
|
-
end
|
102
|
-
|
103
|
-
def on_and_asgn(node)
|
104
|
-
var, value = * node
|
105
|
-
return if var.type != :lvasgn
|
106
|
-
name = var.children[0]
|
107
|
-
|
108
|
-
scope[name].nice &&= nice(value)
|
109
|
-
end
|
110
|
-
|
111
|
-
def on_or_asgn(node)
|
112
|
-
var, value = * node
|
113
|
-
return if var.type != :lvasgn
|
114
|
-
name = var.children[0]
|
115
|
-
|
116
|
-
scope[name].nice ||= nice(value)
|
117
|
-
end
|
118
|
-
|
119
32
|
def on_send(node)
|
120
33
|
return unless call?(node, :Ops, :add)
|
121
34
|
|
@@ -125,75 +38,8 @@ module RuboCop
|
|
125
38
|
add_offense(node, :selector, format(MSG, method))
|
126
39
|
end
|
127
40
|
|
128
|
-
def on_block(_node)
|
129
|
-
# ignore body, clean slate
|
130
|
-
scope.clear
|
131
|
-
end
|
132
|
-
alias_method :on_for, :on_block
|
133
|
-
|
134
|
-
def on_while(_node)
|
135
|
-
# ignore both condition and body,
|
136
|
-
# with a simplistic scope we cannot handle them
|
137
|
-
|
138
|
-
# clean slate
|
139
|
-
scope.clear
|
140
|
-
end
|
141
|
-
alias_method :on_until, :on_while
|
142
|
-
|
143
|
-
# Exceptions:
|
144
|
-
# `raise` is an ordinary :send for the parser
|
145
|
-
|
146
|
-
def on_rescue(node)
|
147
|
-
# (:rescue, begin-block, resbody..., else-block-or-nil)
|
148
|
-
_begin_body, *_rescue_bodies, _else_body = *node
|
149
|
-
|
150
|
-
# FIXME
|
151
|
-
# @source_rewriter.transaction do
|
152
|
-
# process(begin_body)
|
153
|
-
# process(else_body)
|
154
|
-
# rescue_bodies.each do |r|
|
155
|
-
# process(r)
|
156
|
-
# end
|
157
|
-
# end
|
158
|
-
# rescue TooComplexToTranslateError
|
159
|
-
# warning "begin-rescue is too complex to translate due to a retry"
|
160
|
-
# end
|
161
|
-
end
|
162
|
-
|
163
|
-
def on_resbody(_node)
|
164
|
-
# How it is parsed:
|
165
|
-
# (:resbody, exception-types-or-nil, exception-variable-or-nil, body)
|
166
|
-
# exception-types is an :array
|
167
|
-
# exception-variable is a (:lvasgn, name), without a value
|
168
|
-
|
169
|
-
# A rescue means that *some* previous code was skipped.
|
170
|
-
# We know nothing. We could process the resbodies individually,
|
171
|
-
# and join begin-block with else-block, but it is little worth
|
172
|
-
# because they will contain few zombies.
|
173
|
-
scope.clear
|
174
|
-
end
|
175
|
-
|
176
|
-
def on_ensure(_node)
|
177
|
-
# (:ensure, guarded-code, ensuring-code)
|
178
|
-
# guarded-code may be a :rescue or not
|
179
|
-
|
180
|
-
scope.clear
|
181
|
-
end
|
182
|
-
|
183
|
-
def on_retry(_node)
|
184
|
-
# that makes the :rescue a loop, top-down data-flow fails
|
185
|
-
# FIXME
|
186
|
-
# raise TooComplexToTranslateError
|
187
|
-
end
|
188
|
-
|
189
41
|
private
|
190
42
|
|
191
|
-
def oops(node, exception)
|
192
|
-
puts "Node exception @ #{node.loc.expression}"
|
193
|
-
puts "Offending node: #{node.inspect}"
|
194
|
-
raise exception unless exception.is_a?(TooComplexToTranslateError)
|
195
|
-
end
|
196
|
-
|
197
43
|
def call?(node, namespace, message)
|
198
44
|
n_receiver, n_message = *node
|
199
45
|
n_receiver && n_receiver.type == :const &&
|
@@ -219,7 +65,7 @@ module RuboCop
|
|
219
65
|
"#{arg2.loc.expression.source}"
|
220
66
|
end
|
221
67
|
|
222
|
-
attr_reader :
|
68
|
+
attr_reader :safe_mode
|
223
69
|
end
|
224
70
|
end
|
225
71
|
end
|