rubocop 0.10.0 → 0.11.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 (79) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +35 -0
  3. data/CONTRIBUTING.md +2 -0
  4. data/README.md +102 -5
  5. data/config/default.yml +31 -3
  6. data/config/enabled.yml +21 -3
  7. data/lib/rubocop.rb +5 -0
  8. data/lib/rubocop/cli.rb +27 -7
  9. data/lib/rubocop/config.rb +21 -2
  10. data/lib/rubocop/config_store.rb +4 -1
  11. data/lib/rubocop/cop/commissioner.rb +2 -4
  12. data/lib/rubocop/cop/cop.rb +8 -8
  13. data/lib/rubocop/cop/lint/assignment_in_condition.rb +3 -0
  14. data/lib/rubocop/cop/lint/block_alignment.rb +10 -20
  15. data/lib/rubocop/cop/lint/useless_assignment.rb +63 -0
  16. data/lib/rubocop/cop/lint/useless_comparison.rb +30 -0
  17. data/lib/rubocop/cop/style/align_parameters.rb +23 -13
  18. data/lib/rubocop/cop/style/and_or.rb +13 -1
  19. data/lib/rubocop/cop/style/blocks.rb +35 -0
  20. data/lib/rubocop/cop/style/character_literal.rb +1 -1
  21. data/lib/rubocop/cop/style/comment_annotation.rb +20 -5
  22. data/lib/rubocop/cop/style/dot_position.rb +7 -1
  23. data/lib/rubocop/cop/style/empty_line_between_defs.rb +1 -1
  24. data/lib/rubocop/cop/style/favor_modifier.rb +4 -4
  25. data/lib/rubocop/cop/style/module_function.rb +34 -0
  26. data/lib/rubocop/cop/style/multiline_if_then.rb +7 -9
  27. data/lib/rubocop/cop/style/redundant_begin.rb +7 -7
  28. data/lib/rubocop/cop/style/redundant_return.rb +9 -11
  29. data/lib/rubocop/cop/style/redundant_self.rb +5 -1
  30. data/lib/rubocop/cop/style/regexp_literal.rb +2 -1
  31. data/lib/rubocop/cop/style/signal_exception.rb +40 -0
  32. data/lib/rubocop/cop/style/string_literals.rb +2 -2
  33. data/lib/rubocop/cop/style/symbol_name.rb +11 -0
  34. data/lib/rubocop/cop/style/trivial_accessors.rb +22 -10
  35. data/lib/rubocop/cop/variable_inspector.rb +92 -71
  36. data/lib/rubocop/formatter/clang_style_formatter.rb +8 -3
  37. data/lib/rubocop/formatter/disabled_config_formatter.rb +32 -0
  38. data/lib/rubocop/formatter/formatter_set.rb +7 -4
  39. data/lib/rubocop/target_finder.rb +3 -4
  40. data/lib/rubocop/version.rb +1 -1
  41. data/rubocop.gemspec +1 -1
  42. data/spec/rubocop/cli_spec.rb +90 -1
  43. data/spec/rubocop/cops/commissioner_spec.rb +1 -1
  44. data/spec/rubocop/cops/lint/assignment_in_condition_spec.rb +6 -0
  45. data/spec/rubocop/cops/lint/block_alignment_spec.rb +16 -4
  46. data/spec/rubocop/cops/lint/empty_ensure_spec.rb +1 -1
  47. data/spec/rubocop/cops/lint/ensure_return_spec.rb +1 -1
  48. data/spec/rubocop/cops/lint/shadowing_outer_local_variable_spec.rb +4 -4
  49. data/spec/rubocop/cops/lint/unused_local_variable_spec.rb +49 -13
  50. data/spec/rubocop/cops/lint/useless_assignment_spec.rb +62 -0
  51. data/spec/rubocop/cops/lint/useless_comparison_spec.rb +31 -0
  52. data/spec/rubocop/cops/style/align_parameters_spec.rb +9 -0
  53. data/spec/rubocop/cops/style/and_or_spec.rb +12 -0
  54. data/spec/rubocop/cops/style/avoid_global_vars_spec.rb +1 -1
  55. data/spec/rubocop/cops/style/blocks_spec.rb +57 -14
  56. data/spec/rubocop/cops/style/character_literal_spec.rb +2 -2
  57. data/spec/rubocop/cops/style/comment_annotation_spec.rb +32 -4
  58. data/spec/rubocop/cops/style/dot_position_spec.rb +10 -0
  59. data/spec/rubocop/cops/style/empty_line_between_defs_spec.rb +12 -0
  60. data/spec/rubocop/cops/style/end_of_line_spec.rb +1 -0
  61. data/spec/rubocop/cops/style/favor_modifier_spec.rb +18 -0
  62. data/spec/rubocop/cops/style/hash_syntax_spec.rb +7 -2
  63. data/spec/rubocop/cops/style/module_function_spec.rb +30 -0
  64. data/spec/rubocop/cops/style/redundant_begin_spec.rb +2 -2
  65. data/spec/rubocop/cops/style/redundant_return_spec.rb +4 -4
  66. data/spec/rubocop/cops/style/redundant_self_spec.rb +36 -2
  67. data/spec/rubocop/cops/style/regexp_literal_spec.rb +1 -0
  68. data/spec/rubocop/cops/style/signal_exception_spec.rb +74 -0
  69. data/spec/rubocop/cops/style/string_literals_spec.rb +10 -0
  70. data/spec/rubocop/cops/style/symbol_name_spec.rb +13 -0
  71. data/spec/rubocop/cops/style/trivial_accessors_spec.rb +28 -3
  72. data/spec/rubocop/cops/variable_inspector_spec.rb +217 -36
  73. data/spec/rubocop/formatter/base_formatter_spec.rb +3 -3
  74. data/spec/rubocop/formatter/clang_style_formatter_spec.rb +19 -0
  75. data/spec/rubocop/formatter/disabled_config_formatter_spec.rb +48 -0
  76. data/spec/rubocop/formatter/formatter_set_spec.rb +1 -1
  77. data/spec/rubocop/processed_source_spec.rb +1 -1
  78. data/spec/spec_helper.rb +18 -13
  79. metadata +31 -38
