rubocop 0.14.1 → 0.15.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 (109) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -3
  3. data/CHANGELOG.md +245 -198
  4. data/README.md +7 -0
  5. data/Rakefile +5 -1
  6. data/config/default.yml +27 -4
  7. data/config/enabled.yml +18 -4
  8. data/lib/rubocop.rb +13 -1
  9. data/lib/rubocop/cli.rb +83 -23
  10. data/lib/rubocop/config.rb +1 -1
  11. data/lib/rubocop/cop/cop.rb +31 -6
  12. data/lib/rubocop/cop/lint/block_alignment.rb +11 -8
  13. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +21 -14
  14. data/lib/rubocop/cop/lint/useless_setter_call.rb +1 -1
  15. data/lib/rubocop/cop/rails/output.rb +35 -0
  16. data/lib/rubocop/cop/style/{access_control.rb → access_modifier_indentation.rb} +18 -15
  17. data/lib/rubocop/cop/style/alias.rb +14 -2
  18. data/lib/rubocop/cop/style/align_hash.rb +174 -109
  19. data/lib/rubocop/cop/style/autocorrect_alignment.rb +38 -18
  20. data/lib/rubocop/cop/style/blocks.rb +4 -6
  21. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +3 -3
  22. data/lib/rubocop/cop/style/cyclomatic_complexity.rb +46 -0
  23. data/lib/rubocop/cop/style/empty_lines_around_access_modifier.rb +48 -0
  24. data/lib/rubocop/cop/style/empty_lines_around_body.rb +62 -0
  25. data/lib/rubocop/cop/style/end_of_line.rb +6 -2
  26. data/lib/rubocop/cop/style/favor_modifier.rb +11 -1
  27. data/lib/rubocop/cop/style/final_newline.rb +10 -4
  28. data/lib/rubocop/cop/style/hash_syntax.rb +32 -21
  29. data/lib/rubocop/cop/style/leading_comment_space.rb +9 -0
  30. data/lib/rubocop/cop/style/method_call_parentheses.rb +11 -1
  31. data/lib/rubocop/cop/style/numeric_literals.rb +11 -15
  32. data/lib/rubocop/cop/style/redundant_return.rb +7 -4
  33. data/lib/rubocop/cop/style/redundant_self.rb +3 -3
  34. data/lib/rubocop/cop/style/signal_exception.rb +4 -2
  35. data/lib/rubocop/cop/style/space_after_comma_etc.rb +7 -1
  36. data/lib/rubocop/cop/style/space_after_control_keyword.rb +6 -0
  37. data/lib/rubocop/cop/style/space_after_method_name.rb +7 -1
  38. data/lib/rubocop/cop/style/space_after_not.rb +6 -2
  39. data/lib/rubocop/cop/style/space_around_block_braces.rb +149 -0
  40. data/lib/rubocop/cop/style/space_around_equals_in_parameter_default.rb +33 -0
  41. data/lib/rubocop/cop/style/space_around_operators.rb +169 -0
  42. data/lib/rubocop/cop/style/space_before_modifier_keyword.rb +6 -0
  43. data/lib/rubocop/cop/style/space_inside.rb +35 -0
  44. data/lib/rubocop/cop/style/space_inside_brackets.rb +18 -0
  45. data/lib/rubocop/cop/style/space_inside_hash_literal_braces.rb +99 -0
  46. data/lib/rubocop/cop/style/space_inside_parens.rb +18 -0
  47. data/lib/rubocop/cop/style/special_global_vars.rb +52 -25
  48. data/lib/rubocop/cop/style/string_literals.rb +1 -1
  49. data/lib/rubocop/cop/style/surrounding_space.rb +1 -344
  50. data/lib/rubocop/cop/style/trailing_blank_lines.rb +17 -5
  51. data/lib/rubocop/cop/style/trailing_whitespace.rb +9 -5
  52. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -2
  53. data/lib/rubocop/cop/style/word_array.rb +16 -1
  54. data/lib/rubocop/cop/team.rb +5 -5
  55. data/lib/rubocop/cop/util.rb +1 -0
  56. data/lib/rubocop/formatter/offence_count_formatter.rb +0 -1
  57. data/lib/rubocop/options.rb +76 -111
  58. data/lib/rubocop/rake_task.rb +4 -2
  59. data/lib/rubocop/target_finder.rb +3 -3
  60. data/lib/rubocop/version.rb +1 -1
  61. data/spec/rubocop/cli_spec.rb +123 -13
  62. data/spec/rubocop/config_spec.rb +2 -2
  63. data/spec/rubocop/cop/lint/rescue_exception_spec.rb +10 -0
  64. data/spec/rubocop/cop/lint/useless_setter_call_spec.rb +13 -0
  65. data/spec/rubocop/cop/offence_spec.rb +2 -0
  66. data/spec/rubocop/cop/rails/output_spec.rb +40 -0
  67. data/spec/rubocop/cop/style/access_modifier_indentation_spec.rb +243 -0
  68. data/spec/rubocop/cop/style/alias_spec.rb +8 -0
  69. data/spec/rubocop/cop/style/align_array_spec.rb +12 -0
  70. data/spec/rubocop/cop/style/align_hash_spec.rb +15 -0
  71. data/spec/rubocop/cop/style/braces_around_hash_parameters_spec.rb +7 -4
  72. data/spec/rubocop/cop/style/cyclomatic_complexity_spec.rb +203 -0
  73. data/spec/rubocop/cop/style/empty_lines_around_access_modifier_spec.rb +56 -0
  74. data/spec/rubocop/cop/style/empty_lines_around_body_spec.rb +87 -0
  75. data/spec/rubocop/cop/style/end_of_line_spec.rb +17 -8
  76. data/spec/rubocop/cop/style/favor_modifier_spec.rb +34 -0
  77. data/spec/rubocop/cop/style/final_newline_spec.rb +5 -0
  78. data/spec/rubocop/cop/style/hash_syntax_spec.rb +22 -2
  79. data/spec/rubocop/cop/style/leading_comment_space_spec.rb +5 -0
  80. data/spec/rubocop/cop/style/method_call_parentheses_spec.rb +39 -4
  81. data/spec/rubocop/cop/style/numeric_literals_spec.rb +5 -0
  82. data/spec/rubocop/cop/style/signal_exception_spec.rb +11 -0
  83. data/spec/rubocop/cop/style/space_after_colon_spec.rb +7 -0
  84. data/spec/rubocop/cop/style/space_after_comma_spec.rb +5 -0
  85. data/spec/rubocop/cop/style/space_after_control_keyword_spec.rb +29 -8
  86. data/spec/rubocop/cop/style/space_after_method_name_spec.rb +15 -0
  87. data/spec/rubocop/cop/style/space_after_semicolon_spec.rb +5 -0
  88. data/spec/rubocop/cop/style/space_around_block_braces_spec.rb +68 -0
  89. data/spec/rubocop/cop/style/space_around_equals_in_default_parameter_spec.rb +5 -0
  90. data/spec/rubocop/cop/style/space_around_operators_spec.rb +43 -0
  91. data/spec/rubocop/cop/style/space_before_modifier_keyword_spec.rb +23 -0
  92. data/spec/rubocop/cop/style/space_inside_brackets_spec.rb +7 -0
  93. data/spec/rubocop/cop/style/space_inside_hash_literal_braces_spec.rb +65 -23
  94. data/spec/rubocop/cop/style/space_inside_parens_spec.rb +7 -0
  95. data/spec/rubocop/cop/style/special_global_vars_spec.rb +12 -2
  96. data/spec/rubocop/cop/style/string_literals_spec.rb +6 -0
  97. data/spec/rubocop/cop/style/symbol_array_spec.rb +5 -7
  98. data/spec/rubocop/cop/style/trailing_blank_lines_spec.rb +26 -1
  99. data/spec/rubocop/cop/style/trailing_whitespace_spec.rb +7 -0
  100. data/spec/rubocop/cop/style/trivial_accessors_spec.rb +8 -0
  101. data/spec/rubocop/cop/style/word_array_spec.rb +33 -2
  102. data/spec/rubocop/cop/team_spec.rb +4 -4
  103. data/spec/rubocop/formatter/json_formatter_spec.rb +1 -1
  104. data/spec/rubocop/options_spec.rb +5 -96
  105. data/spec/rubocop/processed_source_spec.rb +3 -3
  106. data/spec/spec_helper.rb +28 -23
  107. data/spec/support/mri_syntax_checker.rb +20 -16
  108. metadata +24 -5
  109. data/spec/rubocop/cop/style/access_control_spec.rb +0 -164
