rubocop 0.34.2 → 0.35.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 (102) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +86 -0
  3. data/README.md +103 -31
  4. data/config/default.yml +32 -2
  5. data/config/disabled.yml +24 -0
  6. data/config/enabled.yml +20 -2
  7. data/lib/rubocop.rb +13 -0
  8. data/lib/rubocop/ast_node.rb +48 -0
  9. data/lib/rubocop/cli.rb +9 -0
  10. data/lib/rubocop/config.rb +8 -6
  11. data/lib/rubocop/config_loader.rb +30 -8
  12. data/lib/rubocop/cop/commissioner.rb +1 -1
  13. data/lib/rubocop/cop/cop.rb +19 -6
  14. data/lib/rubocop/cop/lint/circular_argument_reference.rb +33 -2
  15. data/lib/rubocop/cop/lint/debugger.rb +9 -56
  16. data/lib/rubocop/cop/lint/end_alignment.rb +29 -9
  17. data/lib/rubocop/cop/lint/eval.rb +6 -2
  18. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +24 -6
  19. data/lib/rubocop/cop/lint/literal_in_condition.rb +0 -5
  20. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +10 -1
  21. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
  22. data/lib/rubocop/cop/lint/space_before_first_arg.rb +1 -1
  23. data/lib/rubocop/cop/lint/unused_block_argument.rb +6 -0
  24. data/lib/rubocop/cop/lint/unused_method_argument.rb +8 -0
  25. data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
  26. data/lib/rubocop/cop/mixin/access_modifier_node.rb +1 -1
  27. data/lib/rubocop/cop/mixin/autocorrect_alignment.rb +1 -1
  28. data/lib/rubocop/cop/mixin/autocorrect_unless_changing_ast.rb +26 -3
  29. data/lib/rubocop/cop/mixin/check_assignment.rb +2 -3
  30. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +59 -12
  31. data/lib/rubocop/cop/mixin/configurable_max.rb +1 -1
  32. data/lib/rubocop/cop/mixin/configurable_naming.rb +1 -1
  33. data/lib/rubocop/cop/mixin/first_element_line_break.rb +41 -0
  34. data/lib/rubocop/cop/mixin/negative_conditional.rb +1 -1
  35. data/lib/rubocop/cop/mixin/safe_assignment.rb +3 -14
  36. data/lib/rubocop/cop/mixin/statement_modifier.rb +2 -2
  37. data/lib/rubocop/cop/performance/detect.rb +5 -1
  38. data/lib/rubocop/cop/performance/fixed_size.rb +50 -0
  39. data/lib/rubocop/cop/performance/size.rb +1 -1
  40. data/lib/rubocop/cop/performance/string_replacement.rb +14 -8
  41. data/lib/rubocop/cop/rails/pluralization_grammar.rb +97 -0
  42. data/lib/rubocop/cop/style/align_hash.rb +1 -12
  43. data/lib/rubocop/cop/style/align_parameters.rb +19 -7
  44. data/lib/rubocop/cop/style/and_or.rb +42 -13
  45. data/lib/rubocop/cop/style/block_comments.rb +4 -2
  46. data/lib/rubocop/cop/style/block_delimiters.rb +57 -18
  47. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +1 -1
  48. data/lib/rubocop/cop/style/command_literal.rb +2 -10
  49. data/lib/rubocop/cop/style/copyright.rb +5 -3
  50. data/lib/rubocop/cop/style/documentation.rb +9 -6
  51. data/lib/rubocop/cop/style/dot_position.rb +6 -0
  52. data/lib/rubocop/cop/style/double_negation.rb +4 -15
  53. data/lib/rubocop/cop/style/each_with_object.rb +17 -4
  54. data/lib/rubocop/cop/style/empty_line_between_defs.rb +1 -5
  55. data/lib/rubocop/cop/style/encoding.rb +10 -4
  56. data/lib/rubocop/cop/style/extra_spacing.rb +23 -13
  57. data/lib/rubocop/cop/style/first_array_element_line_break.rb +41 -0
  58. data/lib/rubocop/cop/style/first_hash_element_line_break.rb +35 -0
  59. data/lib/rubocop/cop/style/first_method_argument_line_break.rb +37 -0
  60. data/lib/rubocop/cop/style/first_method_parameter_line_break.rb +42 -0
  61. data/lib/rubocop/cop/style/for.rb +2 -1
  62. data/lib/rubocop/cop/style/if_unless_modifier.rb +31 -0
  63. data/lib/rubocop/cop/style/indent_hash.rb +67 -37
  64. data/lib/rubocop/cop/style/indentation_width.rb +1 -1
  65. data/lib/rubocop/cop/style/leading_comment_space.rb +3 -2
  66. data/lib/rubocop/cop/style/method_call_parentheses.rb +8 -0
  67. data/lib/rubocop/cop/style/method_def_parentheses.rb +10 -7
  68. data/lib/rubocop/cop/style/multiline_operation_indentation.rb +8 -13
  69. data/lib/rubocop/cop/style/nested_modifier.rb +97 -0
  70. data/lib/rubocop/cop/style/next.rb +18 -0
  71. data/lib/rubocop/cop/style/parallel_assignment.rb +57 -15
  72. data/lib/rubocop/cop/style/predicate_name.rb +7 -2
  73. data/lib/rubocop/cop/style/regexp_literal.rb +2 -10
  74. data/lib/rubocop/cop/style/single_line_methods.rb +7 -5
  75. data/lib/rubocop/cop/style/single_space_before_first_arg.rb +1 -1
  76. data/lib/rubocop/cop/style/space_around_operators.rb +2 -0
  77. data/lib/rubocop/cop/style/special_global_vars.rb +4 -2
  78. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +108 -0
  79. data/lib/rubocop/cop/style/trailing_comma.rb +9 -6
  80. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +23 -2
  81. data/lib/rubocop/cop/style/unneeded_percent_q.rb +31 -20
  82. data/lib/rubocop/cop/style/variable_name.rb +5 -0
  83. data/lib/rubocop/cop/style/word_array.rb +2 -1
  84. data/lib/rubocop/cop/team.rb +17 -4
  85. data/lib/rubocop/cop/util.rb +5 -0
  86. data/lib/rubocop/cop/variable_force/locatable.rb +1 -1
  87. data/lib/rubocop/formatter/base_formatter.rb +1 -1
  88. data/lib/rubocop/formatter/disabled_config_formatter.rb +22 -10
  89. data/lib/rubocop/formatter/simple_text_formatter.rb +1 -1
  90. data/lib/rubocop/node_pattern.rb +390 -0
  91. data/lib/rubocop/options.rb +48 -36
  92. data/lib/rubocop/processed_source.rb +3 -1
  93. data/lib/rubocop/rake_task.rb +1 -1
  94. data/lib/rubocop/remote_config.rb +60 -0
  95. data/lib/rubocop/result_cache.rb +4 -2
  96. data/lib/rubocop/runner.rb +33 -10
  97. data/lib/rubocop/token.rb +2 -1
  98. data/lib/rubocop/version.rb +1 -1
  99. data/lib/rubocop/warning.rb +11 -0
  100. data/relnotes/v0.35.0.md +210 -0
  101. data/rubocop.gemspec +2 -2
  102. metadata +20 -6
