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.

Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -266
  3. data/CHANGELOG.md +49 -7
  4. data/README.md +75 -2
  5. data/Rakefile +2 -2
  6. data/bin/rubocop +15 -10
  7. data/lib/rubocop.rb +19 -1
  8. data/lib/rubocop/cli.rb +113 -116
  9. data/lib/rubocop/config.rb +202 -0
  10. data/lib/rubocop/config_store.rb +37 -0
  11. data/lib/rubocop/cop/alias.rb +2 -5
  12. data/lib/rubocop/cop/align_parameters.rb +1 -1
  13. data/lib/rubocop/cop/array_literal.rb +43 -4
  14. data/lib/rubocop/cop/avoid_for.rb +2 -4
  15. data/lib/rubocop/cop/avoid_global_vars.rb +49 -0
  16. data/lib/rubocop/cop/block_comments.rb +17 -0
  17. data/lib/rubocop/cop/brace_after_percent.rb +9 -5
  18. data/lib/rubocop/cop/{indentation.rb → case_indentation.rb} +1 -1
  19. data/lib/rubocop/cop/class_methods.rb +20 -0
  20. data/lib/rubocop/cop/colon_method_call.rb +44 -0
  21. data/lib/rubocop/cop/cop.rb +30 -2
  22. data/lib/rubocop/cop/def_parentheses.rb +1 -1
  23. data/lib/rubocop/cop/empty_line_between_defs.rb +26 -0
  24. data/lib/rubocop/cop/empty_lines.rb +10 -13
  25. data/lib/rubocop/cop/eval.rb +22 -0
  26. data/lib/rubocop/cop/favor_join.rb +37 -0
  27. data/lib/rubocop/cop/grammar.rb +2 -2
  28. data/lib/rubocop/cop/hash_literal.rb +43 -4
  29. data/lib/rubocop/cop/hash_syntax.rb +2 -2
  30. data/lib/rubocop/cop/if_then_else.rb +1 -1
  31. data/lib/rubocop/cop/leading_comment_space.rb +20 -0
  32. data/lib/rubocop/cop/line_continuation.rb +18 -0
  33. data/lib/rubocop/cop/line_length.rb +1 -1
  34. data/lib/rubocop/cop/method_and_variable_snake_case.rb +7 -6
  35. data/lib/rubocop/cop/method_length.rb +4 -15
  36. data/lib/rubocop/cop/not.rb +15 -0
  37. data/lib/rubocop/cop/offence.rb +9 -0
  38. data/lib/rubocop/cop/semicolon.rb +74 -3
  39. data/lib/rubocop/cop/single_line_methods.rb +60 -0
  40. data/lib/rubocop/cop/space_after_control_keyword.rb +28 -0
  41. data/lib/rubocop/cop/surrounding_space.rb +48 -9
  42. data/lib/rubocop/cop/symbol_array.rb +29 -0
  43. data/lib/rubocop/cop/trivial_accessors.rb +103 -0
  44. data/lib/rubocop/cop/unless_else.rb +1 -1
  45. data/lib/rubocop/cop/variable_interpolation.rb +3 -2
  46. data/lib/rubocop/cop/word_array.rb +38 -0
  47. data/lib/rubocop/version.rb +1 -1
  48. data/rubocop.gemspec +11 -7
  49. data/spec/project_spec.rb +27 -0
  50. data/spec/rubocop/cli_spec.rb +549 -487
  51. data/spec/rubocop/config_spec.rb +399 -0
  52. data/spec/rubocop/config_store_spec.rb +66 -0
  53. data/spec/rubocop/cops/alias_spec.rb +7 -0
  54. data/spec/rubocop/cops/array_literal_spec.rb +8 -1
  55. data/spec/rubocop/cops/avoid_for_spec.rb +15 -1
  56. data/spec/rubocop/cops/avoid_global_vars.rb +32 -0
  57. data/spec/rubocop/cops/block_comments_spec.rb +29 -0
  58. data/spec/rubocop/cops/brace_after_percent_spec.rb +19 -13
  59. data/spec/rubocop/cops/{indentation_spec.rb → case_indentation_spec.rb} +2 -2
  60. data/spec/rubocop/cops/class_methods_spec.rb +49 -0
  61. data/spec/rubocop/cops/colon_method_call_spec.rb +47 -0
  62. data/spec/rubocop/cops/empty_line_between_defs_spec.rb +83 -0
  63. data/spec/rubocop/cops/empty_lines_spec.rb +6 -63
  64. data/spec/rubocop/cops/eval_spec.rb +36 -0
  65. data/spec/rubocop/cops/favor_join_spec.rb +39 -0
  66. data/spec/rubocop/cops/hash_literal_spec.rb +8 -1
  67. data/spec/rubocop/cops/leading_comment_space_spec.rb +60 -0
  68. data/spec/rubocop/cops/line_continuation_spec.rb +24 -0
  69. data/spec/rubocop/cops/line_length_spec.rb +1 -0
  70. data/spec/rubocop/cops/method_and_variable_snake_case_spec.rb +20 -0
  71. data/spec/rubocop/cops/method_length_spec.rb +2 -5
  72. data/spec/rubocop/cops/new_lambda_literal_spec.rb +2 -3
  73. data/spec/rubocop/cops/not_spec.rb +34 -0
  74. data/spec/rubocop/cops/offence_spec.rb +7 -0
  75. data/spec/rubocop/cops/semicolon_spec.rb +79 -4
  76. data/spec/rubocop/cops/single_line_methods_spec.rb +50 -0
  77. data/spec/rubocop/cops/space_after_control_keyword_spec.rb +28 -0
  78. data/spec/rubocop/cops/space_around_equals_in_default_parameter_spec.rb +11 -1
  79. data/spec/rubocop/cops/space_inside_hash_literal_braces_spec.rb +74 -0
  80. data/spec/rubocop/cops/symbol_array_spec.rb +25 -0
  81. data/spec/rubocop/cops/trivial_accessors_spec.rb +332 -0
  82. data/spec/rubocop/cops/variable_interpolation_spec.rb +10 -1
  83. data/spec/rubocop/cops/word_array_spec.rb +39 -0
  84. data/spec/spec_helper.rb +16 -9
  85. data/spec/support/file_helper.rb +21 -0
  86. data/spec/support/isolated_environment.rb +27 -0
  87. metadata +66 -6
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+
3
+ module Rubocop
4
+ module Cop
5
+ class ClassMethods < Cop
6
+ ERROR_MESSAGE = 'Prefer self over class/module for class/module methods.'
7
+
8
+ def inspect(file, source, tokens, sexp)
9
+ # defs nodes correspond to class & module methods
10
+ each(:defs, sexp) do |s|
11
+ if s[1][0] == :var_ref && s[1][1][0] == :@const
12
+ add_offence(:convention,
13
+ s[1][1][2].lineno,
14
+ ERROR_MESSAGE)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+
3
+ module Rubocop
4
+ module Cop
5
+ class ColonMethodCall < Cop
6
+ ERROR_MESSAGE = 'Do not use :: for method invocation.'
7
+
8
+ def inspect(file, source, tokens, sexp)
9
+ state = :outside
10
+ tokens.each do |t|
11
+ state = case [state, t.type]
12
+ when [:outside, :on_const]
13
+ :const
14
+
15
+ when [:outside, :on_ident]
16
+ :ident
17
+
18
+ when [:const, :on_op]
19
+ t.text == '::' ? :const_colon : :outside
20
+
21
+ when [:ident, :on_op]
22
+ t.text == '::' ? :ident_colon : :outside
23
+
24
+ when [:ident_colon, :on_ident]
25
+ add_offence(:convention, t.pos.lineno, ERROR_MESSAGE)
26
+ :ident
27
+
28
+ when [:const_colon, :on_ident]
29
+ add_offence(:convention, t.pos.lineno, ERROR_MESSAGE)
30
+ :ident
31
+
32
+ when [:ident_colon, :on_const]
33
+ :const
34
+
35
+ when [:const_colon, :on_const]
36
+ :const
37
+ else
38
+ :outside
39
+ end || state
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -32,6 +32,7 @@ module Rubocop
32
32
 