@@ -29,28 +29,48 @@ module Rubocop
29
29
 
30
30
  @corrections << lambda do |corrector|
31
31
  expr = node.loc.expression
32
- if column_delta > 0
33
- corrector.replace(expr,
34
- expr.source.gsub(/^/, ' ' * column_delta))
35
- else
36
- offset = 0
37
- expr.source.each_line do |line|
38
- b = expr.begin_pos + offset
39
- if offset == 0
40
- range = Parser::Source::Range.new(expr.source_buffer,
41
- b + column_delta,
42
- b + line.length)
43
- corrector.replace(range, line)
44
- else
45
- range = Parser::Source::Range.new(expr.source_buffer,
46
- b, b + line.length)
47
- corrector.replace(range, line[-column_delta..-1])
48
- end
49
- offset += line.length
32
+ each_line(expr) do |line_begin_pos, line|
33
+ starts_with_space =
34
+ expr.source_buffer.source[line_begin_pos] =~ / /
35
+ pos_to_remove = if column_delta > 0 || starts_with_space
36
+ line_begin_pos
37
+ else
38
+ line_begin_pos - column_delta.abs
39
+ end
40
+ range = Parser::Source::Range.new(expr.source_buffer,
41
+ pos_to_remove,
42
+ pos_to_remove +
43
+ column_delta.abs)
44
+ if column_delta > 0
45
+ corrector.insert_before(range, ' ' * column_delta)
46
+ else
47
+ remove(range, corrector)
50
48
  end