@@ -31,7 +31,10 @@ module Rubocop
31
31
  dir = File.dirname(file)
32
32
  @path_cache[dir] ||= Config.configuration_file_for(dir)
33
33
  path = @path_cache[dir]
34
- @object_cache[path] ||= Config.configuration_from_file(path)
34
+ @object_cache[path] ||= begin
35
+ print "For #{dir}: " if Config.debug?
36
+ Config.configuration_from_file(path)
37
+ end
35
38
  end
36
39
  end
37
40
  end
@@ -70,9 +70,7 @@ module Rubocop
70
70
  # its own processing.
71
71
  def invoke_cops_callback(processed_source)
72
72
  @cops.each do |cop|
73
- if cop.respond_to?(:investigate)
74
- cop.investigate(processed_source)
75
- end
73
+ cop.investigate(processed_source) if cop.respond_to?(:investigate)
76
74
  end
77
75
  end
78
76
 
@@ -80,7 +78,7 @@ module Rubocop
80
78
  cop.send callback, node
81
79
  rescue => e
82
80
  if @options[:raise_error]
83
- fail e
81
+ raise e
84
82
  else
85
83
  @errors[cop] << e
86
84
  end
@@ -11,16 +11,16 @@ module Rubocop
11
11
  # A commissioner object is responsible for traversing the AST and invoking
12
12
  # the specific callbacks on each cop.
13
13
  # If a cop needs to do its own processing of the AST or depends on
14
- # something else it should define the #investigate method and do
14
+ # something else it should define the `#investigate` method and do
15
15
  # the processing there.
16
16
  #
17
17
  # @example
18
18
  #
19
- # class CustomCop < Cop
20
- # def investigate(processed_source)
21
- # # Do custom processing
19
+ # class CustomCop < Cop
20
+ # def investigate(processed_source)
21
+ # # Do custom processing
22
+ # end
22
23
  # end
23
- # end
24
24
  class Cop
25
25
  extend AST::Sexp
26
26
 
@@ -73,11 +73,11 @@ module Rubocop
73
73
  @corrections = []
74
74
  end
75
75
 
76
- def do_autocorrect(node)
77
- autocorrect_action(node) if autocorrect
76
+ def do_autocorrect(node, *args)
77
+ autocorrect_action(node, *args) if autocorrect
78
78
  end
79
79
 
80
- def autocorrect_action(node)
80
+ def autocorrect_action(node, *args)
81
81
  end
82
82
 
83
83
  def add_offence(severity, location, message)
@@ -26,6 +26,9 @@ module Rubocop
26
26
  def check(node)
27
27
  condition, = *node
28
28
 
29
+ # assignments inside blocks are not what we're looking for
30
+ return if condition.type == :block
31
+
29
32
  on_node([:begin, *ASGN_NODES], condition) do |asgn_node|
30
33
  # skip safe assignment nodes if safe assignment is allowed