33
33
  class Cop
34
34
  attr_accessor :offences
35
+ attr_accessor :debug
35
36
  attr_writer :correlations, :disabled_lines
36
37
 
37
38
  @all = []
@@ -46,8 +47,13 @@ module Rubocop
46
47
  all << subclass
47
48
  end
48
49
 
50
+ def self.cop_name
51
+ name.to_s.split('::').last
52
+ end
53
+
49
54
  def initialize
50
55
  @offences = []
56
+ @debug = false
51
57
  end
52
58
 
53
59
  def has_report?
@@ -56,13 +62,13 @@ module Rubocop
56
62
 
57
63
  def add_offence(severity, line_number, message)
58
64
  unless @disabled_lines && @disabled_lines.include?(line_number)
59
- message = $options[:debug] ? "#{name}: #{message}" : message
65
+ message = debug ? "#{name}: #{message}" : message
60
66
  @offences << Offence.new(severity, line_number, message)
61
67
  end
62
68
  end
63
69
 
64
70
  def name
65
- self.class.to_s.split('::')[-1]
71
+ self.class.cop_name
66
72
  end
67
73
 
68
74
  private
@@ -108,6 +114,28 @@ module Rubocop
108
114
  return [sexp[2]] if sexp[0] =~ /^@/
109
115
  sexp.grep(Array).reduce([]) { |a, e| a + all_positions(e) }