@@ -6,6 +6,8 @@ module RuboCop
6
6
  # This cop looks for uses of block comments (=begin...=end).
7
7
  class BlockComments < Cop
8
8
  MSG = 'Do not use block comments.'
9
+ BEGIN_LENGTH = "=begin\n".length
10
+ END_LENGTH = "\n=end".length
9
11
 
10
12
  def investigate(processed_source)
11
13
  processed_source.comments.each do |comment|
@@ -33,9 +35,9 @@ module RuboCop
33
35
 
34
36
  def parts(comment)
35
37
  expr = comment.loc.expression
36
- eq_begin = expr.resize("=begin\n".length)
38
+ eq_begin = expr.resize(BEGIN_LENGTH)
37
39
  eq_end = Parser::Source::Range.new(expr.source_buffer,
38
- expr.end_pos - "\n=end".length,
40
+ expr.end_pos - END_LENGTH,
39
41
  expr.end_pos)
40
42
  contents = Parser::Source::Range.new(expr.source_buffer,
41
43
  eq_begin.end_pos,
@@ -25,32 +25,55 @@ module RuboCop
25
25
  def on_block(node)
26
26
  return if ignored_node?(node)
27
27
 
28
- if proper_block_style?(node)
29
- correct_style_detected
30
- else
31
- add_offense(node, :begin) { opposite_style_detected }
32
- end
28
+ return if proper_block_style?(node)
29
+
30
+ add_offense(node, :begin)
33
31
  end
34
32
 
35
33
  private
36
34
 
37
- def message(node)
35
+ def line_count_based_message(node)
36
+ block_length = Util.block_length(node)
37
+
38
+ if block_length > 0
39
+ 'Avoid using `{...}` for multi-line blocks.'
40
+ else
41
+ 'Prefer `{...}` over `do...end` for single-line blocks.'
42
+ end
43
+ end
44
+
45
+ def semantic_message(node)
38
46
  block_begin = node.loc.begin.source
47
+
48
+ if block_begin == '{'
49
+ 'Prefer `do...end` over `{...}` for procedural blocks.'
50
+ else
51
+ 'Prefer `{...}` over `do...end` for functional blocks.'
52
+ end
53
+ end
54
+
55
+ def braces_for_chaining_message(node)
39
56
  block_length = Util.block_length(node)
40
57
 
41
- case style
42
- when :line_count_based
43
- if block_length > 0
44
- 'Avoid using `{...}` for multi-line blocks.'
58
+ if block_length > 0
59
+ if return_value_chaining?(node)
60
+ 'Prefer `{...}` over `do...end` for multi-line chained blocks.'
45
61
  else
46
- 'Prefer `{...}` over `do...end` for single-line blocks.'
62
+ 'Prefer `do...end` for multi-line blocks without chaining.'
47
63
  end
64
+ else
65
+ 'Prefer `{...}` over `do...end` for single-line blocks.'
66
+ end
67
+ end
68
+
69
+ def message(node)
70
+ case style
71
+ when :line_count_based
72
+ line_count_based_message(node)
48
73
  when :semantic
49
- if block_begin == '{'
50
- 'Prefer `do...end` over `{...}` for procedural blocks.'
51
- else
52
- 'Prefer `{...}` over `do...end` for functional blocks.'
53
- end
74
+ semantic_message(node)
75
+ when :braces_for_chaining
76
+ braces_for_chaining_message(node)
54
77
  end
55
78
  end
56
79
 
@@ -90,6 +113,8 @@ module RuboCop
90
113
  line_count_based_block_style?(node)
91
114
  when :semantic
92
115
  semantic_block_style?(node)
116
+ when :braces_for_chaining
117
+ braces_for_chaining_style?(node)
93
118
  end
94
119
  end
95
120
 
@@ -117,6 +142,21 @@ module RuboCop
117
142
  end
118
143
  end
119
144
 
145
+ def braces_for_chaining_style?(node)
146
+ block_length = Util.block_length(node)
147
+ block_begin = node.loc.begin.source
148
+
149
+ if block_length > 0
150
+ block_begin == (return_value_chaining?(node) ? '{' : 'do')
151
+ else
152
+ block_begin == '{'
153
+ end
154
+ end
155
+
156
+ def return_value_chaining?(node)
157
+ node.parent && node.parent.send_type? && node.parent.loc.dot
158
+ end
159
+
120
160
  def extract_method_name_from_block(block)
121
161
  node, _args, _body = *block
122
162
  _receiver, method_name, *_args = *node
@@ -148,8 +188,7 @@ module RuboCop
148
188
  if node.parent.begin_type?
149
189
  return_value_used?(node.parent)
150
190
  else
151
- Util::ASGN_NODES.include?(node.parent.type) ||
152
- node.parent.send_type?
191
+ node.parent.assignment? || node.parent.send_type?
153
192
  end
154
193
  end
155
194
 
@@ -15,7 +15,7 @@ module RuboCop
15
15
  _receiver, method_name, *args = *node
16
16
 
17
17
  # Discard attr writer methods.
18
- return if method_name.to_s.end_with?('=')
18
+ return if node.asgn_method_call?
19
19
  # Discard operator methods.
20
20
  return if operator?(method_name)
21
21
 
@@ -47,7 +47,7 @@ module RuboCop
47
47
  def check_backtick_literal(node)
48
48
  return if style == :backticks && !contains_disallowed_backtick?(node)
49
49
  return if style == :mixed &&
50
- single_line?(node) &&
50
+ node.single_line? &&
51
51
  !contains_disallowed_backtick?(node)
52
52
 
53
53
  add_offense(node, :expression, MSG_USE_PERCENT_X)
@@ -56,7 +56,7 @@ module RuboCop
56
56
  def check_percent_x_literal(node)
57
57
  return if style == :backticks && contains_disallowed_backtick?(node)
58
58
  return if style == :percent_x
59
- return if style == :mixed && multi_line?(node)
59
+ return if style == :mixed && node.multiline?
60
60
  return if style == :mixed && contains_disallowed_backtick?(node)
61
61
 
62
62
  add_offense(node, :expression, MSG_USE_BACKTICKS)
@@ -87,14 +87,6 @@ module RuboCop
87
87
  node.loc.begin.source == '`'
88
88
  end
89
89
 
90
- def single_line?(node)
91
- !multi_line?(node)
92
- end
93
-
94
- def multi_line?(node)
95
- block_length(node) > 1
96
- end
97
-
98
90
  def preferred_delimiters
99
91
  config.for_cop('Style/PercentLiteralDelimiters') \
100
92
  ['PreferredDelimiters']['%x'].split(//)
@@ -68,10 +68,12 @@ module RuboCop
68
68
  end
69
69
 
70
70
  def autocorrect(token)
71
- fail 'An AutocorrectNotice must be defined in ' \
71
+ fail Warning, 'An AutocorrectNotice must be defined in ' \
72
72
  'your RuboCop config' if autocorrect_notice.empty?
73
- fail "AutocorrectNotice '#{autocorrect_notice}' must match " \
74
- "Notice /#{notice}/" unless autocorrect_notice =~ Regexp.new(notice)
73
+ regex = Regexp.new(notice)
74
+ fail Warning, "AutocorrectNotice '#{autocorrect_notice}' must " \
75
+ "match Notice /#{notice}/" unless autocorrect_notice =~ regex
76
+
75
77
  lambda do |corrector|
76
78
  if token.nil?
77
79
  range = Parser::Source::Range.new('', 0, 0)
@@ -47,18 +47,21 @@ module RuboCop
47
47
  end
48
48
  end
49
49
 
50
+ # 'Const = Class.new' or 'Const = Module.new' are module definitions
51
+ mod_new = '(send (const nil {:Class :Module}) :new ...)'
52
+ def_node_matcher :module_definition?,
53
+ "{class
54
+ module
55
+ (casgn _ _ {#{mod_new} (block #{mod_new} ...)})}"
56
+
50
57
  def namespace?(body_node)
51
58
  return false unless body_node
52
59
 
53
60
  case body_node.type
54
61
  when :begin
55
- body_node.children.all? do |node|
56
- [:class, :module].include?(node.type)
57
- end
58
- when :class, :module
59
- true
62
+ body_node.children.all? { |node| module_definition?(node) }
60
63
  else
61
- false
64
+ module_definition?(body_node)
62
65
  end
63
66
  end
64
67
 
@@ -46,6 +46,12 @@ module RuboCop
46
46
 
47
47
  dot_line = node.loc.dot.line
48
48
 
49
+ # don't register an offense if there is a line comment between
50
+ # the dot and the selector
51
+ # otherwise, we might break the code while "correcting" it
52
+ # (even if there is just an extra blank line, treat it the same)
53
+ return true if (selector_line - dot_line) > 1
54
+
49
55
  case style
50
56
  when :leading then dot_line == selector_line
51
57
  when :trailing then dot_line != selector_line
@@ -22,22 +22,11 @@ module RuboCop
22
22
  class DoubleNegation < Cop
23
23
  MSG = 'Avoid the use of double negation (`!!`).'
24
24
 
25
- def on_send(node)
26
- return unless not_node?(node)
27
-
28
- receiver, _method_name, *_args = *node
29
-
30
- add_offense(node, :selector) if not_node?(receiver)
31
- end
25
+ def_node_matcher :double_negative?, '(send (send _ :!) :!)'
32
26
 
33
- private
34
-
35
- def not_node?(node)
36
- _receiver, method_name, *args = *node
37
-
38
- # ! does not take any arguments
39
- args.empty? && method_name == :! &&
40
- node.loc.selector.is?('!')
27
+ def on_send(node)
28
+ return unless double_negative?(node) && node.loc.selector.is?('!')
29
+ add_offense(node, :selector)
41
30
  end
42
31
  end
43
32
  end
@@ -7,6 +7,9 @@ module RuboCop
7
7
  # returned at the end and so could be replace by each_with_object without
8
8
  # the need to return the object at the end.
9
9
  #
10
+ # However, we can't replace with each_with_object if the accumulator
11
+ # parameter is assigned to within the block.
12
+ #
10
13
  # @example
11
14
  # # bad
12
15
  # [1, 2].inject({}) { |a, e| a[e] = e; a }
@@ -23,16 +26,26 @@ module RuboCop
23
26
  # filter out super and zsuper nodes
24
27
  return unless method.type == :send
25
28
 
26
- _, method_name, method_args = *method
29
+ _, method_name, method_arg = *method
27
30
 
28
31
  return unless METHODS.include? method_name
29
- return if method_args && method_args.type == :sym
32
+ return if method_arg && BASIC_LITERALS.include?(method_arg.type)
30
33
 
31
34
  return_value = return_value(body)
32
35
  return unless return_value
33
36
 
34
37
  return unless first_argument_returned?(args, return_value)
35
38
 
39
+ # if the accumulator parameter is assigned to in the block,
40
+ # then we can't convert to each_with_object
41
+ first_arg, = *args
42
+ accumulator_var, = *first_arg
43
+ return if body.each_descendant.any? do |n|
44
+ next unless n.assignment?
45
+ lhs, _rhs = *n
46
+ lhs.equal?(accumulator_var)
47
+ end
48
+
36
49
  add_offense(method, :selector, format(MSG, method_name))
37
50
  end
38
51
 
@@ -47,8 +60,8 @@ module RuboCop
47
60
 
48
61
  def first_argument_returned?(args, return_value)
49
62
  first_arg, = *args
50
- accumulator_var = *first_arg
51
- return_var = *return_value
63
+ accumulator_var, = *first_arg
64
+ return_var, = *return_value
52
65
 
53
66
  accumulator_var == return_var
54
67
  end
@@ -16,7 +16,7 @@ module RuboCop
16
16
 
17
17
  return unless nodes.all?(&method(:def_node?))
18
18
  return if blank_lines_between?(*nodes)
19
- return if nodes.all?(&method(:single_line_def?)) &&
19
+ return if nodes.all?(&:single_line?) &&
20
20
  cop_config['AllowAdjacentOneLineDefs']
21
21
 
22
22
  add_offense(node, :keyword)
@@ -45,10 +45,6 @@ module RuboCop
45
45
  processed_source.lines[line_range]
46
46
  end
47
47
 
48
- def single_line_def?(node)
49
- def_start(node) == def_end(node)
50
- end
51
-
52
48
  def def_start(node)
53
49
  node.loc.keyword.line
54
50
  end
@@ -16,7 +16,9 @@ module RuboCop
16
16
 
17
17
  MSG_MISSING = 'Missing utf-8 encoding comment.'
18
18
  MSG_UNNECESSARY = 'Unnecessary utf-8 encoding comment.'
19
- ENCODING_PATTERN = /#.*coding\s?[:=]\s?(?:UTF|utf)-8/
19
+ ENCODING_PATTERN = /#.*coding\s?[:=]\s?(?:UTF|utf)-8/.freeze
20
+ AUTO_CORRECT_ENCODING_COMMENT = 'AutoCorrectEncodingComment'.freeze
21
+ SHEBANG = '#!'.freeze
20
22
 
21
23
  def investigate(processed_source)
22
24
  return if processed_source.buffer.source.empty?
@@ -31,10 +33,14 @@ module RuboCop
31
33
  end
32
34
 
33
35
  def autocorrect(node)
34
- encoding = cop_config['AutoCorrectEncodingComment']
36
+ encoding = cop_config[AUTO_CORRECT_ENCODING_COMMENT]
35
37
  if encoding && encoding =~ ENCODING_PATTERN
36
38
  lambda do |corrector|
37
- corrector.replace(node.pos, "#{encoding}\n#{node.pos.source}")
39
+ if encoding_line_number(processed_source) == 0
40
+ corrector.insert_before(node.pos, "#{encoding}\n")
41
+ else
42
+ corrector.insert_after(node.pos, "\n#{encoding}")
43
+ end
38
44
  end
39
45
  else
40
46
  fail "#{encoding} does not match #{ENCODING_PATTERN}"
@@ -58,7 +64,7 @@ module RuboCop
58
64
 
59
65
  def encoding_line_number(processed_source)
60
66
  line_number = 0
61
- line_number += 1 if processed_source[line_number].start_with?('#!')
67
+ line_number += 1 if processed_source[line_number].start_with?(SHEBANG)
62
68
  line_number
63
69
  end
64
70
  end
@@ -22,7 +22,6 @@ module RuboCop
22
22
 
23
23
  def investigate(processed_source)
24
24
  ast = processed_source.ast
25
- ignored_ranges = ast ? ignored_ranges(ast) : []
26
25
 
27
26
  processed_source.tokens.each_cons(2) do |t1, t2|
28
27
  next if t2.type == :tNL
@@ -30,10 +29,15 @@ module RuboCop
30
29
  next if t2.pos.begin_pos - 1 <= t1.pos.end_pos
31
30
  next if allow_for_alignment? && aligned_with_something?(t2)
32
31
  start_pos = t1.pos.end_pos
32
+ next if ignored_ranges(ast).find { |r| r.include?(start_pos) }
33
+
33
34
  end_pos = t2.pos.begin_pos - 1
34
35
  range = Parser::Source::Range.new(processed_source.buffer,
35
36
  start_pos, end_pos)
36
- add_offense(range, range, MSG) unless ignored_ranges.include?(range)
37
+ # Unary + doesn't appear as a token and needs special handling.
38
+ next if unary_plus_non_offense?(range)
39
+
40
+ add_offense(range, range, MSG)
37
41
  end
38
42
  end
39
43
 
@@ -43,19 +47,25 @@ module RuboCop
43
47
 
44
48
  private
45
49
 
50
+ def unary_plus_non_offense?(range)
51
+ range.resize(range.size + 1).source =~ /^ ?\+$/
52
+ end
53
+
46
54
  # Returns an array of ranges that should not be reported. It's the
47
- # extra spaces between the separators (: or =>) and values in a hash,
48
- # since those are handled by the Style/AlignHash cop.
55
+ # extra spaces between the keys and values in a hash, since those are
56
+ # handled by the Style/AlignHash cop.
49
57
  def ignored_ranges(ast)
50
- ranges = []
51
- on_node(:pair, ast) do |pair|
52
- _, value = *pair
53
- ranges <<
54
- Parser::Source::Range.new(processed_source.buffer,
55
- pair.loc.operator.end_pos,
56
- value.loc.expression.begin_pos - 1)
58
+ return [] unless ast
59
+
60
+ @ignored_ranges ||= begin
61
+ ranges = []
62
+ on_node(:pair, ast) do |pair|
63
+ key, value = *pair
64
+ r = key.loc.expression.end_pos...value.loc.expression.begin_pos
65
+ ranges << r
66
+ end
67
+ ranges
57
68
  end
58
- ranges
59
69
  end
60
70
 
61
71
  def allow_for_alignment?
@@ -122,7 +132,7 @@ module RuboCop
122
132
  whole_line_comments = processed_source.comments.select do |c|
123
133
  begins_its_line?(c.loc.expression)
124
134
  end
125
- whole_line_comments.map(&:loc).map(&:line)
135
+ whole_line_comments.map { |c| c.loc.line }
126
136
  end
127
137
  end
128
138
 
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks for a line break before the first element in a
7
+ # multi-line array.
8
+ #
9
+ # @example
10
+ #
11
+ # # bad
12
+ # [ :a,
13
+ # :b]
14
+ #
15
+ # # good
16
+ # [
17
+ # :a,
18
+ # :b]
19
+ #
20
+ class FirstArrayElementLineBreak < Cop
21
+ include FirstElementLineBreak
22
+
23
+ MSG = 'Add a line break before the first element of a ' \
24
+ 'multi-line array.'
25
+
26
+ def on_array(node)
27
+ return if !node.loc.begin && !assignment_on_same_line?(node)
28
+
29
+ check_children_line_break(node, node.children)
30
+ end
31
+
32
+ private
33
+
34
+ def assignment_on_same_line?(node)
35
+ source = node.loc.expression.source_line[0...node.loc.column]
36
+ source =~ /\s*\=\s*$/
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end