31
34
  return if safe_assignment_allowed? && safe_assignment?(asgn_node)
@@ -21,9 +21,7 @@ module Rubocop
21
21
 
22
22
  def on_block(node)
23
23
  return if already_processed_node?(node)
24
- method, = *node
25
- start_node = method.loc.expression.source =~ /\n/ ? method : node
26
- check_block_alignment(start_node.loc.expression, node.loc)
24
+ check_block_alignment(node, node)
27
25
  end
28
26
 
29
27
  def on_and(node)
@@ -31,7 +29,7 @@ module Rubocop
31
29
 
32
30
  _left, right = *node
33
31
  if right.type == :block
34
- check_block_alignment(node.loc.expression, right.loc)
32
+ check_block_alignment(node, right)
35
33
  @inspected_blocks << right
36
34
  end
37
35
  end
@@ -93,7 +91,7 @@ module Rubocop
93
91
  return if already_processed_node?(block_node)
94
92
 
95
93
  @inspected_blocks << block_node
96
- check_block_alignment(begin_node.loc.expression, block_node.loc)
94
+ check_block_alignment(begin_node, block_node)
97
95
  end
98
96
 
99
97
  def find_block_node(node)
@@ -117,24 +115,16 @@ module Rubocop
117
115
  end
118
116
  end
119
117
 
120
- def check_block_alignment(start_loc, block_loc)
121
- match = start_loc.source.match(/\n(\s*)((end)?\.\S+)\Z/)
122
- if match
123
- start_line = start_loc.line + start_loc.source.count("\n")
124
- start_column = match.captures[0].length
125
- start_source = match.captures[1]
126
- else
127
- start_line = start_loc.line
128
- start_column = start_loc.column
129
- start_source = start_loc.source.lines.to_a.first.chomp
130
- end
131
- end_loc = block_loc.end
132
- if block_loc.begin.line != end_loc.line &&
133
- start_column != end_loc.column
118
+ def check_block_alignment(start_node, block_node)
119
+ start_loc = start_node.loc.expression
120
+ end_loc = block_node.loc.end
121
+ if block_node.loc.begin.line != end_loc.line &&
122
+ start_loc.column != end_loc.column
134
123
  add_offence(:warning,
135
124
  end_loc,
136
125
  sprintf(MSG, end_loc.line, end_loc.column,
137
- start_source, start_line, start_column))
126
+ start_loc.source.lines.to_a.first.chomp,
127
+ start_loc.line, start_loc.column))
138
128
  end
139
129
  end
140
130
 
@@ -0,0 +1,63 @@
1
+ # encoding: utf-8
2
+
3
+ module Rubocop
4
+ module Cop
5
+ module Lint
6
+ # This cop checks for useless assignment as the final expression
7
+ # of a function definition.
8
+ #
9
+ # @example
10
+ #
11
+ # def something
12
+ # x = 5
13
+ # end
14
+ #
15
+ # def something
16
+ # x = Something.new
17
+ # x.attr = 5
18
+ # end
19
+ class UselessAssignment < Cop
20
+ MSG = 'Useless assignment to local variable %s.'
21
+
22
+ def on_def(node)
23
+ _name, _args, body = *node
24
+
25
+ check_for_useless_assignment(body)
26
+ end
27
+
28
+ def on_defs(node)
29
+ _target, _name, _args, body = *node
30
+
31
+ check_for_useless_assignment(body)
32
+ end
33
+
34
+ private
35
+
36
+ def check_for_useless_assignment(body)
37
+ return unless body
38
+
39
+ if body.type == :begin
40
+ expression = body.children
41
+ else
42
+ expression = body
43
+ end
44
+
45
+ last_expr = expression.is_a?(Array) ? expression.last : expression
46
+
47
+ if last_expr && last_expr.type == :lvasgn
48
+ var_name, = *last_expr
49
+ add_offence(:warning, last_expr.loc.name, MSG.format(var_name))
50
+ elsif last_expr && last_expr.type == :send
51
+ receiver, method, _args = *last_expr
52
+
53
+ if receiver && receiver.type == :lvar && method =~ /\w=$/
54
+ add_offence(:warning,
55
+ receiver.loc.name,
56
+ MSG.format(receiver.loc.name.source))
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+
3
+ module Rubocop
4
+ module Cop
5
+ module Lint
6
+ # This cop checks for comparison of something with itself.
7
+ #
8
+ # @example
9
+ #
10
+ # x.top >= x.top
11
+ class UselessComparison < Cop
12
+ MSG = 'Comparison of something with itself detected.'
13
+
14
+ OPS = %w(== === != < > <= >= <=>)
15
+
16
+ def on_send(node)
17
+ op = node.loc.selector.source
18
+
19
+ if OPS.include?(op)
20
+ receiver, _method, selector = *node
21
+
22
+ if receiver == selector
23
+ add_offence(:warning, node.loc.selector, MSG)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -12,22 +12,32 @@ module Rubocop
12
12
  def on_send(node)