110
116
  end
117
+
118
+ def keywords(tokens)
119
+ # we need to keep track of the previous token to avoid
120
+ # interpreting :some_keyword as the keyword some_keyword
121
+ prev = Token.new(0, :init, '')
122
+ # same goes for defs so we need to track those as well
123
+ keywords = []
124
+
125
+ tokens.each do |t|
126
+ keywords << t if prev.type != :on_symbeg && t.type == :on_kw
127
+ # def with name that's a kw confuses Ripper.lex
128
+ penultimate = keywords[-2]
129
+ keywords.pop if penultimate && penultimate.text == 'def'
130
+ prev = t
131
+ end
132
+
133
+ keywords
134
+ end
135
+
136
+ def each_keyword(keyword, tokens)
137
+ keywords(tokens).select { |t| t.text == keyword }.each { |t| yield t }
138
+ end
111
139
  end
112
140
  end
113
141
  end
@@ -27,7 +27,7 @@ module Rubocop
27
27
  start = method_name_ix + 1
28
28
  rparen_ix = start + tokens[start..-1].index { |t| t.text == ')' }
29
29
  first_body_token = tokens[(rparen_ix + 1)..-1].find do |t|
30
- not whitespace?(t)
30
+ !whitespace?(t)
31
31
  end
32
32
  if first_body_token.pos.lineno > pos.lineno
33
33
  # Only report offence if there's a line break after
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ module Rubocop
4
+ module Cop
5
+ class EmptyLineBetweenDefs < Cop
6
+ ERROR_MESSAGE = 'Use empty lines between defs.'
7
+
8
+ def inspect(file, source, tokens, sexp)
9
+ each_parent_of(:def, sexp) do |parent|
10
+ defs = parent.select { |child| child[0] == :def }
11
+ identifier_of_first_def = defs[0][1]
12
+ current_row_ix = identifier_of_first_def[-1].lineno - 1
13
+ # The first def doesn't need to have an empty line above it,
14
+ # so we iterate starting at index 1.
15
+ defs[1..-1].each do |child|
16
+ next_row_ix = child[1][-1].lineno - 1
17
+ if source[current_row_ix..next_row_ix].grep(/^[ \t]*$/).empty?
18
+ add_offence(:convention, next_row_ix + 1, ERROR_MESSAGE)
19
+ end
20
+ current_row_ix = next_row_ix
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -3,22 +3,19 @@
3
3
  module Rubocop
4
4
  module Cop
5
5
  class EmptyLines < Cop
6
- ERROR_MESSAGE = 'Use empty lines between defs.'
6
+ ERROR_MESSAGE = 'Extra blank line detected.'
7
7
 
8
8
  def inspect(file, source, tokens, sexp)
9
- each_parent_of(:def, sexp) do |parent|
10
- defs = parent.select { |child| child[0] == :def }
11
- identifier_of_first_def = defs[0][1]
12
- current_row_ix = identifier_of_first_def[-1].lineno - 1
13
- # The first def doesn't need to have an empty line above it,
14
- # so we iterate starting at index 1.
15
- defs[1..-1].each do |child|
16
- next_row_ix = child[1][-1].lineno - 1
17
- if source[current_row_ix..next_row_ix].grep(/^[ \t]*$/).empty?
18
- add_offence(:convention, next_row_ix + 1, ERROR_MESSAGE)
19
- end
20
- current_row_ix = next_row_ix
9
+ previous_token = Token.new(0, :init, '')
10
+
11
+ tokens.each do |t|
12
+ if t.type == :on_ignored_nl && previous_token.type == :on_ignored_nl
13
+ add_offence(:convention,
14
+ t.pos.lineno,
15
+ ERROR_MESSAGE)
21
16
  end
