rubocop-yast 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/config/default.yml +8 -0
- data/lib/rubocop-yast.rb +4 -0
- data/lib/rubocop/cop/yast/builtins.rb +28 -1
- data/lib/rubocop/cop/yast/ops.rb +226 -0
- data/lib/rubocop/yast/config.rb +19 -0
- data/lib/rubocop/yast/niceness.rb +60 -0
- data/lib/rubocop/yast/variable_scope.rb +62 -0
- data/lib/rubocop/yast/version.rb +1 -1
- data/rubocop-yast.gemspec +3 -2
- data/spec/builtins_spec.rb +15 -1
- data/spec/ops_spec.rb +116 -0
- metadata +10 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c16c03c56b76dc2b1b8896fda2f2d5765d686c41
|
4
|
+
data.tar.gz: 695426ba9cb0fd0ff1d273d337c3947f8a785b80
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5d69f1ac5711c04993f135091ea1ea1d6ac4159c579a45afe7d3c783f6650416af0aef1b4819ec0a0033dfc140fd1ad54f4e994332ef389905096fad1de8ca94
|
7
|
+
data.tar.gz: 5bd8f3cd68cdbd225c148eec3082914ff9c4345926bf14b0819f9d61b39a857277e0be03deebffb2192ee6a3a520d051935a65ea7695c28f13dbd0adc6478d2b
|
data/config/default.yml
ADDED
data/lib/rubocop-yast.rb
CHANGED
@@ -16,7 +16,13 @@ module RuboCop
|
|
16
16
|
# gettext helpers
|
17
17
|
:dgettext,
|
18
18
|
:dngettext,
|
19
|
-
:dpgettext
|
19
|
+
:dpgettext,
|
20
|
+
# crypt* helpers
|
21
|
+
:crypt,
|
22
|
+
:cryptmd5,
|
23
|
+
:cryptblowfish,
|
24
|
+
:cryptsha256,
|
25
|
+
:cryptsha512
|
20
26
|
]
|
21
27
|
|
22
28
|
BUILTINS_NODES = [
|
@@ -36,6 +42,27 @@ module RuboCop
|
|
36
42
|
|
37
43
|
add_offense(node, :selector, format(MSG, method_name))
|
38
44
|
end
|
45
|
+
|
46
|
+
def autocorrect(node)
|
47
|
+
@corrections << lambda do |corrector|
|
48
|
+
_builtins, message, args = *node
|
49
|
+
|
50
|
+
new_code = builtins_replacement(message, args)
|
51
|
+
|
52
|
+
corrector.replace(node.loc.expression, new_code) if new_code
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
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
|
65
|
+
end
|
39
66
|
end
|
40
67
|
end
|
41
68
|
end
|
@@ -0,0 +1,226 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "rubocop/yast/niceness"
|
4
|
+
require "rubocop/yast/variable_scope"
|
5
|
+
|
6
|
+
# We have encountered code that does satisfy our simplifying assumptions,
|
7
|
+
# translating it would not be correct.
|
8
|
+
class TooComplexToTranslateError < Exception
|
9
|
+
end
|
10
|
+
|
11
|
+
module RuboCop
|
12
|
+
module Cop
|
13
|
+
module Yast
|
14
|
+
# This cop checks for Ops.* calls, it can autocorrect safe places or
|
15
|
+
# all places in unsafe mode
|
16
|
+
class Ops < Cop
|
17
|
+
include Niceness
|
18
|
+
|
19
|
+
# Ops replacement mapping
|
20
|
+
REPLACEMENT = {
|
21
|
+
add: "+"
|
22
|
+
}
|
23
|
+
|
24
|
+
MSG = "Obsolete Ops.%s call found"
|
25
|
+
|
26
|
+
def initialize(config = nil, options = nil)
|
27
|
+
super(config, options)
|
28
|
+
|
29
|
+
@scopes = VariableScopeStack.new
|
30
|
+
@safe_mode = cop_config["SafeMode"]
|
31
|
+
@replaced_nodes = []
|
32
|
+
end
|
33
|
+
|
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
|
+
def on_send(node)
|
120
|
+
return unless call?(node, :Ops, :add)
|
121
|
+
|
122
|
+
_ops, method, a, b = *node
|
123
|
+
return if !(nice(a) && nice(b)) && safe_mode
|
124
|
+
|
125
|
+
add_offense(node, :selector, format(MSG, method))
|
126
|
+
end
|
127
|
+
|
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
|
+
private
|
190
|
+
|
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
|
+
def call?(node, namespace, message)
|
198
|
+
n_receiver, n_message = *node
|
199
|
+
n_receiver && n_receiver.type == :const &&
|
200
|
+
n_receiver.children[0].nil? &&
|
201
|
+
n_receiver.children[1] == namespace &&
|
202
|
+
n_message == message
|
203
|
+
end
|
204
|
+
|
205
|
+
def autocorrect(node)
|
206
|
+
@corrections << lambda do |corrector|
|
207
|
+
_ops, message, arg1, arg2 = *node
|
208
|
+
|
209
|
+
new_ops = REPLACEMENT[message]
|
210
|
+
return unless new_ops
|
211
|
+
|
212
|
+
corrector.replace(node.loc.expression,
|
213
|
+
ops_replacement(new_ops, arg1, arg2))
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def ops_replacement(new_ops, arg1, arg2)
|
218
|
+
"#{arg1.loc.expression.source} #{new_ops} " \
|
219
|
+
"#{arg2.loc.expression.source}"
|
220
|
+
end
|
221
|
+
|
222
|
+
attr_reader :scopes, :safe_mode
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
module RuboCop
|
6
|
+
module Yast
|
7
|
+
# patch the Rubocop config - include the plugin defaults
|
8
|
+
module Config
|
9
|
+
DEFAULT = File.expand_path("../../../../config/default.yml", __FILE__)
|
10
|
+
|
11
|
+
def self.load_defaults
|
12
|
+
plugin_config = YAML.load_file(DEFAULT)
|
13
|
+
config = ConfigLoader.merge_with_default(plugin_config, DEFAULT)
|
14
|
+
|
15
|
+
ConfigLoader.instance_variable_set(:@default_configuration, config)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require "set"
|
2
|
+
|
3
|
+
# Niceness of a node means that it cannot be nil.
|
4
|
+
#
|
5
|
+
# Note that the module depends on the includer
|
6
|
+
# to provide #scope (for #nice_variable)
|
7
|
+
module Niceness
|
8
|
+
# Literals are nice, except the nil literal.
|
9
|
+
NICE_LITERAL_NODE_TYPES = [
|
10
|
+
:self,
|
11
|
+
:false, :true,
|
12
|
+
:int, :float,
|
13
|
+
:str, :sym, :regexp,
|
14
|
+
:array, :hash, :pair, :irange, # may contain nils but they are not nil
|
15
|
+
:dstr, # "String #{interpolation}" mixes :str, :begin
|
16
|
+
:dsym # :"#{foo}"
|
17
|
+
].to_set
|
18
|
+
|
19
|
+
def nice(node)
|
20
|
+
nice_literal(node) || nice_variable(node) || nice_send(node) ||
|
21
|
+
nice_begin(node)
|
22
|
+
end
|
23
|
+
|
24
|
+
def nice_literal(node)
|
25
|
+
NICE_LITERAL_NODE_TYPES.include? node.type
|
26
|
+
end
|
27
|
+
|
28
|
+
def nice_variable(node)
|
29
|
+
node.type == :lvar && scope[node.children.first].nice
|
30
|
+
end
|
31
|
+
|
32
|
+
# Methods that preserve niceness if all their arguments are nice
|
33
|
+
# These are global, called with a nil receiver
|
34
|
+
NICE_GLOBAL_METHODS = {
|
35
|
+
# message, number of arguments
|
36
|
+
_: 1
|
37
|
+
}.freeze
|
38
|
+
|
39
|
+
NICE_OPERATORS = {
|
40
|
+
# message, number of arguments (other than receiver)
|
41
|
+
:+ => 1
|
42
|
+
}.freeze
|
43
|
+
|
44
|
+
def nice_send(node)
|
45
|
+
return false unless node.type == :send
|
46
|
+
receiver, message, *args = *node
|
47
|
+
|
48
|
+
if receiver.nil?
|
49
|
+
arity = NICE_GLOBAL_METHODS.fetch(message, -1)
|
50
|
+
else
|
51
|
+
return false unless nice(receiver)
|
52
|
+
arity = NICE_OPERATORS.fetch(message, -1)
|
53
|
+
end
|
54
|
+
args.size == arity && args.all? { |a| nice(a) }
|
55
|
+
end
|
56
|
+
|
57
|
+
def nice_begin(node)
|
58
|
+
node.type == :begin && nice(node.children.last)
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Tracks state for a variable
|
2
|
+
class VariableState
|
3
|
+
attr_accessor :nice
|
4
|
+
end
|
5
|
+
|
6
|
+
# Tracks state for local variables visible at certain point.
|
7
|
+
# Keys are symbols, values are VariableState
|
8
|
+
class VariableScope < Hash
|
9
|
+
def initialize
|
10
|
+
super do |hash, key|
|
11
|
+
hash[key] = VariableState.new
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Deep copy the VariableState values
|
16
|
+
def dup
|
17
|
+
copy = self.class.new
|
18
|
+
each do |k, v|
|
19
|
+
copy[k] = v.dup
|
20
|
+
end
|
21
|
+
copy
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [VariableState] state
|
25
|
+
def [](varname)
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
29
|
+
# Set state for a variable
|
30
|
+
def []=(varname, state)
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# A stack of VariableScope
|
36
|
+
class VariableScopeStack
|
37
|
+
def initialize
|
38
|
+
outer_scope = VariableScope.new
|
39
|
+
@stack = [outer_scope]
|
40
|
+
end
|
41
|
+
|
42
|
+
# The innermost, or current VariableScope
|
43
|
+
def innermost
|
44
|
+
@stack.last
|
45
|
+
end
|
46
|
+
|
47
|
+
# Run *block* using a new clean scope
|
48
|
+
# @return the scope as the block left it, popped from the stack
|
49
|
+
def with_new(&block)
|
50
|
+
@stack.push VariableScope.new
|
51
|
+
block.call
|
52
|
+
@stack.pop
|
53
|
+
end
|
54
|
+
|
55
|
+
# Run *block* using a copy of the innermost scope
|
56
|
+
# @return the scope as the block left it, popped from the stack
|
57
|
+
def with_copy(&block)
|
58
|
+
@stack.push innermost.dup
|
59
|
+
block.call
|
60
|
+
@stack.pop
|
61
|
+
end
|
62
|
+
end
|
data/lib/rubocop/yast/version.rb
CHANGED
data/rubocop-yast.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
spec.files = Dir[
|
22
|
-
"{lib,spec}/**/*",
|
22
|
+
"{config,lib,spec}/**/*",
|
23
23
|
"*.md",
|
24
24
|
"*.gemspec",
|
25
25
|
"Gemfile",
|
@@ -28,7 +28,8 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.test_files = spec.files.grep(/^spec\//)
|
29
29
|
spec.extra_rdoc_files = ["LICENSE", "README.md"]
|
30
30
|
|
31
|
-
spec.
|
31
|
+
spec.add_runtime_dependency("rubocop", "~> 0.27")
|
32
|
+
|
32
33
|
spec.add_development_dependency("rake")
|
33
34
|
spec.add_development_dependency("rspec", "~> 3.1.0")
|
34
35
|
spec.add_development_dependency("simplecov")
|
data/spec/builtins_spec.rb
CHANGED
@@ -6,7 +6,7 @@ def expect_y2milestone_offense(cop)
|
|
6
6
|
expect(cop.offenses.size).to eq(1)
|
7
7
|
expect(cop.offenses.first.line).to eq(1)
|
8
8
|
expect(cop.messages).to eq(["Builtin call `y2milestone` is obsolete, " \
|
9
|
-
|
9
|
+
"use native Ruby function instead."])
|
10
10
|
end
|
11
11
|
|
12
12
|
describe RuboCop::Cop::Yast::Builtins do
|
@@ -48,4 +48,18 @@ describe RuboCop::Cop::Yast::Builtins do
|
|
48
48
|
expect(cop.offenses).to be_empty
|
49
49
|
end
|
50
50
|
|
51
|
+
it "auto-corrects Builtins.time with ::Time.now.to_i" do
|
52
|
+
new_source = autocorrect_source(cop, "Builtins.time")
|
53
|
+
expect(new_source).to eq("::Time.now.to_i")
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'auto-corrects Builtins.getenv("foo") with ENV["foo"]' do
|
57
|
+
new_source = autocorrect_source(cop, 'Builtins.getenv("foo")')
|
58
|
+
expect(new_source).to eq('ENV["foo"]')
|
59
|
+
end
|
60
|
+
|
61
|
+
it "auto-corrects Builtins.getenv(foo) with ENV[foo]" do
|
62
|
+
new_source = autocorrect_source(cop, "Builtins.getenv(foo)")
|
63
|
+
expect(new_source).to eq("ENV[foo]")
|
64
|
+
end
|
51
65
|
end
|
data/spec/ops_spec.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
def config(safe_mode: true)
|
6
|
+
conf = { "Yast/Ops" => { "SafeMode" => safe_mode } }
|
7
|
+
RuboCop::Config.new(conf)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe RuboCop::Cop::Yast::Ops do
|
11
|
+
context("In safe mode") do
|
12
|
+
subject(:cop) { described_class.new(config(safe_mode: true)) }
|
13
|
+
|
14
|
+
it "finds trivial Ops.add call" do
|
15
|
+
inspect_source(cop, ["Ops.add(2, 4)"])
|
16
|
+
|
17
|
+
expect(cop.offenses.size).to eq(1)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "finds Ops.add call with variable" do
|
21
|
+
inspect_source(cop, ["foo = 2\n Ops.add(foo, 4)"])
|
22
|
+
|
23
|
+
expect(cop.offenses.size).to eq(1)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "finds Ops.add call with variable inside condition" do
|
27
|
+
inspect_source(cop, ["foo = 1\nif true\nOps.add(foo, 4)\nend"])
|
28
|
+
|
29
|
+
expect(cop.offenses.size).to eq(1)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "ignores unsafe calls" do
|
33
|
+
inspect_source(cop, ["if true\nOps.add(foo, 4)\nend"])
|
34
|
+
|
35
|
+
expect(cop.offenses).to be_empty
|
36
|
+
end
|
37
|
+
|
38
|
+
# check that all node types are handled properly
|
39
|
+
it "parses complex code" do
|
40
|
+
src = <<-EOF
|
41
|
+
module Foo
|
42
|
+
class Bar
|
43
|
+
def baz(arg)
|
44
|
+
case arg
|
45
|
+
when :foo
|
46
|
+
a &&= true
|
47
|
+
b ||= true
|
48
|
+
end
|
49
|
+
rescue e
|
50
|
+
while false
|
51
|
+
find.foo do
|
52
|
+
end
|
53
|
+
retry
|
54
|
+
end
|
55
|
+
ensure
|
56
|
+
sure
|
57
|
+
end
|
58
|
+
class << foo
|
59
|
+
end
|
60
|
+
def self.foo
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
EOF
|
65
|
+
|
66
|
+
inspect_source(cop, src)
|
67
|
+
|
68
|
+
expect(cop.offenses).to be_empty
|
69
|
+
end
|
70
|
+
|
71
|
+
it "auto-corrects Ops.add(2, 4) with 2 + 4" do
|
72
|
+
new_source = autocorrect_source(cop, "Ops.add(2, 4)")
|
73
|
+
expect(new_source).to eq("2 + 4")
|
74
|
+
end
|
75
|
+
|
76
|
+
it "auto-corrects Ops.add(a, b) with a + b" do
|
77
|
+
new_source = autocorrect_source(cop, "a = 1; b = 2; Ops.add(a, b)")
|
78
|
+
expect(new_source).to eq("a = 1; b = 2; a + b")
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'auto-corrects Ops.add("foo", "bar") with "foo" + "bar"' do
|
82
|
+
new_source = autocorrect_source(cop, 'Ops.add("foo", "bar")')
|
83
|
+
expect(new_source).to eq('"foo" + "bar"')
|
84
|
+
end
|
85
|
+
|
86
|
+
# FIXME: auto-correct does not work work recursively
|
87
|
+
xit "auto-corrects nested Ops.add calls" do
|
88
|
+
new_source = autocorrect_source(cop,
|
89
|
+
'Ops.add("foo", Ops.add("bar", "baz"))')
|
90
|
+
expect(new_source).to eq('"foo" + "bar + baz"')
|
91
|
+
end
|
92
|
+
|
93
|
+
it "keeps unsafe call Ops.add(foo, bar)" do
|
94
|
+
source = "foo = 1; Ops.add(foo, bar)"
|
95
|
+
new_source = autocorrect_source(cop, source)
|
96
|
+
expect(new_source).to eq(source)
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
context("In unsafe mode") do
|
102
|
+
subject(:cop) { described_class.new(config(safe_mode: false)) }
|
103
|
+
|
104
|
+
it "finds unsafe Ops.add calls" do
|
105
|
+
inspect_source(cop, ["if true\nOps.add(foo, 4)\nend"])
|
106
|
+
|
107
|
+
expect(cop.offenses.size).to eq(1)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "auto-corrects unsafe call Ops.add(foo, bar) with foo + bar" do
|
111
|
+
new_source = autocorrect_source(cop, "Ops.add(foo, bar)")
|
112
|
+
expect(new_source).to eq("foo + bar")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubocop-yast
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ladislav Slezák
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-12-
|
11
|
+
date: 2014-12-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubocop
|
@@ -17,7 +17,7 @@ dependencies:
|
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0.27'
|
20
|
-
type: :
|
20
|
+
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
@@ -81,11 +81,17 @@ files:
|
|
81
81
|
- LICENSE
|
82
82
|
- README.md
|
83
83
|
- Rakefile
|
84
|
+
- config/default.yml
|
84
85
|
- lib/rubocop-yast.rb
|
85
86
|
- lib/rubocop/cop/yast/builtins.rb
|
87
|
+
- lib/rubocop/cop/yast/ops.rb
|
88
|
+
- lib/rubocop/yast/config.rb
|
89
|
+
- lib/rubocop/yast/niceness.rb
|
90
|
+
- lib/rubocop/yast/variable_scope.rb
|
86
91
|
- lib/rubocop/yast/version.rb
|
87
92
|
- rubocop-yast.gemspec
|
88
93
|
- spec/builtins_spec.rb
|
94
|
+
- spec/ops_spec.rb
|
89
95
|
- spec/spec_helper.rb
|
90
96
|
homepage: http://github.com/yast/rubocop-yast
|
91
97
|
licenses:
|
@@ -113,4 +119,5 @@ specification_version: 4
|
|
113
119
|
summary: Specific YaST Rubocop checks
|
114
120
|
test_files:
|
115
121
|
- spec/builtins_spec.rb
|
122
|
+
- spec/ops_spec.rb
|
116
123
|
- spec/spec_helper.rb
|