rubocop-yast 0.0.4 → 0.0.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c16c03c56b76dc2b1b8896fda2f2d5765d686c41
4
- data.tar.gz: 695426ba9cb0fd0ff1d273d337c3947f8a785b80
3
+ metadata.gz: 8f14f2792588ff28f55867a356603deeeafe6deb
4
+ data.tar.gz: 03433493409f9d50a333391b4e05c8782a525064
5
5
  SHA512:
6
- metadata.gz: 5d69f1ac5711c04993f135091ea1ea1d6ac4159c579a45afe7d3c783f6650416af0aef1b4819ec0a0033dfc140fd1ad54f4e994332ef389905096fad1de8ca94
7
- data.tar.gz: 5bd8f3cd68cdbd225c148eec3082914ff9c4345926bf14b0819f9d61b39a857277e0be03deebffb2192ee6a3a520d051935a65ea7695c28f13dbd0adc6478d2b
6
+ metadata.gz: 3fed38028da0bd94e103e5a5091e1723857c3c5b1995216fa175f8dc6fadaefc81f77f6182abf80a6cbe7c5e7b41fda716f44150ce6a4dc3aa2a599b7cec01f6
7
+ data.tar.gz: 32e08248270ec322da28aabf4ff9fa27fc8be17729b3b64d92336106e92cf946a65d3462c4634cca7467ace025eba6ffe41b593aff467af02d09f38c16bd3d8c
data/Gemfile CHANGED
@@ -6,4 +6,5 @@ gemspec
6
6
 
7
7
  group :test do
8
8
  gem "coveralls", require: false if ENV["TRAVIS"]
9
+ gem "byebug", require: false if ENV["DEBUGGER"]
9
10
  end
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
- MSG = "Builtin call `%s` is obsolete, use native Ruby function instead."
15
+ attr_reader :handlers, :default_handler
11
16
 
12
- # white list of allowed Builtins calls
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
- ALLOWED_FUNCTIONS.include?(method_name)
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
- @corrections << lambda do |corrector|
48
- _builtins, message, args = *node
49
-
50
- new_code = builtins_replacement(message, args)
46
+ _builtins, method_name, *_args = *node
51
47
 
52
- corrector.replace(node.loc.expression, new_code) if new_code
53
- end
48
+ @corrections << builtin_handler(method_name).correction(node)
54
49
  end
55
50
 
56
51
  private
57
52
 
58
- def builtins_replacement(message, args)
59
- case message
60
- when :getenv
61
- "ENV[#{args.loc.expression.source}]"
62
- when :time
63
- "::Time.now.to_i"
64
- end
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
@@ -1,7 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- require "rubocop/yast/niceness"
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 Niceness
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 :scopes, :safe_mode
68
+ attr_reader :safe_mode
223
69
  end
224
70
  end
225
71
  end