17
+
18
+ previous_token = t
22
19
  end
23
20
  end
24
21
  end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ module Rubocop
4
+ module Cop
5
+ class Eval < Cop
6
+ ERROR_MESSAGE = 'The use of eval is a serious security risk.'
7
+
8
+ def inspect(file, source, tokens, sexp)
9
+ each(:command, sexp) { |s| process_ident(s[1]) }
10
+ each(:fcall, sexp) { |s| process_ident(s[1]) }
11
+ end
12
+
13
+ def process_ident(sexp)
14
+ if sexp[0] == :@ident && sexp[1] == 'eval'
15
+ add_offence(:security,
16
+ sexp[2].lineno,
17
+ ERROR_MESSAGE)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+
3
+ module Rubocop
4
+ module Cop
5
+ class FavorJoin < Cop
6
+ ERROR_MESSAGE = 'Favor Array#join over Array#*.'
7
+
8
+ def inspect(file, source, tokens, sexp)
9
+ each(:binary, sexp) do |s|
10
+ op1 = s[1]
11
+ operator = s[2]
12
+ op2 = s[3]
13
+
14
+ # we care only about the * operator
15
+ next unless matching?(operator, op1, op2)
16
+
17
+ pos = all_positions(s[1]).first
18
+ lineno = pos.lineno if pos
19
+
20
+ add_offence(
21
+ :convention,
22
+ lineno,
23
+ ERROR_MESSAGE
24
+ ) if lineno
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def matching?(operator, op1, op2)
31
+ return false unless operator == :*
32
+
33
+ op1[0] == :array and op2[0] == :string_literal
34
+ end
35
+ end
36
+ end
37
+ end
@@ -18,6 +18,7 @@ module Rubocop
18
18
  @special = {
19
19
  assign: [[:on_op, '=']],
20
20
  brace_block: [[:on_lbrace, '{']],
21
+ hash: [[:on_lbrace, '{']],
21
22
  ifop: [[:on_op, '?'], [:on_op, ':']]
22
23
  }
23
24
  end
@@ -99,8 +100,7 @@ module Rubocop
99
100
 
100
101
  children.each do |elem|
101
102
  case elem
102
- when Array
103
- correlate(elem, path) # Dive deeper
103
+ when Array then correlate(elem, path) # Dive deeper
104
104
  when Symbol
105
105
  unless elem.to_s =~ /^@?[a-z_]+$/
106
106
  # There's a trailing @ in some symbols in sexp,
@@ -6,16 +6,55 @@ module Rubocop
6
6
  ERROR_MESSAGE = 'Use hash literal {} instead of Hash.new.'
7
7
 
8
8
  def inspect(file, source, tokens, sexp)
9
+ offences = preliminary_scan(sexp)
10
+
11
+ # find Hash.new()
9
12
  each(:method_add_arg, sexp) do |s|
10
- potential_class = s[1][1][1]
13
+ next if s[1][0] != :call
14
+
15
+ receiver = s[1][1][1]
16
+ method_name = s[1][3][1]
11
17
 
12
- if potential_class && potential_class[1] == 'Hash' &&
13
- s[1][3][1] == 'new' && s[2] == [:arg_paren, nil]
18
+ if receiver && receiver[1] == 'Hash' &&
19
+ method_name == 'new' && s[2] == [:arg_paren, nil]
20
+ offences.delete(Offence.new(:convention,
21
+ receiver[2].lineno,
22
+ ERROR_MESSAGE))
14
23
  add_offence(:convention,
15
- potential_class[2].lineno,
24
+ receiver[2].lineno,
16
25
  ERROR_MESSAGE)
17
26
  end
27
+
28
+ # check for false positives
29
+ if receiver && receiver[1] == 'Hash' &&
30
+ method_name == 'new' && s[2] != [:arg_paren, nil]
31
+ offences.delete(Offence.new(:convention,
32
+ receiver[2].lineno,
33
+ ERROR_MESSAGE))
34
+ end
35
+ end
36
+
37
+ offences.each { |o| add_offence(*o.explode) }
38
+ end
39
+
40
+ def preliminary_scan(sexp)
41
+ offences = []
42
+
43
+ # find Hash.new
44
+ # there will be some false positives here, which
45
+ # we'll eliminate later on
46
+ each(:call, sexp) do |s|
47
+ receiver = s[1][1]
48
+
49
+ if receiver && receiver[1] == 'Hash' &&
50
+ s[3][1] == 'new'
51
+ offences << Offence.new(:convention,
52
+ receiver[2].lineno,
53
+ ERROR_MESSAGE)
54
+ end
18
55
  end