13
13
  _receiver, method, *args = *node
14
14
 
15
- if method != :[]= && args.size > 1
16
- first_arg_col = args.first.loc.expression.column
17
- prev_arg_line = args.first.loc.expression.line
18
- prev_arg_col = first_arg_col
15
+ return if method == :[]=
16
+ return if args.size <= 1
19
17
 
20
- args.each do |arg|
21
- cur_arg_line = arg.loc.expression.line
22
- cur_arg_col = arg.loc.expression.column
18
+ first_arg_column = args.first.loc.expression.column
23
19
 
24
- if cur_arg_line != prev_arg_line &&
25
- cur_arg_col != first_arg_col
26
- add_offence(:convention, arg.loc.expression, MSG)
27
- end
20
+ args.each_cons(2) do |prev, current|
21
+ current_pos = current.loc.expression
28
22
 
29
- prev_arg_col = cur_arg_col
30
- prev_arg_line = cur_arg_line
23
+ if current_pos.line > prev.loc.expression.line &&
24
+ current_pos.column != first_arg_column
25
+ add_offence(:convention, current_pos, MSG)
26
+ do_autocorrect(current, first_arg_column - current_pos.column)
27
+ end
28
+ end
29
+ end
30
+
31
+ def autocorrect_action(node, column_delta)
32
+ @corrections << lambda do |corrector|
33
+ expr = node.loc.expression
34
+ if column_delta > 0
35
+ corrector.replace(expr, ' ' * column_delta + expr.source)
36
+ else
37
+ range = Parser::Source::Range.new(expr.source_buffer,
38
+ expr.begin_pos + column_delta,
39
+ expr.end_pos)
40
+ corrector.replace(range, expr.source)
31
41
  end
32
42
  end
33
43
  end
@@ -32,10 +32,22 @@ module Rubocop
32
32
  end
33
33
 
34
34
  def autocorrect_action(node)
35
- @corrections << lambda do |corrector|
35
+ correction = lambda do |corrector|
36
36
  replacement = (node.type == :and ? '&&' : '||')
37
37
  corrector.replace(node.loc.operator, replacement)
38
38
  end
39
+
40
+ new_source = rewrite_node(node, correction)
41
+
42
+ # Make the correction only if it doesn't change the AST.
43
+ if node == SourceParser.parse(new_source).ast
44
+ @corrections << correction
45
+ end
46
+ end
47
+
48
+ def rewrite_node(node, correction)
49
+ processed_source = SourceParser.parse(node.loc.expression.source)
50
+ Corrector.new(processed_source.buffer, [correction]).rewrite
39
51
  end
40
52
  end
41
53
  end
@@ -9,7 +9,22 @@ module Rubocop
9
9
  MULTI_LINE_MSG = 'Avoid using {...} for multi-line blocks.'
10
10
  SINGLE_LINE_MSG = 'Prefer {...} over do...end for single-line blocks.'
11
11
 
12
+ def on_send(node)
13
+ _receiver, method_name, *args = *node
14
+ if args.any?
15
+ block = get_block(args.last)
16
+ if block && !has_parentheses?(node) && !operator?(method_name)
17
+ # If there are no parentheses around the arguments, then braces
18
+ # and do-end have different meaning due to how they bind, so we
19
+ # allow either.
20
+ ignore_node(block)
21
+ end
22
+ end
23
+ end
24
+
12
25
  def on_block(node)
26
+ return if ignored_node?(node)
27
+
13
28
  block_length = Util.block_length(node)
14
29
  block_begin = node.loc.begin.source
15
30
 
@@ -19,6 +34,26 @@ module Rubocop
19
34
  add_offence(:convention, node.loc.begin, SINGLE_LINE_MSG)
20
35
  end
21
36
  end
37
+
38
+ private
39
+
40
+ def get_block(node)
41
+ case node.type
42
+ when :block
43
+ node
44
+ when :send
45
+ receiver, _method_name, *_args = *node
46
+ get_block(receiver) if receiver
47
+ end
48
+ end
49
+
50
+ def has_parentheses?(send_node)
51
+ send_node.loc.begin
52
+ end
53
+
54
+ def operator?(method_name)
55
+ method_name =~ /^\W/
56
+ end
22
57
  end