51
49
  end
52
50
  end
53
51
  end
52
+
53
+ def remove(range, corrector)
54
+ original_stderr = $stderr
55
+ $stderr = StringIO.new # Avoid error messages on console
56
+ corrector.remove(range)
57
+ rescue RuntimeError
58
+ range = Parser::Source::Range.new(range.source_buffer,
59
+ range.begin_pos + 1,
60
+ range.end_pos + 1)
61
+ retry if range.source =~ /^ +$/
62
+ ensure
63
+ $stderr = original_stderr
64
+ end
65
+
66
+ def each_line(expr)
67
+ offset = 0
68
+ expr.source.each_line do |line|
69
+ line_begin_pos = expr.begin_pos + offset
70
+ yield line_begin_pos, line
71
+ offset += line.length
72
+ end
73
+ end
54
74
  end
55
75
  end
56
76
  end
@@ -41,12 +41,10 @@ module Rubocop
41
41
  if b.is?('{')
42
42
  # If the left brace is immediately preceded by a word character,
43
43
  # then we need a space before `do` to get valid Ruby code.
44
- padding = if b.source_buffer.source[b.begin_pos - 1, 1] =~ /\w/
45
- ' '
46
- else
47
- ''
48
- end
49
- corrector.replace(b, padding + 'do')
44
+ if b.source_buffer.source[b.begin_pos - 1, 1] =~ /\w/
45
+ corrector.insert_before(b, ' ')
46
+ end
47
+ corrector.replace(b, 'do')
50
48
  corrector.replace(e, 'end')
51
49
  else
52
50
  corrector.replace(b, '{')
@@ -8,19 +8,19 @@ module Rubocop
8
8
  def on_send(node)
9
9
  _receiver, method_name, *args = *node
10
10
 
11
- return unless args.size == 1
12
11
  # discard attr writer methods.
13
12
  return if method_name.to_s.end_with?('=')
14
13
  # discard operator methods
15
14
  return if OPERATOR_METHODS.include?(method_name)
16
15
 
17
16
  # we care only for the first argument
18
- arg = args.first
17
+ arg = args.last
19
18
  return unless arg && arg.type == :hash && arg.children.any?
