rubocop 0.6.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rubocop might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -266
- data/CHANGELOG.md +49 -7
- data/README.md +75 -2
- data/Rakefile +2 -2
- data/bin/rubocop +15 -10
- data/lib/rubocop.rb +19 -1
- data/lib/rubocop/cli.rb +113 -116
- data/lib/rubocop/config.rb +202 -0
- data/lib/rubocop/config_store.rb +37 -0
- data/lib/rubocop/cop/alias.rb +2 -5
- data/lib/rubocop/cop/align_parameters.rb +1 -1
- data/lib/rubocop/cop/array_literal.rb +43 -4
- data/lib/rubocop/cop/avoid_for.rb +2 -4
- data/lib/rubocop/cop/avoid_global_vars.rb +49 -0
- data/lib/rubocop/cop/block_comments.rb +17 -0
- data/lib/rubocop/cop/brace_after_percent.rb +9 -5
- data/lib/rubocop/cop/{indentation.rb → case_indentation.rb} +1 -1
- data/lib/rubocop/cop/class_methods.rb +20 -0
- data/lib/rubocop/cop/colon_method_call.rb +44 -0
- data/lib/rubocop/cop/cop.rb +30 -2
- data/lib/rubocop/cop/def_parentheses.rb +1 -1
- data/lib/rubocop/cop/empty_line_between_defs.rb +26 -0
- data/lib/rubocop/cop/empty_lines.rb +10 -13
- data/lib/rubocop/cop/eval.rb +22 -0
- data/lib/rubocop/cop/favor_join.rb +37 -0
- data/lib/rubocop/cop/grammar.rb +2 -2
- data/lib/rubocop/cop/hash_literal.rb +43 -4
- data/lib/rubocop/cop/hash_syntax.rb +2 -2
- data/lib/rubocop/cop/if_then_else.rb +1 -1
- data/lib/rubocop/cop/leading_comment_space.rb +20 -0
- data/lib/rubocop/cop/line_continuation.rb +18 -0
- data/lib/rubocop/cop/line_length.rb +1 -1
- data/lib/rubocop/cop/method_and_variable_snake_case.rb +7 -6
- data/lib/rubocop/cop/method_length.rb +4 -15
- data/lib/rubocop/cop/not.rb +15 -0
- data/lib/rubocop/cop/offence.rb +9 -0
- data/lib/rubocop/cop/semicolon.rb +74 -3
- data/lib/rubocop/cop/single_line_methods.rb +60 -0
- data/lib/rubocop/cop/space_after_control_keyword.rb +28 -0
- data/lib/rubocop/cop/surrounding_space.rb +48 -9
- data/lib/rubocop/cop/symbol_array.rb +29 -0
- data/lib/rubocop/cop/trivial_accessors.rb +103 -0
- data/lib/rubocop/cop/unless_else.rb +1 -1
- data/lib/rubocop/cop/variable_interpolation.rb +3 -2
- data/lib/rubocop/cop/word_array.rb +38 -0
- data/lib/rubocop/version.rb +1 -1
- data/rubocop.gemspec +11 -7
- data/spec/project_spec.rb +27 -0
- data/spec/rubocop/cli_spec.rb +549 -487
- data/spec/rubocop/config_spec.rb +399 -0
- data/spec/rubocop/config_store_spec.rb +66 -0
- data/spec/rubocop/cops/alias_spec.rb +7 -0
- data/spec/rubocop/cops/array_literal_spec.rb +8 -1
- data/spec/rubocop/cops/avoid_for_spec.rb +15 -1
- data/spec/rubocop/cops/avoid_global_vars.rb +32 -0
- data/spec/rubocop/cops/block_comments_spec.rb +29 -0
- data/spec/rubocop/cops/brace_after_percent_spec.rb +19 -13
- data/spec/rubocop/cops/{indentation_spec.rb → case_indentation_spec.rb} +2 -2
- data/spec/rubocop/cops/class_methods_spec.rb +49 -0
- data/spec/rubocop/cops/colon_method_call_spec.rb +47 -0
- data/spec/rubocop/cops/empty_line_between_defs_spec.rb +83 -0
- data/spec/rubocop/cops/empty_lines_spec.rb +6 -63
- data/spec/rubocop/cops/eval_spec.rb +36 -0
- data/spec/rubocop/cops/favor_join_spec.rb +39 -0
- data/spec/rubocop/cops/hash_literal_spec.rb +8 -1
- data/spec/rubocop/cops/leading_comment_space_spec.rb +60 -0
- data/spec/rubocop/cops/line_continuation_spec.rb +24 -0
- data/spec/rubocop/cops/line_length_spec.rb +1 -0
- data/spec/rubocop/cops/method_and_variable_snake_case_spec.rb +20 -0
- data/spec/rubocop/cops/method_length_spec.rb +2 -5
- data/spec/rubocop/cops/new_lambda_literal_spec.rb +2 -3
- data/spec/rubocop/cops/not_spec.rb +34 -0
- data/spec/rubocop/cops/offence_spec.rb +7 -0
- data/spec/rubocop/cops/semicolon_spec.rb +79 -4
- data/spec/rubocop/cops/single_line_methods_spec.rb +50 -0
- data/spec/rubocop/cops/space_after_control_keyword_spec.rb +28 -0
- data/spec/rubocop/cops/space_around_equals_in_default_parameter_spec.rb +11 -1
- data/spec/rubocop/cops/space_inside_hash_literal_braces_spec.rb +74 -0
- data/spec/rubocop/cops/symbol_array_spec.rb +25 -0
- data/spec/rubocop/cops/trivial_accessors_spec.rb +332 -0
- data/spec/rubocop/cops/variable_interpolation_spec.rb +10 -1
- data/spec/rubocop/cops/word_array_spec.rb +39 -0
- data/spec/spec_helper.rb +16 -9
- data/spec/support/file_helper.rb +21 -0
- data/spec/support/isolated_environment.rb +27 -0
- metadata +66 -6
@@ -10,30 +10,19 @@ module Rubocop
|
|
10
10
|
def_lineno, end_lineno = def_and_end_lines(tokens, t_ix)
|
11
11
|
length = calculate_length(def_lineno, end_lineno, source)
|
12
12
|
|
13
|
-
|
14
|
-
|
13
|
+
max = MethodLength.config['Max']
|
14
|
+
if length > max
|
15
|
+
message = sprintf(ERROR_MESSAGE, length, max)
|
15
16
|
add_offence(:convention, def_lineno, message)
|
16
17
|
end
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
20
|
-
def self.max
|
21
|
-
MethodLength.config ? MethodLength.config['Max'] || 10 : 10
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.count_comments?
|
25
|
-
if MethodLength.config
|
26
|
-
MethodLength.config['CountComments'] || false
|
27
|
-
else
|
28
|
-
false
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
21
|
private
|
33
22
|
|
34
23
|
def calculate_length(def_lineno, end_lineno, source)
|
35
24
|
lines = source[def_lineno..(end_lineno - 2)].reject(&:empty?)
|
36
|
-
unless MethodLength.
|
25
|
+
unless MethodLength.config['CountComments']
|
37
26
|
lines = lines.reject { |line| line =~ /^\s*#/ }
|
38
27
|
end
|
39
28
|
lines.size
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Rubocop
|
4
|
+
module Cop
|
5
|
+
class Not < Cop
|
6
|
+
ERROR_MESSAGE = 'Use ! instead of not.'
|
7
|
+
|
8
|
+
def inspect(file, source, tokens, sexp)
|
9
|
+
each_keyword('not', tokens) do |t|
|
10
|
+
add_offence(:convention, t.pos.lineno, ERROR_MESSAGE)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/rubocop/cop/offence.rb
CHANGED
@@ -22,6 +22,15 @@ module Rubocop
|
|
22
22
|
def encode_severity
|
23
23
|
@severity.to_s[0].upcase
|
24
24
|
end
|
25
|
+
|
26
|
+
def ==(other)
|
27
|
+
severity == other.severity and line_number == other.line_number and
|
28
|
+
message == other.message
|
29
|
+
end
|
30
|
+
|
31
|
+
def explode
|
32
|
+
[severity, line_number, message]
|
33
|
+
end
|
25
34
|
end
|
26
35
|
end
|
27
36
|
end
|
@@ -6,13 +6,84 @@ module Rubocop
|
|
6
6
|
ERROR_MESSAGE = 'Do not use semicolons to terminate expressions.'
|
7
7
|
|
8
8
|
def inspect(file, source, tokens, sexp)
|
9
|
-
tokens
|
10
|
-
|
9
|
+
@tokens = tokens
|
10
|
+
already_checked_line = nil
|
11
|
+
tokens.each_with_index do |t, ix|
|
11
12
|
if t.type == :on_semicolon
|
12
|
-
|
13
|
+
next if t.pos.lineno == already_checked_line # fast-forward
|
14
|
+
token_1_ix = index_of_first_token_on_line(ix, t.pos.lineno)
|
15
|
+
if %w(def class module).include?(tokens[token_1_ix].text)
|
16
|
+
handle_exceptions_to_the_rule(token_1_ix)
|
17
|
+
if source[t.pos.lineno - 1] =~ /;\s*(#.*)?$/
|
18
|
+
# Semicolons at end of a lines are always reported.
|
19
|
+
add_offence(:convention, t.pos.lineno, ERROR_MESSAGE)
|
20
|
+
end
|
21
|
+
# When dealing with these one line definitions, we check
|
22
|
+
# the whole line at once. That's why we use the variable
|
23
|
+
# already_checked_line to know when to fast-forward past
|
24
|
+
# the current line.
|
25
|
+
already_checked_line = t.pos.lineno
|
26
|
+
else
|
27
|
+
add_offence(:convention, t.pos.lineno, ERROR_MESSAGE)
|
28
|
+
end
|
13
29
|
end
|
14
30
|
end
|
15
31
|
end
|
32
|
+
|
33
|
+
def index_of_first_token_on_line(ix, lineno)
|
34
|
+
# Index of last token on the previous line
|
35
|
+
prev_line_ix =
|
36
|
+
@tokens[0...ix].rindex { |t| t.pos.lineno < lineno } || 0
|
37
|
+
# Index of first non-whitespace token on the current line.
|
38
|
+
prev_line_ix + @tokens[prev_line_ix..ix].index { |t| !whitespace?(t) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def handle_exceptions_to_the_rule(token_1_ix)
|
42
|
+
# We only do further checking of the def case, which means
|
43
|
+
# that there are some cases of semicolon usage within
|
44
|
+
# non-empty one-line class or method definitions that we don't
|
45
|
+
# catch, but these should be rare.
|
46
|
+
if @tokens[token_1_ix].text == 'def'
|
47
|
+
state = :initial
|
48
|
+
@tokens[token_1_ix..-1].each do |t|
|
49
|
+
state = next_state(state, t) || state
|
50
|
+
break if t.text == 'end'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def next_state(state, token)
|
56
|
+
return nil if whitespace?(token) # no state change for whitespace
|
57
|
+
|
58
|
+
case state
|
59
|
+
when :initial
|
60
|
+
:def_kw if token.type == :on_kw
|
61
|
+
when :def_kw
|
62
|
+
:method_name if token.type == :on_ident
|
63
|
+
when :method_name
|
64
|
+
case token.type
|
65
|
+
when :on_lparen then :inside_param_list
|
66
|
+
when :on_semicolon then :method_body
|
67
|
+
end
|
68
|
+
when :inside_param_list
|
69
|
+
:right_after_param_list if token.type == :on_rparen
|
70
|
+
when :right_after_param_list
|
71
|
+
if token.type == :on_semicolon
|
72
|
+
unless Semicolon.config['AllowAfterParameterListInOneLineMethods']
|
73
|
+
add_offence(:convention, token.pos.lineno, ERROR_MESSAGE)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
:method_body
|
77
|
+
when :method_body
|
78
|
+
:semicolon_used if token.type == :on_semicolon
|
79
|
+
when :semicolon_used
|
80
|
+
if token.text != 'end' ||
|
81
|
+
!Semicolon.config['AllowBeforeEndInOneLineMethods']
|
82
|
+
add_offence(:convention, token.pos.lineno, ERROR_MESSAGE)
|
83
|
+
end
|
84
|
+
:method_body
|
85
|
+
end
|
86
|
+
end
|
16
87
|
end
|
17
88
|
end
|
18
89
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Rubocop
|
4
|
+
module Cop
|
5
|
+
class SingleLineMethods < Cop
|
6
|
+
ERROR_MESSAGE = 'Avoid single-line methods.'
|
7
|
+
|
8
|
+
def inspect(file, source, tokens, sexp)
|
9
|
+
if SingleLineMethods.config['AllowIfMethodIsEmpty']
|
10
|
+
is_empty = empty_methods(sexp)
|
11
|
+
end
|
12
|
+
|
13
|
+
lineno_of_def = nil
|
14
|
+
possible_offence = false
|
15
|
+
|
16
|
+
tokens.each_with_index do |token, ix|
|
17
|
+
if possible_offence
|
18
|
+
if token.pos.lineno > lineno_of_def
|
19
|
+
possible_offence = false
|
20
|
+
elsif [token.type, token.text] == [:on_kw, 'end']
|
21
|
+
add_offence(:convention, lineno_of_def, ERROR_MESSAGE)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
if [token.type, token.text] == [:on_kw, 'def']
|
26
|
+
lineno_of_def = token.pos.lineno
|
27
|
+
name_token = tokens[ix..-1].find do |t|
|
28
|
+
[:on_ident, :on_const].include?(t.type)
|
29
|
+
end
|
30
|
+
possible_offence =
|
31
|
+
if SingleLineMethods.config['AllowIfMethodIsEmpty']
|
32
|
+
!is_empty[name_token.pos]
|
33
|
+
else
|
34
|
+
true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
# Returns a hash mapping positions of method names to booleans
|
43
|
+
# saying whether or not the method is empty.
|
44
|
+
def empty_methods(sexp)
|
45
|
+
is_empty = {}
|
46
|
+
# Since def is a keyword, def: can confuse the editor. Hence
|
47
|
+
# Ruby 1.8 hash syntax is used here.
|
48
|
+
# rubocop:disable HashSyntax
|
49
|
+
{ :def => [1, 3], :defs => [3, 5] }.each do |key, offsets|
|
50
|
+
each(key, sexp) do |d|
|
51
|
+
method_name_pos = d[offsets.first][-1]
|
52
|
+
is_empty[method_name_pos] =
|
53
|
+
(d[offsets.last] == [:bodystmt, [[:void_stmt]], nil, nil, nil])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
is_empty
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Rubocop
|
4
|
+
module Cop
|
5
|
+
class SpaceAfterControlKeyword < Cop
|
6
|
+
ERROR_MESSAGE = 'Use space after control keywords.'
|
7
|
+
|
8
|
+
KEYWORDS = %w(if elsif case when while until unless)
|
9
|
+
|
10
|
+
def inspect(file, source, tokens, sexp)
|
11
|
+
# we need to keep track of the previous token to
|
12
|
+
# avoid confusing symbols like :if with real keywords
|
13
|
+
prev = Token.new(0, :init, '')
|
14
|
+
|
15
|
+
tokens.each_cons(2) do |t1, t2|
|
16
|
+
if prev.type != :on_symbeg && t1.type == :on_kw &&
|
17
|
+
KEYWORDS.include?(t1.text) && t2.type != :on_sp
|
18
|
+
add_offence(:convention,
|
19
|
+
t1.pos.lineno,
|
20
|
+
ERROR_MESSAGE)
|
21
|
+
end
|
22
|
+
|
23
|
+
prev = t1
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -13,7 +13,7 @@ module Rubocop
|
|
13
13
|
private
|
14
14
|
|
15
15
|
def previous_non_space(tokens, ix)
|
16
|
-
tokens[0...ix].reverse.find { |t|
|
16
|
+
tokens[0...ix].reverse.find { |t| !whitespace?(t) }
|
17
17
|
end
|
18
18
|
|
19
19
|
def ok_without_spaces?(grammar_path)
|
@@ -66,6 +66,9 @@ module Rubocop
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def check_missing_space(tokens, ix, grammar_path)
|
69
|
+
# Checked by SpaceInsideHashLiteralBraces and not here.
|
70
|
+
return if grammar_path.last == :hash
|
71
|
+
|
69
72
|
t = tokens[ix]
|
70
73
|
case t.type
|
71
74
|
when :on_lbrace
|
@@ -123,17 +126,53 @@ module Rubocop
|
|
123
126
|
end
|
124
127
|
end
|
125
128
|
|
129
|
+
class SpaceInsideHashLiteralBraces < Cop
|
130
|
+
include SurroundingSpace
|
131
|
+
|
132
|
+
def check_missing_space(tokens, ix, grammar_path)
|
133
|
+
if self.class.config['EnforcedStyleIsWithSpaces']
|
134
|
+
check_space(tokens, ix, grammar_path, 'missing') do |t|
|
135
|
+
!(whitespace?(t) || [:on_lbrace, :on_rbrace].include?(t.type))
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def check_unwanted_space(tokens, ix)
|
141
|
+
unless self.class.config['EnforcedStyleIsWithSpaces']
|
142
|
+
grammar_path = @correlations[ix] or return
|
143
|
+
check_space(tokens, ix, grammar_path, 'detected') do |t|
|
144
|
+
whitespace?(t)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
|
151
|
+
def check_space(tokens, ix, grammar_path, word)
|
152
|
+
if grammar_path[-1] == :hash
|
153
|
+
is_offence = case tokens[ix].type
|
154
|
+
when :on_lbrace then yield tokens[ix + 1]
|
155
|
+
when :on_rbrace then yield tokens[ix - 1]
|
156
|
+
else false
|
157
|
+
end
|
158
|
+
if is_offence
|
159
|
+
add_offence(:convention, tokens[ix].pos.lineno,
|
160
|
+
"Space inside hash literal braces #{word}.")
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
126
166
|
class SpaceAroundEqualsInParameterDefault < Cop
|
127
167
|
def inspect(file, source, tokens, sexp)
|
128
168
|
each(:params, sexp) do |s|
|
129
|
-
(s[2] || []).each do |param,
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
end
|
169
|
+
(s[2] || []).each do |param, _|
|
170
|
+
param_pos = param.last
|
171
|
+
ix = tokens.index { |t| t.pos == param_pos }
|
172
|
+
unless whitespace?(tokens[ix + 1]) && whitespace?(tokens[ix + 3])
|
173
|
+
add_offence(:convention, param[-1].lineno,
|
174
|
+
'Surrounding space missing in default value ' +
|
175
|
+
'assignment.')
|
137
176
|
end
|
138
177
|
end
|
139
178
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Rubocop
|
4
|
+
module Cop
|
5
|
+
class SymbolArray < Cop
|
6
|
+
ERROR_MESSAGE = 'Use %i or %I for array of symbols.'
|
7
|
+
|
8
|
+
def inspect(file, source, tokens, sexp)
|
9
|
+
# %i and %I were introduced in Ruby 2.0
|
10
|
+
unless RUBY_VERSION < '2.0.0'
|
11
|
+
each(:array, sexp) do |s|
|
12
|
+
array_elems = s[1]
|
13
|
+
|
14
|
+
# no need to check empty arrays
|
15
|
+
return unless array_elems && array_elems.size > 1
|
16
|
+
|
17
|
+
symbol_array = array_elems.all? { |e| e[0] == :symbol_literal }
|
18
|
+
|
19
|
+
if symbol_array
|
20
|
+
add_offence(:convention,
|
21
|
+
all_positions(s).first.lineno,
|
22
|
+
ERROR_MESSAGE)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Rubocop
|
4
|
+
module Cop
|
5
|
+
class TrivialAccessors < Cop
|
6
|
+
READER_MESSAGE = 'Use attr_reader to define trivial reader methods.'
|
7
|
+
WRITER_MESSAGE = 'Use attr_writer to define trivial writer methods.'
|
8
|
+
|
9
|
+
def inspect(file, source, token, sexp)
|
10
|
+
each(:def, sexp) do |def_block|
|
11
|
+
find_trivial_accessors def_block
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
# Parse the sexp given, corresponding to a def method.
|
18
|
+
# Looking for a trivial reader/writer pattern
|
19
|
+
def find_trivial_accessors(sexp)
|
20
|
+
lineno = sexp[1][2].lineno
|
21
|
+
accessor_var = sexp[1][1]
|
22
|
+
if trivial_reader?(sexp, accessor_var)
|
23
|
+
add_offence(:convention, lineno, READER_MESSAGE)
|
24
|
+
elsif trivial_writer?(sexp, accessor_var)
|
25
|
+
add_offence(:convention, lineno, WRITER_MESSAGE)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# body statements that exclude a trivial accessor
|
30
|
+
NON_TRIVIAL_BODYSTMT = [:void_stmt, :unary, :binary,
|
31
|
+
:@float, :@int, :hash, :begin,
|
32
|
+
:yield0, :zsuper, :array]
|
33
|
+
|
34
|
+
# looking for a trivial reader method
|
35
|
+
def trivial_reader?(sexp, accessor_var)
|
36
|
+
if reader_shape?(sexp)
|
37
|
+
accessor_body = sexp[3][1][0][1][1]
|
38
|
+
accessor_body.slice!(0) if accessor_body[0] == '@'
|
39
|
+
accessor_var == accessor_body
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# looking for a trivial writer method
|
44
|
+
def trivial_writer?(sexp, accessor_var)
|
45
|
+
if accessor_var[-1] == '=' &&
|
46
|
+
writer_shape?(sexp) &&
|
47
|
+
has_only_one_assignment?(sexp)
|
48
|
+
accessor_var.chop!
|
49
|
+
accessor_body = sexp[3][1][0][1][1][1]
|
50
|
+
accessor_body.slice!(0) if accessor_body[0] == '@'
|
51
|
+
unless sexp[3][1][0][0] == :vcall
|
52
|
+
body_purpose = sexp[3][1][0][2][0]
|
53
|
+
accessor_var == accessor_body && body_purpose == :var_ref
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# return true if the sexp is a reader accessor, without params
|
59
|
+
# or with empty braces
|
60
|
+
def reader_shape?(sexp)
|
61
|
+
accessor_shape?(sexp) &&
|
62
|
+
(sexp[2][0] == :params || empty_params?(sexp[2]))
|
63
|
+
end
|
64
|
+
|
65
|
+
# return true if the sexp is a writer accessor, with a param
|
66
|
+
# and with or without braces
|
67
|
+
def writer_shape?(sexp)
|
68
|
+
accessor_shape?(sexp) && with_braces?(sexp[2])
|
69
|
+
end
|
70
|
+
|
71
|
+
# return true if the sexp has the common shape of an accessor
|
72
|
+
def accessor_shape?(sexp)
|
73
|
+
[:@ident, :@const].include?(sexp[1][0]) &&
|
74
|
+
sexp[3][0] == :bodystmt &&
|
75
|
+
!NON_TRIVIAL_BODYSTMT.include?(sexp[3][1][0][0])
|
76
|
+
end
|
77
|
+
|
78
|
+
# detect "def foo() ..." or
|
79
|
+
# "[:paren, [:params, nil, nil, nil, nil, nil, nil, nil]]"
|
80
|
+
def empty_params?(sexp)
|
81
|
+
sexp[0] == :paren &&
|
82
|
+
sexp[1][0] == :params &&
|
83
|
+
sexp[1][1..-1].reject { |x| !x }.empty?
|
84
|
+
end
|
85
|
+
|
86
|
+
# detect "def name= name" or "def name=(name)
|
87
|
+
def with_braces?(sexp)
|
88
|
+
(sexp[0] == :paren && sexp[1][0] == :params) ||
|
89
|
+
sexp[0] == :params
|
90
|
+
end
|
91
|
+
|
92
|
+
# return true if the sexp has only one assignment in the body
|
93
|
+
# false otherwise (maybe one or more function calls).
|
94
|
+
# why [3..-1]? because:
|
95
|
+
# [:bodystmt, [[:assign, [:var_field, [:var_ref ...] and no :vcall
|
96
|
+
# thus [:bodystmt, :assign, :var_ref, nil, nil, nil ...]
|
97
|
+
def has_only_one_assignment?(sexp)
|
98
|
+
sexp[3][1][1] == nil
|
99
|
+
end
|
100
|
+
|
101
|
+
end # TrivialAccessors
|
102
|
+
end # Cop
|
103
|
+
end # Rubocop
|