rubocop 0.2.1 → 0.3.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 +89 -0
- data/Rakefile +4 -3
- data/VERSION +1 -1
- data/lib/rubocop.rb +3 -0
- data/lib/rubocop/cli.rb +45 -7
- data/lib/rubocop/cop/ampersands_pipes_vs_and_or.rb +25 -0
- data/lib/rubocop/cop/blocks.rb +39 -28
- data/lib/rubocop/cop/cop.rb +7 -12
- data/lib/rubocop/cop/def_parentheses.rb +37 -24
- data/lib/rubocop/cop/encoding.rb +4 -4
- data/lib/rubocop/cop/if_then_else.rb +28 -15
- data/lib/rubocop/cop/offence.rb +1 -1
- data/lib/rubocop/cop/space_after_comma_etc.rb +27 -10
- data/lib/rubocop/cop/surrounding_space.rb +102 -53
- data/lib/rubocop/cop/ternary_operator.rb +27 -10
- data/lib/rubocop/cop/unless_else.rb +19 -0
- data/lib/rubocop/cop/when_then.rb +25 -0
- data/lib/rubocop/report/emacs_style.rb +2 -1
- data/rubocop.gemspec +24 -7
- data/spec/rubocop/cli_spec.rb +71 -1
- data/spec/rubocop/cops/align_parameters_spec.rb +4 -4
- data/spec/rubocop/cops/ampersands_pipes_vs_and_or_spec.rb +57 -0
- data/spec/rubocop/cops/cop_spec.rb +2 -2
- data/spec/rubocop/cops/{def_parentheses_spec.rb → def_with_parentheses_spec.rb} +3 -18
- data/spec/rubocop/cops/def_without_parentheses_spec.rb +26 -0
- data/spec/rubocop/cops/encoding_spec.rb +41 -0
- data/spec/rubocop/cops/end_of_line_spec.rb +6 -0
- data/spec/rubocop/cops/if_with_semicolon_spec.rb +17 -0
- data/spec/rubocop/cops/indentation_spec.rb +2 -1
- data/spec/rubocop/cops/{blocks_spec.rb → multiline_blocks_spec.rb} +2 -13
- data/spec/rubocop/cops/multiline_if_then_spec.rb +56 -0
- data/spec/rubocop/cops/one_line_conditional_spec.rb +17 -0
- data/spec/rubocop/cops/single_line_blocks_spec.rb +22 -0
- data/spec/rubocop/cops/{space_after_comma_etc_spec.rb → space_after_colon_spec.rb} +2 -14
- data/spec/rubocop/cops/space_after_comma_spec.rb +17 -0
- data/spec/rubocop/cops/space_after_semicolon_spec.rb +17 -0
- data/spec/rubocop/cops/space_around_braces_spec.rb +32 -0
- data/spec/rubocop/cops/{surrounding_space_spec.rb → space_around_operators_spec.rb} +4 -73
- data/spec/rubocop/cops/space_inside_brackets_spec.rb +43 -0
- data/spec/rubocop/cops/space_inside_parens_spec.rb +27 -0
- data/spec/rubocop/cops/ternary_operator_spec.rb +26 -6
- data/spec/rubocop/cops/unless_else_spec.rb +29 -0
- data/spec/rubocop/cops/when_then_spec.rb +38 -0
- data/spec/spec_helper.rb +5 -0
- metadata +25 -8
- data/spec/rubocop/cops/if_then_else_spec.rb +0 -74
data/.rubocop.yml
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# This is the default configuration file with all checking enabled. It is also
|
2
|
+
# the configuration used to check the rubocop source code.
|
3
|
+
|
4
|
+
Encoding:
|
5
|
+
Enabled: true
|
6
|
+
|
7
|
+
LineLength:
|
8
|
+
Enabled: true
|
9
|
+
|
10
|
+
Tab:
|
11
|
+
Enabled: true
|
12
|
+
|
13
|
+
TrailingWhitespace:
|
14
|
+
Enabled: true
|
15
|
+
|
16
|
+
Indentation:
|
17
|
+
Enabled: true
|
18
|
+
|
19
|
+
EmptyLines:
|
20
|
+
Enabled: true
|
21
|
+
|
22
|
+
SpaceAroundOperators:
|
23
|
+
Enabled: true
|
24
|
+
|
25
|
+
SpaceAroundBraces:
|
26
|
+
Enabled: true
|
27
|
+
|
28
|
+
SpaceInsideParens:
|
29
|
+
Enabled: true
|
30
|
+
|
31
|
+
SpaceInsideBrackets:
|
32
|
+
Enabled: true
|
33
|
+
|
34
|
+
SpaceAfterComma:
|
35
|
+
Enabled: true
|
36
|
+
|
37
|
+
SpaceAfterSemicolon:
|
38
|
+
Enabled: true
|
39
|
+
|
40
|
+
SpaceAfterColon:
|
41
|
+
Enabled: true
|
42
|
+
|
43
|
+
HashSyntax:
|
44
|
+
Enabled: true
|
45
|
+
|
46
|
+
EndOfLine:
|
47
|
+
Enabled: true
|
48
|
+
|
49
|
+
NumericLiterals:
|
50
|
+
Enabled: true
|
51
|
+
|
52
|
+
AlignParameters:
|
53
|
+
Enabled: true
|
54
|
+
|
55
|
+
DefWithParentheses:
|
56
|
+
Enabled: true
|
57
|
+
|
58
|
+
DefWithoutParentheses:
|
59
|
+
Enabled: true
|
60
|
+
|
61
|
+
IfWithSemicolon:
|
62
|
+
Enabled: true
|
63
|
+
|
64
|
+
MultilineIfThen:
|
65
|
+
Enabled: true
|
66
|
+
|
67
|
+
OneLineConditional:
|
68
|
+
Enabled: true
|
69
|
+
|
70
|
+
MultilineBlocks:
|
71
|
+
Enabled: true
|
72
|
+
|
73
|
+
SingleLineBlocks:
|
74
|
+
Enabled: true
|
75
|
+
|
76
|
+
ParameterLists:
|
77
|
+
Enabled: true
|
78
|
+
|
79
|
+
StringLiterals:
|
80
|
+
Enabled: true
|
81
|
+
|
82
|
+
MultilineTernaryOperator:
|
83
|
+
Enabled: true
|
84
|
+
|
85
|
+
NestedTernaryOperator:
|
86
|
+
Enabled: true
|
87
|
+
|
88
|
+
UnlessElse:
|
89
|
+
Enabled: true
|
data/Rakefile
CHANGED
@@ -31,9 +31,10 @@ RSpec::Core::RakeTask.new(:spec) do |spec|
|
|
31
31
|
spec.pattern = FileList['spec/**/*_spec.rb']
|
32
32
|
end
|
33
33
|
|
34
|
-
RSpec
|
35
|
-
|
36
|
-
|
34
|
+
desc 'Run RSpec with code coverage'
|
35
|
+
task :coverage do
|
36
|
+
ENV['COVERAGE'] = 'true'
|
37
|
+
Rake::Task['spec'].execute
|
37
38
|
end
|
38
39
|
|
39
40
|
task :default => :spec
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/lib/rubocop.rb
CHANGED
@@ -22,6 +22,9 @@ require 'rubocop/cop/blocks'
|
|
22
22
|
require 'rubocop/cop/parameter_lists'
|
23
23
|
require 'rubocop/cop/string_literals'
|
24
24
|
require 'rubocop/cop/ternary_operator'
|
25
|
+
require 'rubocop/cop/unless_else'
|
26
|
+
require 'rubocop/cop/ampersands_pipes_vs_and_or'
|
27
|
+
require 'rubocop/cop/when_then'
|
25
28
|
|
26
29
|
require 'rubocop/report/report'
|
27
30
|
require 'rubocop/report/plain_text'
|
data/lib/rubocop/cli.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
require 'optparse'
|
4
|
+
require 'yaml'
|
4
5
|
|
5
6
|
module Rubocop
|
6
7
|
# The CLI is a class responsible of handling all the command line interface
|
@@ -22,11 +23,18 @@ module Rubocop
|
|
22
23
|
opts.on('-e', '--emacs', 'Emacs style output') do
|
23
24
|
$options[:mode] = :emacs_style
|
24
25
|
end
|
26
|
+
opts.on('-c FILE', '--config FILE', 'Configuration file') do |f|
|
27
|
+
$options[:config] = YAML.load_file(f)
|
28
|
+
end
|
29
|
+
opts.on('-s', '--silent', 'Silence summary') do |s|
|
30
|
+
$options[:silent] = s
|
31
|
+
end
|
25
32
|
end.parse!(args)
|
26
33
|
|
27
34
|
cops = Cop::Cop.all
|
28
35
|
show_cops_on_duty(cops) if $options[:verbose]
|
29
36
|
total_offences = 0
|
37
|
+
@configs = {}
|
30
38
|
|
31
39
|
target_files(args).each do |file|
|
32
40
|
report = Report.create(file, $options[:mode])
|
@@ -38,18 +46,24 @@ module Rubocop
|
|
38
46
|
tokens, sexp, correlations = CLI.rip_source(source)
|
39
47
|
|
40
48
|
cops.each do |cop_klass|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
49
|
+
config = $options[:config] || config_from_dotfile(File.dirname(file))
|
50
|
+
cop_config = config[cop_klass.name.split('::').last] if config
|
51
|
+
if cop_config.nil? || cop_config['Enabled']
|
52
|
+
cop = cop_klass.new
|
53
|
+
cop.correlations = correlations
|
54
|
+
cop.inspect(file, source, tokens, sexp)
|
55
|
+
total_offences += cop.offences.count
|
56
|
+
report << cop if cop.has_report?
|
57
|
+
end
|
46
58
|
end
|
47
59
|
|
48
60
|
report.display unless report.empty?
|
49
61
|
end
|
50
62
|
|
51
|
-
|
52
|
-
|
63
|
+
unless $options[:silent]
|
64
|
+
print "\n#{target_files(args).count} files inspected, "
|
65
|
+
puts "#{total_offences} offences detected"
|
66
|
+
end
|
53
67
|
|
54
68
|
return total_offences == 0 ? 0 : 1
|
55
69
|
end
|
@@ -70,6 +84,30 @@ module Rubocop
|
|
70
84
|
[tokens, sexp, correlations]
|
71
85
|
end
|
72
86
|
|
87
|
+
# Returns the configuration hash from .rubocop.yml searching
|
88
|
+
# upwards in the directory structure starting at the given
|
89
|
+
# directory where the inspected file is. If no .rubocop.yml is
|
90
|
+
# found there, the user's home directory is checked.
|
91
|
+
def config_from_dotfile(target_file_dir)
|
92
|
+
# @configs is a cache that maps directories to
|
93
|
+
# configurations. We search for .rubocop.yml only if we haven't
|
94
|
+
# already found it for the given directory.
|
95
|
+
unless @configs[target_file_dir]
|
96
|
+
dir = target_file_dir
|
97
|
+
while dir != '/'
|
98
|
+
path = File.join(dir, '.rubocop.yml')
|
99
|
+
if File.exist?(path)
|
100
|
+
@configs[target_file_dir] = YAML.load_file(path)
|
101
|
+
break
|
102
|
+
end
|
103
|
+
dir = File.expand_path('..', dir)
|
104
|
+
end
|
105
|
+
path = File.join(Dir.home, '.rubocop.yml')
|
106
|
+
@configs[target_file_dir] = YAML.load_file(path) if File.exist?(path)
|
107
|
+
end
|
108
|
+
@configs[target_file_dir]
|
109
|
+
end
|
110
|
+
|
73
111
|
def show_cops_on_duty(cops)
|
74
112
|
puts 'Reporting for duty:'
|
75
113
|
cops.each { |c| puts c }
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Rubocop
|
4
|
+
module Cop
|
5
|
+
class AmpersandsPipesVsAndOr < Cop
|
6
|
+
ERROR_MESSAGE =
|
7
|
+
'Use &&/|| for boolean expressions, and/or for control flow.'
|
8
|
+
|
9
|
+
def inspect(file, source, tokens, sexp)
|
10
|
+
[:if, :unless, :while, :until].each { |keyword| check(keyword, sexp) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def check(keyword, sexp)
|
14
|
+
each(keyword, sexp) do |sub_sexp|
|
15
|
+
condition = sub_sexp[1]
|
16
|
+
if condition[0] == :binary && [:and, :or].include?(condition[2])
|
17
|
+
add_offence(:convention,
|
18
|
+
sub_sexp.flatten.grep(Position).first.lineno,
|
19
|
+
ERROR_MESSAGE)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/rubocop/cop/blocks.rb
CHANGED
@@ -2,47 +2,58 @@
|
|
2
2
|
|
3
3
|
module Rubocop
|
4
4
|
module Cop
|
5
|
-
|
6
|
-
ERROR_MESSAGE = ['Avoid using {...} for multi-line blocks.',
|
7
|
-
'Prefer {...} over do...end for single-line blocks.']
|
8
|
-
|
5
|
+
module Blocks
|
9
6
|
def inspect(file, source, tokens, sexp)
|
10
7
|
@file = file
|
11
8
|
|
12
|
-
# The reverse_correlations maps grammar path object ids to
|
9
|
+
# The @reverse_correlations maps grammar path object ids to
|
13
10
|
# token indexes, so we can use it to find the corresponding }
|
14
11
|
# for each {.
|
15
|
-
reverse_correlations = Hash.new([])
|
12
|
+
@reverse_correlations = Hash.new([])
|
16
13
|
@correlations.each do |ix, path|
|
17
|
-
reverse_correlations[path.object_id] += [ix]
|
14
|
+
@reverse_correlations[path.object_id] += [ix]
|
18
15
|
end
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
unless end_offset
|
16
|
+
|
17
|
+
tokens.each_index { |ix| check(tokens, ix) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class MultilineBlocks < Cop
|
22
|
+
include Blocks
|
23
|
+
ERROR_MESSAGE = 'Avoid using {...} for multi-line blocks.'
|
24
|
+
|
25
|
+
def check(tokens, ix)
|
26
|
+
t = tokens[ix]
|
27
|
+
if [t.type, t.text] == [:on_lbrace, '{']
|
28
|
+
path = @correlations[ix] or return
|
29
|
+
if path.last == :brace_block
|
30
|
+
rbrace_ix = @reverse_correlations[path.object_id] - [ix]
|
31
|
+
if rbrace_ix.empty?
|
36
32
|
fail "\n#@file:#{t.pos.lineno}:#{t.pos.column}: " +
|
37
|
-
'Matching
|
33
|
+
'Matching brace not found'
|
38
34
|
end
|
39
|
-
|
40
|
-
|
41
|
-
add_offence(:convention, t.pos.lineno, ERROR_MESSAGE[1])
|
35
|
+
if tokens[*rbrace_ix].pos.lineno > t.pos.lineno
|
36
|
+
add_offence(:convention, t.pos.lineno, ERROR_MESSAGE)
|
42
37
|
end
|
43
38
|
end
|
44
39
|
end
|
45
40
|
end
|
46
41
|
end
|
42
|
+
|
43
|
+
class SingleLineBlocks < Cop
|
44
|
+
include Blocks
|
45
|
+
ERROR_MESSAGE = 'Prefer {...} over do...end for single-line blocks.'
|
46
|
+
|
47
|
+
def check(tokens, ix)
|
48
|
+
t = tokens[ix]
|
49
|
+
if [t.type, t.text] == [:on_kw, 'do']
|
50
|
+
end_offset = tokens[ix..-1].index { |t2| t2.text == 'end' } or return
|
51
|
+
end_token_ix = ix + end_offset
|
52
|
+
if tokens[end_token_ix].pos.lineno == t.pos.lineno
|
53
|
+
add_offence(:convention, t.pos.lineno, ERROR_MESSAGE)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
47
58
|
end
|
48
59
|
end
|
data/lib/rubocop/cop/cop.rb
CHANGED
@@ -35,12 +35,10 @@ module Rubocop
|
|
35
35
|
attr_writer :correlations
|
36
36
|
|
37
37
|
@all = []
|
38
|
-
@enabled = []
|
39
38
|
@config = {}
|
40
39
|
|
41
40
|
class << self
|
42
41
|
attr_accessor :all
|
43
|
-
attr_accessor :enabled
|
44
42
|
attr_accessor :config
|
45
43
|
end
|
46
44
|
|
@@ -48,14 +46,6 @@ module Rubocop
|
|
48
46
|
all << subclass
|
49
47
|
end
|
50
48
|
|
51
|
-
def self.enabled
|
52
|
-
all.select(&:enabled?)
|
53
|
-
end
|
54
|
-
|
55
|
-
def self.enabled?
|
56
|
-
true
|
57
|
-
end
|
58
|
-
|
59
49
|
def initialize
|
60
50
|
@offences = []
|
61
51
|
end
|
@@ -64,8 +54,8 @@ module Rubocop
|
|
64
54
|
!@offences.empty?
|
65
55
|
end
|
66
56
|
|
67
|
-
def add_offence(
|
68
|
-
@offences << Offence.new(
|
57
|
+
def add_offence(severity, line_number, message)
|
58
|
+
@offences << Offence.new(severity, line_number, message)
|
69
59
|
end
|
70
60
|
|
71
61
|
private
|
@@ -96,6 +86,11 @@ module Rubocop
|
|
96
86
|
def whitespace?(token)
|
97
87
|
[:on_sp, :on_ignored_nl, :on_nl].include?(token.type)
|
98
88
|
end
|
89
|
+
|
90
|
+
def all_positions(sexp)
|
91
|
+
return [sexp[2]] if sexp[0] =~ /^@/
|
92
|
+
sexp.grep(Array).inject([]) { |memo, s| memo + all_positions(s) }
|
93
|
+
end
|
99
94
|
end
|
100
95
|
end
|
101
96
|
end
|
@@ -2,37 +2,50 @@
|
|
2
2
|
|
3
3
|
module Rubocop
|
4
4
|
module Cop
|
5
|
-
|
6
|
-
ERROR_MESSAGE = ['Use def with parentheses when there are arguments.',
|
7
|
-
'Omit the parentheses in defs when the method ' +
|
8
|
-
"doesn't accept any arguments."]
|
5
|
+
module DefParentheses
|
9
6
|
EMPTY_PARAMS = [:params, nil, nil, nil, nil, nil]
|
10
7
|
|
11
8
|
def inspect(file, source, tokens, sexp)
|
12
|
-
each(:def, sexp)
|
9
|
+
each(:def, sexp) { |def_sexp| check(tokens, def_sexp) }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class DefWithParentheses < Cop
|
14
|
+
include DefParentheses
|
15
|
+
def error_message
|
16
|
+
"Omit the parentheses in defs when the method doesn't accept any " +
|
17
|
+
'arguments.'
|
18
|
+
end
|
19
|
+
|
20
|
+
def check(tokens, def_sexp)
|
21
|
+
if def_sexp[2][0] == :paren && def_sexp[2][1] == EMPTY_PARAMS
|
13
22
|
pos = def_sexp[1][-1]
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
first_body_token = tokens[(rparen_ix + 1)..-1].find do |t|
|
25
|
-
not whitespace?(t)
|
26
|
-
end
|
27
|
-
if first_body_token.pos.lineno > pos.lineno
|
28
|
-
# Only report offence if there's a line break after
|
29
|
-
# the empty parens.
|
30
|
-
add_offence(:convention, pos.lineno, ERROR_MESSAGE[1])
|
31
|
-
end
|
32
|
-
end
|
23
|
+
method_name_ix = tokens.index { |t| t.pos == pos }
|
24
|
+
start = method_name_ix + 1
|
25
|
+
rparen_ix = start + tokens[start..-1].index { |t| t.text == ')' }
|
26
|
+
first_body_token = tokens[(rparen_ix + 1)..-1].find do |t|
|
27
|
+
not whitespace?(t)
|
28
|
+
end
|
29
|
+
if first_body_token.pos.lineno > pos.lineno
|
30
|
+
# Only report offence if there's a line break after
|
31
|
+
# the empty parens.
|
32
|
+
add_offence(:convention, pos.lineno, error_message)
|
33
33
|
end
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
37
|
+
|
38
|
+
class DefWithoutParentheses < Cop
|
39
|
+
include DefParentheses
|
40
|
+
def error_message
|
41
|
+
'Use def with parentheses when there are arguments.'
|
42
|
+
end
|
43
|
+
|
44
|
+
def check(tokens, def_sexp)
|
45
|
+
if def_sexp[2][0] == :params && def_sexp[2] != EMPTY_PARAMS
|
46
|
+
add_offence(:convention, def_sexp[1][-1].lineno, error_message)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
37
50
|
end
|
38
51
|
end
|