56
+
57
+ offences
19
58
  end
20
59
  end
21
60
  end
@@ -10,8 +10,8 @@ module Rubocop
10
10
  keys = assoclist_from_args[1].map { |assoc_new| assoc_new[1][0] }
11
11
  # If at least one of the keys in the hash is neither a symbol (:a)
12
12
  # nor a label (a:), we can't require the new syntax.
13
- return if keys.find do |key|
14
- not [:symbol_literal, :@label].include?(key)
13
+ return if keys.any? do |key|
14
+ ![:symbol_literal, :@label].include?(key)
15
15
  end
16
16
  end
17
17
  each(:assoc_new, sexp) do |assoc_new|
@@ -5,7 +5,7 @@ module Rubocop
5
5
  module IfThenElse
6
6
  def inspect(file, source, tokens, sexp)
7
7
  tokens.each_with_index do |t, ix|
8
- if t.type == :on_kw && ['if', 'unless'].include?(t.text)
8
+ if t.type == :on_kw && %w(if unless).include?(t.text)
9
9
  if kind_of_if(tokens, ix + 1) == self.class
10
10
  add_offence(:convention, t.pos.lineno, error_message)
11
11
  end
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+
3
+ module Rubocop
4
+ module Cop
5
+ class LeadingCommentSpace < Cop
6
+ ERROR_MESSAGE = 'Missing space after #.'
7
+
8
+ def inspect(file, source, tokens, sexp)
9
+ tokens.each_index do |ix|
10
+ t = tokens[ix]
11
+ if t.type == :on_comment && t.text =~ /^#+[^#\s]/
12
+ unless t.text.start_with?('#!') && t.pos.lineno == 1
13
+ add_offence(:convention, t.pos.lineno, ERROR_MESSAGE)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+
3
+ module Rubocop
4
+ module Cop
5
+ class LineContinuation < Cop
6
+ ERROR_MESSAGE = 'Avoid the use of the line continuation character(/).'
7
+
8
+ def inspect(file, source, tokens, sexp)
9
+ tokens.each_index do |ix|
10
+ t = tokens[ix]
11
+ if t.type == :on_sp && t.text == "\\\n"
12
+ add_offence(:convention, t.pos.lineno, ERROR_MESSAGE)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -16,7 +16,7 @@ module Rubocop
16
16
  end
17
17
 
18
18
  def self.max
19
- LineLength.config ? LineLength.config['Max'] || 79 : 79
19
+ LineLength.config['Max']
20
20
  end
21
21
  end
22
22
  end
@@ -5,25 +5,26 @@ module Rubocop
5
5
  class MethodAndVariableSnakeCase < Cop
6
6
  ERROR_MESSAGE = 'Use snake_case for methods and variables.'
7
7
  SNAKE_CASE = /^@?[\da-z_]+[!?=]?$/
8
+ CONSTANT = /^[A-Z]/
8
9
 
9
10
  def inspect(file, source, tokens, sexp)
10
- each(:def, sexp) { |s| check(s[1]) }
11
+ each(:def, sexp) { |s| check(*s[1]) }
11
12
 
12
13
  each(:assign, sexp) do |s|
13
14
  case s[1][0]
14
15
  when :var_field
15
- check(s[1][1])
16
+ check(*s[1][1]) unless s[1][1][1] =~ CONSTANT
16
17
  when :field
17
18
  if s[1][1][0] == :var_ref && s[1][1][1][0..1] == [:@kw, 'self']
18
- check(s[1][3])
19
+ check(*s[1][3])
19
20
  end
20
21
  end
21
22
  end
22
23
  end
23
24
 
24
- def check(sexp)
25
- if [:@ivar, :@ident].include?(sexp[0]) && sexp[1] !~ SNAKE_CASE
26
- add_offence(:convention, sexp[2].lineno, ERROR_MESSAGE)
25
+ def check(type, name, pos)
26
+ if [:@ivar, :@ident, :@const].include?(type) && name !~ SNAKE_CASE
27
+ add_offence(:convention, pos.lineno, ERROR_MESSAGE)
27
28
  end
28
29
  end
29
30
  end