23
58
  end
24
59
  end
@@ -10,7 +10,7 @@ module Rubocop
10
10
  def on_str(node)
11
11
  # Constants like __FILE__ are handled as strings,
12
12
  # but don't respond to begin.
13
- return unless node.loc.respond_to?(:begin)
13
+ return unless node.loc.respond_to?(:begin) && node.loc.begin
14
14
  return if part_of_ignored_node?(node)
15
15
 
16
16
  # we don't register an offence for things like ?\C-\M-d
@@ -8,7 +8,6 @@ module Rubocop
8
8
  class CommentAnnotation < Cop
9
9
  MSG = 'Annotation keywords shall be all upper case, followed by a ' +
10
10
  'colon and a space, then a note describing the problem.'
11
- KEYWORDS = %w(TODO FIXME OPTIMIZE HACK REVIEW)
12
11
 
13
12
  def investigate(processed_source)
14
13
  processed_source.comments.each do |comment|
@@ -17,7 +16,7 @@ module Rubocop
17
16
  margin, first_word, colon, space, note = *match.captures
18
17
  if annotation?(first_word, colon, space, note) &&
19
18
  !correct_annotation?(first_word, colon, space, note)
20
- start = comment.loc.begin_pos + margin.length
19
+ start = comment.loc.expression.begin_pos + margin.length
21
20
  length = first_word.length + (colon || '').length
22
21
  range = Parser::Source::Range.new(processed_source.buffer,
23
22
  start,
@@ -28,15 +27,31 @@ module Rubocop
28
27
  end
29
28
  end
30
29
 
30
+ def self.keywords
31
+ CommentAnnotation.config['Keywords']
32
+ end
33
+
31
34
  private
32
35
 
33
36
  def annotation?(first_word, colon, space, note)
34
- KEYWORDS.include?(first_word.upcase) && (colon || space || !note)
37
+ keyword_appearance?(first_word, colon, space) &&
38
+ !just_first_word_of_sentence?(first_word, colon, space, note)
39
+ end
40
+
41
+ def keyword_appearance?(first_word, colon, space)
42
+ keyword?(first_word.upcase) && (colon || space)
43
+ end
44
+
45
+ def just_first_word_of_sentence?(first_word, colon, space, note)
46
+ first_word == first_word.capitalize && !colon && space && note
35
47
  end
36
48
 
37
49
  def correct_annotation?(first_word, colon, space, note)
38
- KEYWORDS.include?(first_word) &&
39
- (colon && space && note || !colon && !note)
50
+ keyword?(first_word) && (colon && space && note || !colon && !note)
51
+ end
52
+
53
+ def keyword?(word)
54
+ CommentAnnotation.keywords.include?(word)
40
55
  end
41
56
  end
42
57
  end
@@ -19,7 +19,13 @@ module Rubocop
19
19
 
20
20
  def proper_dot_position?(node)
21
21
  dot_line = node.loc.dot.line
22
- selector_line = node.loc.selector.line
22
+
23
+ if node.loc.selector
24
+ selector_line = node.loc.selector.line
25
+ else
26
+ # l.(1) has no selector, so we use the opening parenthesis instead
27
+ selector_line = node.loc.begin.line
28
+ end
23
29
 
24
30
  case DotPosition.config['Style'].downcase
25
31
  when 'leading' then dot_line == selector_line
@@ -12,7 +12,7 @@ module Rubocop
12
12
  def_start = node.loc.keyword.line
13
13
  def_end = node.loc.end.line
14
14
 
15
- if @prev_def_end && (def_start - @prev_def_end) < 2
15
+ if @prev_def_end && (def_start - @prev_def_end) == 1
16
16
  add_offence(:convention, node.loc.keyword, MSG)
17
17
  end
18
18
 
@@ -65,10 +65,10 @@ module Rubocop
65
65
  return unless processed_source.ast
66
66
  on_node(:if, processed_source.ast) do |node|
67
67
  # discard ternary ops, if/else and modifier if/unless nodes
68
- return if ternary_op?(node)
69
- return if modifier_if?(node)
70
- return if elsif?(node)
71
- return if if_else?(node)
68
+ next if ternary_op?(node)
69
+ next if modifier_if?(node)
70
+ next if elsif?(node)
71
+ next if if_else?(node)
72
72
 
73
73
  if check(node, processed_source.comments)
74
74
  add_offence(:convention, node.loc.expression, error_message)