rubocop 0.5.0 → 0.6.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.
- data/.rubocop.yml +59 -1
- data/CHANGELOG.md +38 -0
- data/Gemfile +1 -10
- data/README.md +28 -1
- data/Rakefile +1 -15
- data/lib/rubocop.rb +14 -0
- data/lib/rubocop/cli.rb +70 -7
- data/lib/rubocop/cop/alias.rb +5 -8
- data/lib/rubocop/cop/array_literal.rb +22 -0
- data/lib/rubocop/cop/ascii_identifiers_and_comments.rb +18 -0
- data/lib/rubocop/cop/avoid_perlisms.rb +19 -28
- data/lib/rubocop/cop/brace_after_percent.rb +28 -0
- data/lib/rubocop/cop/cop.rb +15 -3
- data/lib/rubocop/cop/encoding.rb +1 -1
- data/lib/rubocop/cop/ensure_return.rb +36 -0
- data/lib/rubocop/cop/favor_percent_r.rb +19 -0
- data/lib/rubocop/cop/favor_sprintf.rb +2 -10
- data/lib/rubocop/cop/grammar.rb +6 -3
- data/lib/rubocop/cop/handle_exceptions.rb +21 -0
- data/lib/rubocop/cop/hash_literal.rb +22 -0
- data/lib/rubocop/cop/method_length.rb +66 -0
- data/lib/rubocop/cop/op_method.rb +23 -0
- data/lib/rubocop/cop/percent_literals.rb +25 -0
- data/lib/rubocop/cop/percent_r.rb +19 -0
- data/lib/rubocop/cop/reduce_arguments.rb +67 -0
- data/lib/rubocop/cop/rescue_exception.rb +39 -0
- data/lib/rubocop/cop/rescue_modifier.rb +20 -0
- data/lib/rubocop/cop/symbol_snake_case.rb +5 -5
- data/lib/rubocop/cop/syntax.rb +13 -2
- data/lib/rubocop/version.rb +3 -1
- data/rubocop.gemspec +36 -169
- data/spec/rubocop/cli_spec.rb +146 -15
- data/spec/rubocop/cops/alias_spec.rb +10 -1
- data/spec/rubocop/cops/array_literal_spec.rb +29 -0
- data/spec/rubocop/cops/ascii_identifiers_and_comments_spec.rb +38 -0
- data/spec/rubocop/cops/avoid_perlisms_spec.rb +12 -0
- data/spec/rubocop/cops/brace_after_percent_spec.rb +27 -0
- data/spec/rubocop/cops/encoding_spec.rb +2 -2
- data/spec/rubocop/cops/ensure_return_spec.rb +37 -0
- data/spec/rubocop/cops/favor_percent_r.rb +29 -0
- data/spec/rubocop/cops/favor_sprintf_spec.rb +8 -1
- data/spec/rubocop/cops/grammar_spec.rb +54 -40
- data/spec/rubocop/cops/handle_exceptions_spec.rb +36 -0
- data/spec/rubocop/cops/hash_literal_spec.rb +29 -0
- data/spec/rubocop/cops/method_length_spec.rb +150 -0
- data/spec/rubocop/cops/op_method_spec.rb +58 -0
- data/spec/rubocop/cops/percent_literals_spec.rb +47 -0
- data/spec/rubocop/cops/percent_r_spec.rb +29 -0
- data/spec/rubocop/cops/reduce_arguments_spec.rb +57 -0
- data/spec/rubocop/cops/rescue_exception_spec.rb +73 -0
- data/spec/rubocop/cops/rescue_modifier.rb +40 -0
- data/spec/rubocop/cops/space_around_operators_spec.rb +7 -0
- data/spec/rubocop/cops/symbol_snake_case_spec.rb +19 -7
- data/spec/rubocop/cops/tab_spec.rb +1 -1
- metadata +131 -50
- data/Gemfile.lock +0 -41
- data/VERSION +0 -1
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Rubocop
|
4
|
+
module Cop
|
5
|
+
class BraceAfterPercent < Cop
|
6
|
+
ERROR_MESSAGE = 'Prefer () as delimiters for all % literals.'
|
7
|
+
LITERALS = {
|
8
|
+
on_tstring_beg: '%q',
|
9
|
+
on_words_beg: '%W',
|
10
|
+
on_qwords_beg: '%w',
|
11
|
+
on_regexp_beg: '%r',
|
12
|
+
on_symbeg: '%s',
|
13
|
+
on_backtick: '%x'
|
14
|
+
}
|
15
|
+
|
16
|
+
def inspect(file, source, tokens, sexp)
|
17
|
+
tokens.each_index do |ix|
|
18
|
+
t = tokens[ix]
|
19
|
+
token = LITERALS[t.type]
|
20
|
+
if token && t.text.downcase.start_with?(token) && t.text[2] != '('
|
21
|
+
add_offence(:convention, t.pos.lineno,
|
22
|
+
ERROR_MESSAGE)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/rubocop/cop/cop.rb
CHANGED
@@ -32,7 +32,7 @@ module Rubocop
|
|
32
32
|
|
33
33
|
class Cop
|
34
34
|
attr_accessor :offences
|
35
|
-
attr_writer :correlations
|
35
|
+
attr_writer :correlations, :disabled_lines
|
36
36
|
|
37
37
|
@all = []
|
38
38
|
@config = {}
|
@@ -55,7 +55,9 @@ module Rubocop
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def add_offence(severity, line_number, message)
|
58
|
-
@
|
58
|
+
unless @disabled_lines && @disabled_lines.include?(line_number)
|
59
|
+
@offences << Offence.new(severity, line_number, message)
|
60
|
+
end
|
59
61
|
end
|
60
62
|
|
61
63
|
private
|
@@ -83,13 +85,23 @@ module Rubocop
|
|
83
85
|
end
|
84
86
|
end
|
85
87
|
|
88
|
+
def find_all(sym, sexp)
|
89
|
+
result = []
|
90
|
+
each(sym, sexp) { |s| result << s }
|
91
|
+
result
|
92
|
+
end
|
93
|
+
|
94
|
+
def find_first(sym, sexp)
|
95
|
+
find_all(sym, sexp).first
|
96
|
+
end
|
97
|
+
|
86
98
|
def whitespace?(token)
|
87
99
|
[:on_sp, :on_ignored_nl, :on_nl].include?(token.type)
|
88
100
|
end
|
89
101
|
|
90
102
|
def all_positions(sexp)
|
91
103
|
return [sexp[2]] if sexp[0] =~ /^@/
|
92
|
-
sexp.grep(Array).reduce([]) { |
|
104
|
+
sexp.grep(Array).reduce([]) { |a, e| a + all_positions(e) }
|
93
105
|
end
|
94
106
|
end
|
95
107
|
end
|
data/lib/rubocop/cop/encoding.rb
CHANGED
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Rubocop
|
4
|
+
module Cop
|
5
|
+
class EnsureReturn < Cop
|
6
|
+
ERROR_MESSAGE = 'Never return from an ensure block.'
|
7
|
+
|
8
|
+
def inspect(file, source, tokens, sexp)
|
9
|
+
in_ensure = false
|
10
|
+
ensure_col = nil
|
11
|
+
|
12
|
+
tokens.each_index do |ix|
|
13
|
+
t = tokens[ix]
|
14
|
+
if ensure_start?(t)
|
15
|
+
in_ensure = true
|
16
|
+
ensure_col = t.pos.column
|
17
|
+
elsif ensure_end?(t, ensure_col)
|
18
|
+
in_ensure = false
|
19
|
+
elsif in_ensure && t.type == :on_kw && t.text == 'return'
|
20
|
+
add_offence(:warning, t.pos.lineno, ERROR_MESSAGE)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def ensure_start?(t)
|
28
|
+
t.type == :on_kw && t.text == 'ensure'
|
29
|
+
end
|
30
|
+
|
31
|
+
def ensure_end?(t, column)
|
32
|
+
t.type == :on_kw && t.text == 'end' && t.pos.column == column
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Rubocop
|
4
|
+
module Cop
|
5
|
+
class FavorPercentR < Cop
|
6
|
+
ERROR_MESSAGE = 'Use %r for regular expressions matching more ' +
|
7
|
+
"than one '/' character."
|
8
|
+
|
9
|
+
def inspect(file, source, tokens, sexp)
|
10
|
+
tokens.each_cons(2) do |t1, t2|
|
11
|
+
if t1.type == :on_regexp_beg && t1.text == '/' &&
|
12
|
+
t2.text.scan(/\//).size > 1
|
13
|
+
add_offence(:convention, t1.pos.lineno, ERROR_MESSAGE)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -14,16 +14,8 @@ module Rubocop
|
|
14
14
|
# we care only about the % operator
|
15
15
|
next unless matching?(operator, op1, op2)
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
if op1[0] == :string_literal
|
20
|
-
lineno_struct = s[1][1][1][2]
|
21
|
-
lineno = lineno_struct.lineno if lineno_struct.respond_to?(:lineno)
|
22
|
-
else
|
23
|
-
# or some identifier
|
24
|
-
lineno_struct = s[1][1][2]
|
25
|
-
lineno = lineno_struct.lineno if lineno_struct.respond_to?(:lineno)
|
26
|
-
end
|
17
|
+
pos = all_positions(s[1]).first
|
18
|
+
lineno = pos.lineno if pos
|
27
19
|
|
28
20
|
add_offence(
|
29
21
|
:convention,
|
data/lib/rubocop/cop/grammar.rb
CHANGED
@@ -16,8 +16,9 @@ module Rubocop
|
|
16
16
|
token_positions = tokens.map { |t| [t.pos.lineno, t.pos.column] }
|
17
17
|
@index_by_pos = Hash[*token_positions.each_with_index.to_a.flatten(1)]
|
18
18
|
@special = {
|
19
|
-
assign: [:on_op, '='],
|
20
|
-
brace_block: [:on_lbrace, '{']
|
19
|
+
assign: [[:on_op, '=']],
|
20
|
+
brace_block: [[:on_lbrace, '{']],
|
21
|
+
ifop: [[:on_op, '?'], [:on_op, ':']]
|
21
22
|
}
|
22
23
|
end
|
23
24
|
|
@@ -84,7 +85,9 @@ module Rubocop
|
|
84
85
|
# Here we don't advance @ix because there may be other
|
85
86
|
# tokens inbetween the current one and the one we get from
|
86
87
|
# @special.
|
87
|
-
|
88
|
+
@special[sexp[0]].each do |token_to_find|
|
89
|
+
find(path, sexp, token_to_find)
|
90
|
+
end
|
88
91
|
when :block_var # "{ |...|" or "do |...|"
|
89
92
|
@ix = find(path, sexp, [:on_op, '|']) + 1
|
90
93
|
find(path, sexp, [:on_op, '|'])
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Rubocop
|
4
|
+
module Cop
|
5
|
+
class HandleExceptions < Cop
|
6
|
+
ERROR_MESSAGE = 'Do not suppress exceptions.'
|
7
|
+
|
8
|
+
def inspect(file, source, tokens, sexp)
|
9
|
+
each(:begin, sexp) do |s|
|
10
|
+
each(:rescue, s) do |rs|
|
11
|
+
if rs[3] == [[:void_stmt]]
|
12
|
+
add_offence(:warning,
|
13
|
+
all_positions(s)[-1].lineno + 1,
|
14
|
+
ERROR_MESSAGE)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Rubocop
|
4
|
+
module Cop
|
5
|
+
class HashLiteral < Cop
|
6
|
+
ERROR_MESSAGE = 'Use hash literal {} instead of Hash.new.'
|
7
|
+
|
8
|
+
def inspect(file, source, tokens, sexp)
|
9
|
+
each(:method_add_arg, sexp) do |s|
|
10
|
+
potential_class = s[1][1][1]
|
11
|
+
|
12
|
+
if potential_class[1] == 'Hash' && s[1][3][1] == 'new' &&
|
13
|
+
s[2] == [:arg_paren, nil]
|
14
|
+
add_offence(:convention,
|
15
|
+
potential_class[2].lineno,
|
16
|
+
ERROR_MESSAGE)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Rubocop
|
4
|
+
module Cop
|
5
|
+
class MethodLength < Cop
|
6
|
+
ERROR_MESSAGE = 'Method has too many lines. [%d/%d]'
|
7
|
+
|
8
|
+
def inspect(file, source, tokens, sexp)
|
9
|
+
def_token_indices(tokens, source).each do |t_ix|
|
10
|
+
def_lineno, end_lineno = def_and_end_lines(tokens, t_ix)
|
11
|
+
length = calculate_length(def_lineno, end_lineno, source)
|
12
|
+
|
13
|
+
if length > MethodLength.max
|
14
|
+
message = sprintf(ERROR_MESSAGE, length, MethodLength.max)
|
15
|
+
add_offence(:convention, def_lineno, message)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
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
|
+
private
|
33
|
+
|
34
|
+
def calculate_length(def_lineno, end_lineno, source)
|
35
|
+
lines = source[def_lineno..(end_lineno - 2)].reject(&:empty?)
|
36
|
+
unless MethodLength.count_comments?
|
37
|
+
lines = lines.reject { |line| line =~ /^\s*#/ }
|
38
|
+
end
|
39
|
+
lines.size
|
40
|
+
end
|
41
|
+
|
42
|
+
def def_token_indices(tokens, source)
|
43
|
+
tokens.each_index.select do |ix|
|
44
|
+
t = tokens[ix]
|
45
|
+
|
46
|
+
# Need to check:
|
47
|
+
# 1. if the previous character is a ':' to prevent matching ':def'
|
48
|
+
# 2. if the method is a one line, which we will ignore
|
49
|
+
[t.type, t.text] == [:on_kw, 'def'] &&
|
50
|
+
source[t.pos.lineno - 1][t.pos.column - 1] != ':' &&
|
51
|
+
source[t.pos.lineno - 1] !~ /^\s*def.*(?:\(.*\)|;).*end\s*$/
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Find the matching 'end' based on the indentation of 'def'
|
56
|
+
# Fall back to last token if indentation cannot be matched
|
57
|
+
def def_and_end_lines(tokens, t_ix)
|
58
|
+
t1 = tokens[t_ix]
|
59
|
+
t2 = tokens[(t_ix + 1)..-1].find(-> { tokens[-1] }) do |t|
|
60
|
+
[t1.pos.column, t.type, t.text] == [t.pos.column, :on_kw, 'end']
|
61
|
+
end
|
62
|
+
[t1.pos.lineno, t2.pos.lineno]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Rubocop
|
4
|
+
module Cop
|
5
|
+
class OpMethod < Cop
|
6
|
+
ERROR_MESSAGE = 'When defining the %s operator, name its argument other.'
|
7
|
+
|
8
|
+
def inspect(file, source, tokens, sexp)
|
9
|
+
each(:def, sexp) do |s|
|
10
|
+
if s[1][0] == :@op && !%w([] []= <<).include?(s[1][1])
|
11
|
+
param = s[2][1][1][0]
|
12
|
+
|
13
|
+
unless param[1] == 'other'
|
14
|
+
add_offence(:convention,
|
15
|
+
param[2].lineno,
|
16
|
+
sprintf(ERROR_MESSAGE, s[1][1]))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Rubocop
|
4
|
+
module Cop
|
5
|
+
class PercentLiterals < Cop
|
6
|
+
ERROR_MESSAGE = 'The use of %s is discouraged.'
|
7
|
+
BAD_LITERALS = {
|
8
|
+
on_tstring_beg: '%q',
|
9
|
+
on_symbeg: '%s',
|
10
|
+
on_backtick: '%x'
|
11
|
+
}
|
12
|
+
|
13
|
+
def inspect(file, source, tokens, sexp)
|
14
|
+
tokens.each_index do |ix|
|
15
|
+
t = tokens[ix]
|
16
|
+
bad_token = BAD_LITERALS[t.type]
|
17
|
+
if bad_token && t.text.downcase.start_with?(bad_token)
|
18
|
+
add_offence(:convention, t.pos.lineno,
|
19
|
+
sprintf(ERROR_MESSAGE, t.text[0, 2]))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Rubocop
|
4
|
+
module Cop
|
5
|
+
class PercentR < Cop
|
6
|
+
ERROR_MESSAGE = 'Use %r only for regular expressions matching more ' +
|
7
|
+
"than one '/' character."
|
8
|
+
|
9
|
+
def inspect(file, source, tokens, sexp)
|
10
|
+
tokens.each_cons(2) do |t1, t2|
|
11
|
+
if t1.type == :on_regexp_beg && t1.text =~ /^%r/ &&
|
12
|
+
t2.text !~ %r(/.*/)
|
13
|
+
add_offence(:convention, t1.pos.lineno, ERROR_MESSAGE)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Rubocop
|
4
|
+
module Cop
|
5
|
+
class ReduceArguments < Cop
|
6
|
+
ERROR_MESSAGE = 'Name reduce arguments |a, e| (accumulator, element)'
|
7
|
+
|
8
|
+
def inspect(file, source, tokens, sexp)
|
9
|
+
find_reduce_blocks(tokens).each do |reduce_block|
|
10
|
+
l_ix, r_ix = reduce_block
|
11
|
+
unless arguments_named_properly?(l_ix, r_ix, tokens)
|
12
|
+
add_offence(:convention, tokens[l_ix].pos.lineno, ERROR_MESSAGE)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def arguments_named_properly?(l_ix, r_ix, tokens)
|
20
|
+
a, e = false, false
|
21
|
+
tokens[l_ix..r_ix].each do |t|
|
22
|
+
if a == true && [t.type, t.text] == [:on_ident, 'e']
|
23
|
+
e = true
|
24
|
+
elsif [t.type, t.text] == [:on_ident, 'a']
|
25
|
+
a = true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
a && e
|
29
|
+
end
|
30
|
+
|
31
|
+
def find_reduce_blocks(tokens)
|
32
|
+
blocks = []
|
33
|
+
tokens.each_with_index do |t, ix|
|
34
|
+
if [t.type, t.text] == [:on_ident, 'reduce']
|
35
|
+
# Make sure we didn't select a :reduce symbol
|
36
|
+
next if tokens[ix - 1].text == ':'
|
37
|
+
|
38
|
+
block = find_brace_block(tokens, ix)
|
39
|
+
blocks << block if block
|
40
|
+
end
|
41
|
+
end
|
42
|
+
blocks
|
43
|
+
end
|
44
|
+
|
45
|
+
def find_brace_block(tokens, reduce_ix)
|
46
|
+
stack = []
|
47
|
+
block = false
|
48
|
+
|
49
|
+
# When we find the braces we need to add reduce_ix in order to
|
50
|
+
# find the real tokens index, since we're looping through a subset
|
51
|
+
tokens[reduce_ix..-1].each_with_index do |t, ix|
|
52
|
+
break if t.pos.lineno != tokens[reduce_ix].pos.lineno
|
53
|
+
if [:on_lbrace, :on_tlambeg].include? t.type
|
54
|
+
stack.push ix + reduce_ix
|
55
|
+
elsif t.type == :on_rbrace
|
56
|
+
left_ix = stack.pop
|
57
|
+
ix += reduce_ix
|
58
|
+
block = [left_ix, ix] if (stack.empty? &&
|
59
|
+
tokens[left_ix].type != :on_tlambeg)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
block
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Rubocop
|
4
|
+
module Cop
|
5
|
+
class RescueException < Cop
|
6
|
+
ERROR_MESSAGE = 'Avoid rescuing the Exception class.'
|
7
|
+
|
8
|
+
def inspect(file, source, tokens, sexp)
|
9
|
+
each(:rescue, sexp) do |s|
|
10
|
+
# TODO Improve handling of rescue One, Two => e
|
11
|
+
if valid_case?(s)
|
12
|
+
target_class = s[1][0][1][1]
|
13
|
+
|
14
|
+
lineno = s[1][0][1][2].lineno
|
15
|
+
|
16
|
+
add_offence(:warning,
|
17
|
+
lineno,
|
18
|
+
ERROR_MESSAGE) if target_class == 'Exception'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def valid_case?(s)
|
24
|
+
if s[1].nil?
|
25
|
+
# rescue with no args
|
26
|
+
false
|
27
|
+
elsif s[1][0] == :mrhs_new_from_args
|
28
|
+
# rescue One, Two => e
|
29
|
+
false
|
30
|
+
elsif s[1][0][0] == :const_path_ref
|
31
|
+
# rescue Module::Class
|
32
|
+
false
|
33
|
+
else
|
34
|
+
true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|