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.

Files changed (46) hide show
  1. data/.rubocop.yml +89 -0
  2. data/Rakefile +4 -3
  3. data/VERSION +1 -1
  4. data/lib/rubocop.rb +3 -0
  5. data/lib/rubocop/cli.rb +45 -7
  6. data/lib/rubocop/cop/ampersands_pipes_vs_and_or.rb +25 -0
  7. data/lib/rubocop/cop/blocks.rb +39 -28
  8. data/lib/rubocop/cop/cop.rb +7 -12
  9. data/lib/rubocop/cop/def_parentheses.rb +37 -24
  10. data/lib/rubocop/cop/encoding.rb +4 -4
  11. data/lib/rubocop/cop/if_then_else.rb +28 -15
  12. data/lib/rubocop/cop/offence.rb +1 -1
  13. data/lib/rubocop/cop/space_after_comma_etc.rb +27 -10
  14. data/lib/rubocop/cop/surrounding_space.rb +102 -53
  15. data/lib/rubocop/cop/ternary_operator.rb +27 -10
  16. data/lib/rubocop/cop/unless_else.rb +19 -0
  17. data/lib/rubocop/cop/when_then.rb +25 -0
  18. data/lib/rubocop/report/emacs_style.rb +2 -1
  19. data/rubocop.gemspec +24 -7
  20. data/spec/rubocop/cli_spec.rb +71 -1
  21. data/spec/rubocop/cops/align_parameters_spec.rb +4 -4
  22. data/spec/rubocop/cops/ampersands_pipes_vs_and_or_spec.rb +57 -0
  23. data/spec/rubocop/cops/cop_spec.rb +2 -2
  24. data/spec/rubocop/cops/{def_parentheses_spec.rb → def_with_parentheses_spec.rb} +3 -18
  25. data/spec/rubocop/cops/def_without_parentheses_spec.rb +26 -0
  26. data/spec/rubocop/cops/encoding_spec.rb +41 -0
  27. data/spec/rubocop/cops/end_of_line_spec.rb +6 -0
  28. data/spec/rubocop/cops/if_with_semicolon_spec.rb +17 -0
  29. data/spec/rubocop/cops/indentation_spec.rb +2 -1
  30. data/spec/rubocop/cops/{blocks_spec.rb → multiline_blocks_spec.rb} +2 -13
  31. data/spec/rubocop/cops/multiline_if_then_spec.rb +56 -0
  32. data/spec/rubocop/cops/one_line_conditional_spec.rb +17 -0
  33. data/spec/rubocop/cops/single_line_blocks_spec.rb +22 -0
  34. data/spec/rubocop/cops/{space_after_comma_etc_spec.rb → space_after_colon_spec.rb} +2 -14
  35. data/spec/rubocop/cops/space_after_comma_spec.rb +17 -0
  36. data/spec/rubocop/cops/space_after_semicolon_spec.rb +17 -0
  37. data/spec/rubocop/cops/space_around_braces_spec.rb +32 -0
  38. data/spec/rubocop/cops/{surrounding_space_spec.rb → space_around_operators_spec.rb} +4 -73
  39. data/spec/rubocop/cops/space_inside_brackets_spec.rb +43 -0
  40. data/spec/rubocop/cops/space_inside_parens_spec.rb +27 -0
  41. data/spec/rubocop/cops/ternary_operator_spec.rb +26 -6
  42. data/spec/rubocop/cops/unless_else_spec.rb +29 -0
  43. data/spec/rubocop/cops/when_then_spec.rb +38 -0
  44. data/spec/spec_helper.rb +5 -0
  45. metadata +25 -8
  46. 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::Core::RakeTask.new(:rcov) do |spec|
35
- spec.pattern = 'spec/**/*_spec.rb'
36
- spec.rcov = true
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.2.1
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
- cop = cop_klass.new
42
- cop.correlations = correlations
43
- cop.inspect(file, source, tokens, sexp)
44
- total_offences += cop.offences.count
45
- report << cop if cop.has_report?
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
- print "\n#{target_files(args).count} files inspected, "
52
- puts "#{total_offences} offences detected"
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
@@ -2,47 +2,58 @@
2
2
 
3
3
  module Rubocop
4
4
  module Cop
5
- class Blocks < Cop
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
- tokens.each_with_index do |t, ix|
20
- case [t.type, t.text]
21
- when [:on_lbrace, '{']
22
- path = @correlations[ix] or next
23
- if path.last == :brace_block
24
- rbrace_ix = reverse_correlations[path.object_id] - [ix]
25
- if rbrace_ix.empty?
26
- fail "\n#@file:#{t.pos.lineno}:#{t.pos.column}: " +
27
- 'Matching brace not found'
28
- end
29
- if tokens[*rbrace_ix].pos.lineno > t.pos.lineno
30
- add_offence(:convention, t.pos.lineno, ERROR_MESSAGE[0])
31
- end
32
- end
33
- when [:on_kw, 'do']
34
- end_offset = tokens[ix..-1].index { |t2| t2.text == 'end' }
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 end not found'
33
+ 'Matching brace not found'
38
34
  end
39
- end_token_ix = ix + end_offset
40
- if tokens[end_token_ix].pos.lineno == t.pos.lineno
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
@@ -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(file, line_number, message)
68
- @offences << Offence.new(file, line_number, message)
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
- class DefParentheses < Cop
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) do |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
- case def_sexp[2][0]
15
- when :params
16
- if def_sexp[2] != EMPTY_PARAMS
17
- add_offence(:convention, pos.lineno, ERROR_MESSAGE[0])
18
- end
19
- when :paren
20
- if def_sexp[2][1] == EMPTY_PARAMS
21
- method_name_ix = tokens.index { |t| t.pos == pos }
22
- start = method_name_ix + 1
23
- rparen_ix = start + tokens[start..-1].index { |t| t.text == ')' }
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