20
19
 
21
20
  has_braces = !arg.loc.begin.nil?
21
+ all_hashes = args.length > 1 && args.all? { |a| a.type == :hash }
22
22
 
23
- if style == :no_braces && has_braces
23
+ if style == :no_braces && has_braces && !all_hashes
24
24
  convention(arg,
25
25
  :expression,
26
26
  'Redundant curly braces around a hash parameter.')
@@ -0,0 +1,46 @@
1
+ # encoding: utf-8
2
+
3
+ module Rubocop
4
+ module Cop
5
+ module Style
6
+ # This cop checks that the cyclomatic complexity of methods is not higher
7
+ # than the configured maximum. The cyclomatic complexity is the number of
8
+ # linearly independent paths through a method. The algorithm counts
9
+ # decision points and adds one.
10
+ #
11
+ # An if statement (or unless or ?:) increases the complexity by one. An
12
+ # else branch does not, since it doesn't add a decision point. The &&
13
+ # operator (or keyword and) can be converted to a nested if statement,
14
+ # and ||/or is shorthand for a sequence of ifs, so they also add one.
15
+ # Loops can be said to have an exit condition, so they add one.
16
+ class CyclomaticComplexity < Cop
17
+ MSG = 'Cyclomatic complexity for %s is too high. [%d/%d]'
18
+ DECISION_POINT_NODES = [:if, :while, :until, :for, :rescue, :when,
19
+ :and, :or]
20
+
21
+ def on_def(node)
22
+ method_name, _args, _body = *node
23
+ check(node, method_name)
24
+ end
25
+
26
+ def on_defs(node)
27
+ _scope, method_name, _args, _body = *node
28
+ check(node, method_name)
29
+ end
30
+
31
+ private
32
+
33
+ def check(node, method_name)
34
+ complexity = 1
35
+ on_node(DECISION_POINT_NODES, node) { complexity += 1 }
36
+
37
+ max = cop_config['Max']
38
+ if complexity > max
39
+ convention(node, :keyword,
40
+ sprintf(MSG, method_name, complexity, max))
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,48 @@
1
+ # encoding: utf-8
2
+
3
+ module Rubocop
4
+ module Cop
5
+ module Style
6
+ # Access modifiers should be surrounded by blank lines.
7
+ class EmptyLinesAroundAccessModifier < Cop
8
+ MSG = 'Keep a blank line before and after %s.'
9
+
10
+ PRIVATE_NODE = s(:send, nil, :private)
11
+ PROTECTED_NODE = s(:send, nil, :protected)
12
+ PUBLIC_NODE = s(:send, nil, :public)
13
+
14
+ def on_send(node)
15
+ return unless modifier_node?(node)
16
+
17
+ return if empty_lines_around?(node)
18
+
19
+ convention(node, :expression)
20
+ end
21
+
22
+ private
23
+
24
+ def empty_lines_around?(node)
25
+ send_line = node.loc.line
26
+ previous_line = processed_source[send_line - 2]
27
+ next_line = processed_source[send_line]
28
+
29
+ (class_def?(previous_line.lstrip) ||
30
+ previous_line.blank?) &&
31
+ next_line.blank?
32
+ end
33
+
34
+ def class_def?(line)
35
+ %w(class module).any? { |keyword| line.start_with?(keyword) }
36
+ end
37
+
38
+ def message(node)
39
+ format(MSG, node.loc.selector.source)
40
+ end
41
+
42
+ def modifier_node?(node)
43
+ [PRIVATE_NODE, PROTECTED_NODE, PUBLIC_NODE].include?(node)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,62 @@
1
+ # encoding: utf-8
2
+
3
+ module Rubocop
4
+ module Cop
5
+ module Style
6
+ # This cops checks for two or more consecutive blank lines.
7
+ class EmptyLinesAroundBody < Cop
8
+ MSG_BEG = 'Extra blank line detected at body beginning.'
9
+ MSG_END = 'Extra blank line detected at body end.'
10
+
11
+ def on_class(node)
12
+ check_node(node)
13
+ end
14
+
15
+ def on_module(node)
16
+ check_node(node)
17
+ end
18
+
19
+ def on_sclass(node)
20
+ check_node(node)
21
+ end
22
+
23
+ def on_def(node)
24
+ check_node(node)
25
+ end
26
+
27
+ def on_defs(node)
28
+ check_node(node)
29
+ end
30
+
31
+ private
32
+
33
+ def check_node(node)
34
+ start_line = node.loc.keyword.line
35
+ end_line = node.loc.end.line
36
+
37
+ return if start_line == end_line
38
+
39
+ check_source(start_line, end_line)
40
+ end
41
+
42
+ def check_source(start_line, end_line)
43
+ if processed_source.lines[start_line].blank?
44
+ range = source_range(processed_source.buffer,
45
+ processed_source[0...start_line],
46
+ 0,
47
+ 1)
48
+ convention(nil, range, MSG_BEG)
49
+ end
50
+
51
+ if processed_source.lines[end_line - 2].blank?
52
+ range = source_range(processed_source.buffer,
53
+ processed_source[0...(end_line - 2)],
54
+ 0,
55
+ 1)
56
+ convention(nil, range, MSG_END)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -8,13 +8,17 @@ module Rubocop
8
8
  MSG = 'Carriage return character detected.'
9
9
 
10
10
  def investigate(processed_source)
11
- processed_source.lines.each_with_index do |line, index|
11
+ original_source = IO.read(processed_source.buffer.name)
12
+ original_source.lines.each_with_index do |line, index|
12
13
  if line =~ /\r$/
13
14
  convention(nil,
14
15
  source_range(processed_source.buffer,
15
16
  processed_source[0...index],
16
- line.length - 1, 1),
17
+ 0, line.length),
17
18
  MSG)
19
+ # Usually there will be carriage return characters on all or none
20
+ # of the lines in a file, so we report only one offence.
21
+ break
18
22
  end
19
23
  end
20
24
  end
@@ -23,7 +23,7 @@ module Rubocop
23
23
  else
24
24
  indentation = sexp.loc.keyword.column
25
25
  kw_length = sexp.loc.keyword.size
26
- cond_length = cond.loc.expression.size
26
+ cond_length = conditional_length(cond)
27
27
  space = 1
28
28
  total = indentation + body_length + space + kw_length + space +
29
29
  cond_length
@@ -40,6 +40,16 @@ module Rubocop
40
40
  sexp.loc.expression.source.lines.to_a.size
41
41
  end
42
42
 
43
+ def conditional_length(conditional_node)
44
+ node = if conditional_node.type == :match_current_line
45
+ conditional_node.children.first
46
+ else
47
+ conditional_node
48
+ end
49
+
50
+ node.loc.expression.size
51
+ end
52
+
43
53
  def body_length(body)
44
54
  if body && body.loc.expression
45
55
  body.loc.expression.size
@@ -11,10 +11,16 @@ module Rubocop
11
11
  final_line = processed_source.raw_lines.to_a.last
12
12
 
13
13
  unless final_line.nil? || final_line.end_with?("\n")
14
- convention(nil,
15
- source_range(processed_source.buffer,
16
- processed_source[0...-1],
17
- final_line.length - 1, 1))
14
+ range = source_range(processed_source.buffer,
15
+ processed_source[0...-1],
16
+ final_line.length - 1, 1)
17
+ convention(range, range)
18
+ end
19
+ end
20
+
21
+ def autocorrect(range)
22
+ @corrections << lambda do |corrector|
23
+ corrector.insert_after(range, "\n")
18
24
  end
19
25
  end
20
26
  end
@@ -26,44 +26,55 @@ module Rubocop
26
26
 
27
27
  sym_indices = pairs.all? { |p| word_symbol_pair?(p) }
28
28
 
29
- if sym_indices
30
- pairs.each do |pair|
31
- if pair.loc.operator && pair.loc.operator.is?('=>')
32
- convention(pair,
33
- pair.loc.expression.begin.join(pair.loc.operator),
34
- MSG_19)
35
- end
36
- end
37
- end
29
+ check(pairs, '=>', MSG_19) if sym_indices
38
30
  end
39
31
 
40
32
  def hash_rockets_check(node)
41
33
  pairs = *node
42
34
 
43
- pairs.each do |pair|
44
- if pair.loc.operator && pair.loc.operator.is?(':')
45
- convention(pair,
46
- pair.loc.expression.begin.join(pair.loc.operator),
47
- MSG_HASH_ROCKETS)
48
- end
49
- end
35
+ check(pairs, ':', MSG_HASH_ROCKETS)
50
36
  end
51
37
 
52
38
  def autocorrect(node)
39
+ key = node.children.first.loc.expression
40
+ op = node.loc.operator
41
+ if cop_config['EnforcedStyle'] == 'ruby19' &&
42
+ !space_before_operator?(op, key) &&
43
+ config.for_cop('SpaceAroundOperators')['Enabled']
44
+ # Don't do the correction if there is no space before '=>'. The
45
+ # combined corrections of this cop and SpaceAroundOperators could
46
+ # produce code with illegal syntax.
47
+ fail CorrectionNotPossible
48
+ end
49
+
53
50
  @corrections << lambda do |corrector|
54
51
  if cop_config['EnforcedStyle'] == 'ruby19'
55
- replacement = node.loc.expression.source[1..-1]
56
- .sub(/\s*=>\s*/, ': ')
52
+ corrector.insert_after(key, ' ')
53
+ corrector.replace(key, key.source.sub(/^:(.*)/, '\1:'))
57
54
  else
58
- replacement = ':' + node.loc.expression.source
59
- .sub(/:\s*/, ' => ')
55
+ corrector.insert_after(key, ' => ')
56
+ corrector.insert_before(key, ':')
60
57
  end
61
- corrector.replace(node.loc.expression, replacement)
58
+ corrector.remove(range_with_surrounding_space(op))
62
59
  end
63
60
  end
64
61
 
65
62
  private
66
63
 
64
+ def space_before_operator?(op, key)
65
+ op.begin_pos - key.begin_pos - key.source.length > 0
66
+ end
67
+
68
+ def check(pairs, delim, msg)
69
+ pairs.each do |pair|
70
+ if pair.loc.operator && pair.loc.operator.is?(delim)
71
+ convention(pair,
72
+ pair.loc.expression.begin.join(pair.loc.operator),
73
+ msg)
74
+ end
75
+ end
76
+ end
77
+
67
78
  def word_symbol_pair?(pair)
68
79
  key, _value = *pair
69
80
 
@@ -19,6 +19,15 @@ module Rubocop
19
19
  end
20
20
  end
21
21
  end
22
+
23
+ def autocorrect(comment)
24
+ expr = comment.loc.expression
25
+ b = expr.begin_pos
26
+ hash_mark = Parser::Source::Range.new(expr.source_buffer, b, b + 1)
27
+ @corrections << lambda do |corrector|
28
+ corrector.insert_after(hash_mark, ' ')
29
+ end
30
+ end
22
31
  end
23
32
  end
24
33
  end
@@ -8,12 +8,22 @@ module Rubocop
8
8
  MSG = 'Do not use parentheses for method calls with no arguments.'
9
9
 
10
10
  def on_send(node)
11
- _receiver, _method_name, *args = *node
11
+ _receiver, method_name, *args = *node
12
+
13
+ # methods starting with a capital letter should be skipped
14
+ return if method_name =~ /\A[A-Z]/
12
15
 
13
16
  convention(node, :begin) if args.empty? && node.loc.begin
14
17
  end
15
18
 
16
19
  def autocorrect(node)
20
+ # Bail out if the call is going to be auto-corrected by EmptyLiteral.
21
+ if config.for_cop('EmptyLiteral')['Enabled'] &&
22
+ [EmptyLiteral::HASH_NODE,
23
+ EmptyLiteral::ARRAY_NODE,
24
+ EmptyLiteral::STR_NODE].include?(node)
25
+ fail CorrectionNotPossible
26
+ end
17
27
  @corrections << lambda do |corrector|
18
28
  corrector.remove(node.loc.begin)
19
29
  corrector.remove(node.loc.end)