rubocop 0.48.1 → 0.49.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (184) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -3
  3. data/config/default.yml +397 -357
  4. data/config/disabled.yml +29 -29
  5. data/config/enabled.yml +366 -326
  6. data/lib/rubocop.rb +85 -70
  7. data/lib/rubocop/ast/builder.rb +4 -1
  8. data/lib/rubocop/ast/node.rb +2 -2
  9. data/lib/rubocop/ast/node/and_node.rb +1 -1
  10. data/lib/rubocop/ast/node/args_node.rb +24 -0
  11. data/lib/rubocop/ast/node/block_node.rb +107 -0
  12. data/lib/rubocop/ast/node/case_node.rb +1 -1
  13. data/lib/rubocop/ast/node/ensure_node.rb +1 -1
  14. data/lib/rubocop/ast/node/for_node.rb +1 -1
  15. data/lib/rubocop/ast/node/if_node.rb +1 -1
  16. data/lib/rubocop/ast/node/mixin/parameterized_node.rb +74 -0
  17. data/lib/rubocop/ast/node/or_node.rb +1 -1
  18. data/lib/rubocop/ast/node/pair_node.rb +1 -1
  19. data/lib/rubocop/ast/node/resbody_node.rb +1 -1
  20. data/lib/rubocop/ast/node/send_node.rb +36 -57
  21. data/lib/rubocop/ast/node/super_node.rb +42 -0
  22. data/lib/rubocop/ast/node/until_node.rb +1 -1
  23. data/lib/rubocop/ast/node/when_node.rb +1 -1
  24. data/lib/rubocop/ast/node/while_node.rb +1 -1
  25. data/lib/rubocop/cli.rb +10 -0
  26. data/lib/rubocop/config.rb +23 -7
  27. data/lib/rubocop/config_loader.rb +19 -3
  28. data/lib/rubocop/cop/badge.rb +1 -1
  29. data/lib/rubocop/cop/bundler/duplicated_gem.rb +2 -2
  30. data/lib/rubocop/cop/commissioner.rb +1 -1
  31. data/lib/rubocop/cop/cop.rb +10 -0
  32. data/lib/rubocop/cop/{style → layout}/access_modifier_indentation.rb +33 -3
  33. data/lib/rubocop/cop/{style → layout}/align_array.rb +16 -1
  34. data/lib/rubocop/cop/{style → layout}/align_hash.rb +1 -1
  35. data/lib/rubocop/cop/{style → layout}/align_parameters.rb +29 -1
  36. data/lib/rubocop/cop/{style → layout}/block_end_newline.rb +10 -5
  37. data/lib/rubocop/cop/{style → layout}/case_indentation.rb +64 -1
  38. data/lib/rubocop/cop/{style → layout}/closing_parenthesis_indentation.rb +2 -2
  39. data/lib/rubocop/cop/{style → layout}/comment_indentation.rb +1 -1
  40. data/lib/rubocop/cop/{style → layout}/dot_position.rb +1 -1
  41. data/lib/rubocop/cop/{style → layout}/else_alignment.rb +1 -1
  42. data/lib/rubocop/cop/{style → layout}/empty_line_after_magic_comment.rb +1 -1
  43. data/lib/rubocop/cop/{style → layout}/empty_line_between_defs.rb +1 -1
  44. data/lib/rubocop/cop/{style → layout}/empty_lines.rb +1 -1
  45. data/lib/rubocop/cop/{style → layout}/empty_lines_around_access_modifier.rb +2 -7
  46. data/lib/rubocop/cop/{style → layout}/empty_lines_around_begin_body.rb +1 -1
  47. data/lib/rubocop/cop/{style → layout}/empty_lines_around_block_body.rb +2 -4
  48. data/lib/rubocop/cop/{style → layout}/empty_lines_around_class_body.rb +1 -1
  49. data/lib/rubocop/cop/{style → layout}/empty_lines_around_exception_handling_keywords.rb +1 -1
  50. data/lib/rubocop/cop/{style → layout}/empty_lines_around_method_body.rb +1 -1
  51. data/lib/rubocop/cop/{style → layout}/empty_lines_around_module_body.rb +1 -1
  52. data/lib/rubocop/cop/{style → layout}/end_of_line.rb +1 -1
  53. data/lib/rubocop/cop/{style → layout}/extra_spacing.rb +1 -1
  54. data/lib/rubocop/cop/{style → layout}/first_array_element_line_break.rb +1 -1
  55. data/lib/rubocop/cop/{style → layout}/first_hash_element_line_break.rb +1 -1
  56. data/lib/rubocop/cop/{style → layout}/first_method_argument_line_break.rb +1 -1
  57. data/lib/rubocop/cop/{style → layout}/first_method_parameter_line_break.rb +1 -1
  58. data/lib/rubocop/cop/{style → layout}/first_parameter_indentation.rb +1 -1
  59. data/lib/rubocop/cop/{style → layout}/indent_array.rb +1 -1
  60. data/lib/rubocop/cop/{style → layout}/indent_assignment.rb +1 -1
  61. data/lib/rubocop/cop/{style → layout}/indent_hash.rb +2 -2
  62. data/lib/rubocop/cop/{style → layout}/indent_heredoc.rb +3 -3
  63. data/lib/rubocop/cop/{style → layout}/indentation_consistency.rb +1 -1
  64. data/lib/rubocop/cop/{style → layout}/indentation_width.rb +10 -12
  65. data/lib/rubocop/cop/{style → layout}/initial_indentation.rb +1 -1
  66. data/lib/rubocop/cop/{style → layout}/leading_comment_space.rb +1 -1
  67. data/lib/rubocop/cop/{style → layout}/multiline_array_brace_layout.rb +1 -1
  68. data/lib/rubocop/cop/{style → layout}/multiline_assignment_layout.rb +1 -1
  69. data/lib/rubocop/cop/{style → layout}/multiline_block_layout.rb +21 -36
  70. data/lib/rubocop/cop/{style → layout}/multiline_hash_brace_layout.rb +5 -1
  71. data/lib/rubocop/cop/{style → layout}/multiline_method_call_brace_layout.rb +1 -1
  72. data/lib/rubocop/cop/{style → layout}/multiline_method_call_indentation.rb +3 -3
  73. data/lib/rubocop/cop/{style → layout}/multiline_method_definition_brace_layout.rb +1 -1
  74. data/lib/rubocop/cop/{style → layout}/multiline_operation_indentation.rb +6 -5
  75. data/lib/rubocop/cop/{style → layout}/rescue_ensure_alignment.rb +1 -1
  76. data/lib/rubocop/cop/{style → layout}/space_after_colon.rb +2 -2
  77. data/lib/rubocop/cop/{style → layout}/space_after_comma.rb +2 -2
  78. data/lib/rubocop/cop/{style → layout}/space_after_method_name.rb +1 -1
  79. data/lib/rubocop/cop/{style → layout}/space_after_not.rb +1 -1
  80. data/lib/rubocop/cop/{style → layout}/space_after_semicolon.rb +2 -2
  81. data/lib/rubocop/cop/{style → layout}/space_around_block_parameters.rb +7 -5
  82. data/lib/rubocop/cop/{style → layout}/space_around_equals_in_parameter_default.rb +1 -1
  83. data/lib/rubocop/cop/{style → layout}/space_around_keyword.rb +1 -1
  84. data/lib/rubocop/cop/{style → layout}/space_around_operators.rb +6 -2
  85. data/lib/rubocop/cop/{style → layout}/space_before_block_braces.rb +6 -2
  86. data/lib/rubocop/cop/{style → layout}/space_before_comma.rb +1 -1
  87. data/lib/rubocop/cop/{style → layout}/space_before_comment.rb +1 -1
  88. data/lib/rubocop/cop/{style → layout}/space_before_first_arg.rb +4 -2
  89. data/lib/rubocop/cop/{style → layout}/space_before_semicolon.rb +1 -1
  90. data/lib/rubocop/cop/{style → layout}/space_in_lambda_literal.rb +1 -1
  91. data/lib/rubocop/cop/{style → layout}/space_inside_array_percent_literal.rb +1 -1
  92. data/lib/rubocop/cop/{style → layout}/space_inside_block_braces.rb +3 -4
  93. data/lib/rubocop/cop/{style → layout}/space_inside_brackets.rb +1 -1
  94. data/lib/rubocop/cop/{style → layout}/space_inside_hash_literal_braces.rb +1 -1
  95. data/lib/rubocop/cop/{style → layout}/space_inside_parens.rb +1 -1
  96. data/lib/rubocop/cop/{style → layout}/space_inside_percent_literal_delimiters.rb +8 -7
  97. data/lib/rubocop/cop/{style → layout}/space_inside_range_literal.rb +1 -1
  98. data/lib/rubocop/cop/{style → layout}/space_inside_string_interpolation.rb +1 -1
  99. data/lib/rubocop/cop/{style → layout}/tab.rb +1 -1
  100. data/lib/rubocop/cop/{style → layout}/trailing_blank_lines.rb +1 -1
  101. data/lib/rubocop/cop/{style → layout}/trailing_whitespace.rb +2 -2
  102. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  103. data/lib/rubocop/cop/lint/ambiguous_operator.rb +4 -4
  104. data/lib/rubocop/cop/lint/debugger.rb +0 -15
  105. data/lib/rubocop/cop/lint/duplicate_methods.rb +2 -1
  106. data/lib/rubocop/cop/lint/rescue_type.rb +81 -0
  107. data/lib/rubocop/cop/lint/script_permission.rb +42 -0
  108. data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -1
  109. data/lib/rubocop/cop/message_annotator.rb +23 -13
  110. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  111. data/lib/rubocop/cop/mixin/array_min_size.rb +59 -0
  112. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +10 -11
  113. data/lib/rubocop/cop/mixin/def_node.rb +1 -1
  114. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +1 -1
  115. data/lib/rubocop/cop/mixin/enforce_superclass.rb +36 -0
  116. data/lib/rubocop/cop/mixin/hash_alignment.rb +1 -1
  117. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +7 -3
  118. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  119. data/lib/rubocop/cop/performance/caller.rb +41 -0
  120. data/lib/rubocop/cop/performance/compare_with_block.rb +60 -14
  121. data/lib/rubocop/cop/performance/double_start_end_with.rb +2 -2
  122. data/lib/rubocop/cop/performance/redundant_merge.rb +2 -0
  123. data/lib/rubocop/cop/rails/action_filter.rb +1 -3
  124. data/lib/rubocop/cop/rails/application_job.rb +32 -0
  125. data/lib/rubocop/cop/rails/application_record.rb +32 -0
  126. data/lib/rubocop/cop/rails/blank.rb +9 -3
  127. data/lib/rubocop/cop/rails/output_safety.rb +59 -15
  128. data/lib/rubocop/cop/rails/present.rb +9 -3
  129. data/lib/rubocop/cop/rails/relative_date_constant.rb +35 -4
  130. data/lib/rubocop/cop/rails/reversible_migration.rb +82 -18
  131. data/lib/rubocop/cop/rails/save_bang.rb +7 -2
  132. data/lib/rubocop/cop/rails/skips_model_validations.rb +7 -0
  133. data/lib/rubocop/cop/registry.rb +4 -3
  134. data/lib/rubocop/cop/security/eval.rb +9 -3
  135. data/lib/rubocop/cop/style/and_or.rb +1 -1
  136. data/lib/rubocop/cop/style/block_delimiters.rb +11 -17
  137. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +1 -1
  138. data/lib/rubocop/cop/style/collection_methods.rb +1 -3
  139. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
  140. data/lib/rubocop/cop/style/copyright.rb +2 -2
  141. data/lib/rubocop/cop/style/documentation_method.rb +1 -1
  142. data/lib/rubocop/cop/style/each_for_simple_loop.rb +2 -1
  143. data/lib/rubocop/cop/style/each_with_object.rb +10 -6
  144. data/lib/rubocop/cop/style/empty_case_condition.rb +2 -2
  145. data/lib/rubocop/cop/style/for.rb +4 -5
  146. data/lib/rubocop/cop/style/format_string.rb +49 -0
  147. data/lib/rubocop/cop/style/format_string_token.rb +141 -0
  148. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  149. data/lib/rubocop/cop/style/identical_conditional_branches.rb +2 -2
  150. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +1 -1
  151. data/lib/rubocop/cop/style/inverse_methods.rb +10 -1
  152. data/lib/rubocop/cop/style/lambda.rb +9 -9
  153. data/lib/rubocop/cop/style/line_end_concatenation.rb +4 -0
  154. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +3 -3
  155. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +1 -2
  156. data/lib/rubocop/cop/style/method_name.rb +8 -2
  157. data/lib/rubocop/cop/style/mixin_grouping.rb +41 -3
  158. data/lib/rubocop/cop/style/multiline_block_chain.rb +7 -11
  159. data/lib/rubocop/cop/style/multiple_comparison.rb +77 -0
  160. data/lib/rubocop/cop/style/next.rb +11 -22
  161. data/lib/rubocop/cop/style/parallel_assignment.rb +10 -19
  162. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -2
  163. data/lib/rubocop/cop/style/self_assignment.rb +4 -0
  164. data/lib/rubocop/cop/style/single_line_block_params.rb +23 -17
  165. data/lib/rubocop/cop/style/symbol_array.rb +24 -13
  166. data/lib/rubocop/cop/style/symbol_proc.rb +4 -0
  167. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  168. data/lib/rubocop/cop/style/unneeded_interpolation.rb +4 -0
  169. data/lib/rubocop/cop/style/word_array.rb +33 -53
  170. data/lib/rubocop/cop/style/yoda_condition.rb +78 -0
  171. data/lib/rubocop/cop/team.rb +1 -14
  172. data/lib/rubocop/cop/util.rb +16 -0
  173. data/lib/rubocop/formatter/simple_text_formatter.rb +0 -11
  174. data/lib/rubocop/node_pattern.rb +52 -52
  175. data/lib/rubocop/options.rb +25 -0
  176. data/lib/rubocop/path_util.rb +17 -1
  177. data/lib/rubocop/result_cache.rb +8 -7
  178. data/lib/rubocop/rspec/expect_offense.rb +167 -0
  179. data/lib/rubocop/rspec/shared_examples.rb +0 -8
  180. data/lib/rubocop/rspec/support.rb +1 -0
  181. data/lib/rubocop/runner.rb +12 -2
  182. data/lib/rubocop/target_finder.rb +5 -0
  183. data/lib/rubocop/version.rb +1 -1
  184. metadata +101 -72
@@ -107,11 +107,16 @@ module RuboCop
107
107
 
108
108
  def persisted_referenced?(assignment)
109
109
  return unless assignment.referenced?
110
+
110
111
  assignment.variable.references.any? do |reference|
111
- reference.node.parent.method?(:persisted?)
112
+ call_to_persisted?(reference.node.parent)
112
113
  end
113
114
  end
114
115
 
116
+ def call_to_persisted?(node)
117
+ node.send_type? && node.method?(:persisted?)
118
+ end
119
+
115
120
  def check_used_in_conditional(node)
116
121
  return false unless conditional?(node)
117
122
 
@@ -130,7 +135,7 @@ module RuboCop
130
135
  end
131
136
 
132
137
  def last_call_of_method?(node)
133
- node.parent && node.parent.children.count == node.sibling_index + 1
138
+ node.parent && node.parent.children.size == node.sibling_index + 1
134
139
  end
135
140
 
136
141
  # Ignore simple assignment or if condition
@@ -22,6 +22,7 @@ module RuboCop
22
22
  #
23
23
  # # good
24
24
  # user.update_attributes(website: 'example.com')
25
+ # FileUtils.touch('file')
25
26
  class SkipsModelValidations < Cop
26
27
  MSG = 'Avoid using `%s` because it skips validations.'.freeze
27
28
 
@@ -36,6 +37,10 @@ module RuboCop
36
37
  update_columns
37
38
  update_counters].freeze
38
39
 
40
+ def_node_matcher :good_touch?, <<-PATTERN
41
+ (send (const nil :FileUtils) :touch ...)
42
+ PATTERN
43
+
39
44
  def on_send(node)
40
45
  return unless blacklist.include?(node.method_name.to_s)
41
46
 
@@ -45,6 +50,8 @@ module RuboCop
45
50
  return
46
51
  end
47
52
 
53
+ return if good_touch?(node)
54
+
48
55
  add_offense(node, :selector)
49
56
  end
50
57
 
@@ -62,17 +62,18 @@ module RuboCop
62
62
  # @example gives back a correctly qualified cop name
63
63
  #
64
64
  # cops = RuboCop::Cop::Cop.all
65
- # cops.qualified_cop_name('Style/IndentArray') # => 'Style/IndentArray'
65
+ # cops.
66
+ # qualified_cop_name('Layout/IndentArray') # => 'Layout/IndentArray'
66
67
  #
67
68
  # @example fixes incorrect namespaces
68
69
  #
69
70
  # cops = RuboCop::Cop::Cop.all
70
- # cops.qualified_cop_name('Lint/IndentArray') # => 'Style/IndentArray'
71
+ # cops.qualified_cop_name('Lint/IndentArray') # => 'Layout/IndentArray'
71
72
  #
72
73
  # @example namespaces bare cop identifiers
73
74
  #
74
75
  # cops = RuboCop::Cop::Cop.all
75
- # cops.qualified_cop_name('IndentArray') # => 'Style/IndentArray'
76
+ # cops.qualified_cop_name('IndentArray') # => 'Layout/IndentArray'
76
77
  #
77
78
  # @example passes back unrecognized cop names
78
79
  #
@@ -3,20 +3,26 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Security
6
- # This cop checks for the use of *Kernel#eval*.
6
+ # This cop checks for the use of `Kernel#eval` and `Binding#eval`.
7
7
  #
8
8
  # @example
9
9
  #
10
10
  # # bad
11
11
  #
12
12
  # eval(something)
13
+ # binding.eval(something)
13
14
  class Eval < Cop
14
15
  MSG = 'The use of `eval` is a serious security risk.'.freeze
15
16
 
16
- def_node_matcher :eval?, '(send nil :eval $!str ...)'
17
+ def_node_matcher :eval?, <<-END
18
+ (send {nil (send nil :binding)} :eval $!str ...)
19
+ END
17
20
 
18
21
  def on_send(node)
19
- eval?(node) { add_offense(node, :selector) }
22
+ eval?(node) do |code|
23
+ return if code.dstr_type? && code.recursive_literal?
24
+ add_offense(node, :selector)
25
+ end
20
26
  end
21
27
  end
22
28
  end
@@ -108,7 +108,7 @@ module RuboCop
108
108
  end
109
109
 
110
110
  def correctable_send?(node)
111
- !node.parenthesized? && node.arguments?
111
+ !node.parenthesized? && !node.method?(:[]) && node.arguments?
112
112
  end
113
113
 
114
114
  def whitespace_before_arg(node)
@@ -71,7 +71,7 @@ module RuboCop
71
71
  def autocorrect(node)
72
72
  return if correction_would_break_code?(node)
73
73
 
74
- if node.loc.begin.is?('{')
74
+ if node.braces?
75
75
  replace_braces_with_do_end(node.loc)
76
76
  else
77
77
  replace_do_end_with_braces(node.loc)
@@ -97,17 +97,18 @@ module RuboCop
97
97
 
98
98
  lambda do |corrector|
99
99
  corrector.insert_after(b, ' ') unless whitespace_after?(b, 2)
100
+
100
101
  corrector.replace(b, '{')
101
102
  corrector.replace(e, '}')
102
103
  end
103
104
  end
104
105
 
105
- def whitespace_before?(node)
106
- node.source_buffer.source[node.begin_pos - 1, 1] =~ /\s/
106
+ def whitespace_before?(range)
107
+ range.source_buffer.source[range.begin_pos - 1, 1] =~ /\s/
107
108
  end
108
109
 
109
- def whitespace_after?(node, length = 1)
110
- node.source_buffer.source[node.begin_pos + length, 1] =~ /\s/
110
+ def whitespace_after?(range, length = 1)
111
+ range.source_buffer.source[range.begin_pos + length, 1] =~ /\s/
111
112
  end
112
113
 
113
114
  def get_blocks(node, &block)
@@ -120,7 +121,7 @@ module RuboCop
120
121
  # A hash which is passed as method argument may have no braces
121
122
  # In that case, one of the K/V pairs could contain a block node
122
123
  # which could change in meaning if do...end replaced {...}
123
- return if node.loc.begin
124
+ return if node.braces?
124
125
  node.each_child_node { |child| get_blocks(child, &block) }
125
126
  when :pair
126
127
  node.each_child_node { |child| get_blocks(child, &block) }
@@ -139,16 +140,13 @@ module RuboCop
139
140
  end
140
141
 
141
142
  def line_count_based_block_style?(node)
142
- block_begin = node.loc.begin.source
143
-
144
- (block_length(node) > 0) ^ (block_begin == '{')
143
+ node.multiline? ^ node.braces?
145
144
  end
146
145
 
147
146
  def semantic_block_style?(node)
148
147
  method_name = node.method_name
149
- block_begin = node.loc.begin.source
150
148
 
151
- if block_begin == '{'
149
+ if node.braces?
152
150
  functional_method?(method_name) || functional_block?(node)
153
151
  else
154
152
  procedural_method?(method_name) || !return_value_used?(node)
@@ -171,13 +169,9 @@ module RuboCop
171
169
  end
172
170
 
173
171
  def correction_would_break_code?(node)
174
- return unless node.loc.begin.is?('do')
175
-
176
- # Converting `obj.method arg do |x| end` to use `{}` would cause
177
- # a syntax error.
178
- send = node.children.first
172
+ return unless node.keywords?
179
173
 
180
- send.arguments? && !send.parenthesized?
174
+ node.send_node.arguments? && !node.send_node.parenthesized?
181
175
  end
182
176
 
183
177
  def ignored_method?(method_name)
@@ -45,7 +45,7 @@ module RuboCop
45
45
  MSG = '%s curly braces around a hash parameter.'.freeze
46
46
 
47
47
  def on_send(node)
48
- return if node.asgn_method_call? || node.operator_method?
48
+ return if node.assignment_method? || node.operator_method?
49
49
 
50
50
  return unless node.arguments? && node.last_argument.hash_type? &&
51
51
  !node.last_argument.empty?
@@ -15,9 +15,7 @@ module RuboCop
15
15
  MSG = 'Prefer `%s` over `%s`.'.freeze
16
16
 
17
17
  def on_block(node)
18
- method, _args, _body = *node
19
-
20
- check_method_node(method)
18
+ check_method_node(node.send_node)
21
19
  end
22
20
 
23
21
  def on_send(node)
@@ -209,7 +209,7 @@ module RuboCop
209
209
  ASSIGNMENT_TYPES = VARIABLE_ASSIGNMENT_TYPES +
210
210
  %i[and_asgn or_asgn op_asgn masgn].freeze
211
211
  LINE_LENGTH = 'Metrics/LineLength'.freeze
212
- INDENTATION_WIDTH = 'Style/IndentationWidth'.freeze
212
+ INDENTATION_WIDTH = 'Layout/IndentationWidth'.freeze
213
213
  ENABLED = 'Enabled'.freeze
214
214
  MAX = 'Max'.freeze
215
215
  SINGLE_LINE_CONDITIONS_ONLY = 'SingleLineConditionsOnly'.freeze
@@ -8,8 +8,8 @@ module RuboCop
8
8
  # The default regexp for an acceptable copyright notice can be found in
9
9
  # config/default.yml. The default can be changed as follows:
10
10
  #
11
- # Style/Copyright:
12
- # Notice: '^Copyright (\(c\) )?2\d{3} Acme Inc'
11
+ # Style/Copyright:
12
+ # Notice: '^Copyright (\(c\) )?2\d{3} Acme Inc'
13
13
  #
14
14
  # This regex string is treated as an unanchored regex. For each file
15
15
  # that RuboCop scans, a comment that matches this regex must be found or
@@ -43,7 +43,7 @@ module RuboCop
43
43
  # end
44
44
  # end
45
45
  #
46
- # # Documenation
46
+ # # Documentation
47
47
  # def foo.bar
48
48
  # puts baz
49
49
  # end
@@ -29,7 +29,8 @@ module RuboCop
29
29
  def on_block(node)
30
30
  return unless offending_each_range(node)
31
31
 
32
- send_node, = *node
32
+ send_node = node.send_node
33
+
33
34
  range = send_node.receiver.source_range.join(send_node.loc.selector)
34
35
 
35
36
  add_offense(node, range)
@@ -38,20 +38,24 @@ module RuboCop
38
38
  end
39
39
  end
40
40
 
41
+ private
42
+
43
+ # rubocop:disable Metrics/AbcSize
41
44
  def autocorrect(node)
42
45
  lambda do |corrector|
43
- method, args, body = *node
44
- corrector.replace(method.loc.selector, 'each_with_object')
45
- first_arg, second_arg = *args
46
+ corrector.replace(node.send_node.loc.selector, 'each_with_object')
47
+
48
+ first_arg, second_arg = *node.arguments
49
+
46
50
  corrector.replace(first_arg.loc.expression, second_arg.source)
47
51
  corrector.replace(second_arg.loc.expression, first_arg.source)
48
52
 
49
- return_value = return_value(body)
53
+ return_value = return_value(node.body)
54
+
50
55
  corrector.remove(return_value.loc.expression)
51
56
  end
52
57
  end
53
-
54
- private
58
+ # rubocop:endable Metrics/AbcSize
55
59
 
56
60
  def simple_method_arg?(method_arg)
57
61
  method_arg && method_arg.basic_literal?
@@ -57,11 +57,11 @@ module RuboCop
57
57
  end
58
58
 
59
59
  def correct_case_when(corrector, case_node, when_nodes)
60
- case_range = case_node.loc.keyword.join(when_nodes.shift.loc.keyword)
60
+ case_range = case_node.loc.keyword.join(when_nodes.first.loc.keyword)
61
61
 
62
62
  corrector.replace(case_range, 'if')
63
63
 
64
- when_nodes.each do |when_node|
64
+ when_nodes[1..-1].each do |when_node|
65
65
  corrector.replace(when_node.loc.keyword, 'elsif')
66
66
  end
67
67
  end
@@ -22,14 +22,13 @@ module RuboCop
22
22
  end
23
23
 
24
24
  def on_block(node)
25
- return if block_length(node).zero?
25
+ return if node.single_line?
26
26
 
27
- method, _args, _body = *node
28
- return unless method.send_type? && method.method?(:each) &&
29
- !method.arguments?
27
+ return unless node.send_node.method?(:each) &&
28
+ !node.send_node.arguments?
30
29
 
31
30
  if style == :for
32
- incorrect_style_detected(method)
31
+ incorrect_style_detected(node.send_node)
33
32
  else
34
33
  correct_style_detected
35
34
  end
@@ -40,6 +40,55 @@ module RuboCop
40
40
  def method_name(style_name)
41
41
  style_name == :percent ? 'String#%' : style_name
42
42
  end
43
+
44
+ def autocorrect(node)
45
+ lambda do |corrector|
46
+ _receiver, detected_method = *node
47
+
48
+ case detected_method
49
+ when :%
50
+ autocorrect_from_percent(corrector, node)
51
+ when :format, :sprintf
52
+ case style
53
+ when :percent
54
+ autocorrect_to_percent(corrector, node)
55
+ when :format, :sprintf
56
+ corrector.replace(node.loc.selector, style.to_s)
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def autocorrect_from_percent(corrector, node)
65
+ receiver, _method, args = *node
66
+ format = receiver.source
67
+ args = if args.array_type? || args.hash_type?
68
+ args.children.map(&:source).join(', ')
69
+ else
70
+ args.source
71
+ end
72
+ corrected = "#{style}(#{format}, #{args})"
73
+ corrector.replace(node.loc.expression, corrected)
74
+ end
75
+
76
+ def autocorrect_to_percent(corrector, node)
77
+ _nil, _method, format, *args = *node
78
+ format = format.source
79
+ args = if args.one?
80
+ arg = args.first
81
+ if arg.hash_type?
82
+ "{ #{arg.source} }"
83
+ else
84
+ arg.source
85
+ end
86
+ else
87
+ "[#{args.map(&:source).join(', ')}]"
88
+ end
89
+ corrected = "#{format} % #{args}"
90
+ corrector.replace(node.loc.expression, corrected)
91
+ end
43
92
  end
44
93
  end
45
94
  end
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Use a consistent style for named format string tokens.
7
+ #
8
+ # @example
9
+ #
10
+ # EnforcedStyle: annotated
11
+ #
12
+ # # bad
13
+ #
14
+ # format('%{greeting}', greeting: 'Hello')
15
+ # format('%s', 'Hello')
16
+ #
17
+ # # good
18
+ #
19
+ # format('%<greeting>s', greeting: 'Hello')
20
+ #
21
+ # @example
22
+ #
23
+ # EnforcedStyle: template
24
+ #
25
+ # # bad
26
+ #
27
+ # format('%<greeting>s', greeting: 'Hello')
28
+ # format('%s', 'Hello')
29
+ #
30
+ # # good
31
+ #
32
+ # format('%{greeting}', greeting: 'Hello')
33
+ class FormatStringToken < Cop
34
+ include ConfigurableEnforcedStyle
35
+
36
+ FIELD_CHARACTERS = Regexp.union(%w[A B E G X a b c d e f g i o p s u x])
37
+
38
+ STYLE_PATTERNS = {
39
+ annotated: /(?<token>%<[^>]+>#{FIELD_CHARACTERS})/,
40
+ template: /(?<token>%\{[^\}]+\})/
41
+ }.freeze
42
+
43
+ TOKEN_PATTERN = Regexp.union(STYLE_PATTERNS.values)
44
+
45
+ def on_str(node)
46
+ return if node.each_ancestor(:xstr).any?
47
+
48
+ tokens(node) do |detected_style, token_range|
49
+ if detected_style == style
50
+ correct_style_detected
51
+ else
52
+ style_detected(detected_style)
53
+ add_offense(node, token_range, message(detected_style))
54
+ end
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def message(detected_style)
61
+ "Prefer #{message_text(style)} over #{message_text(detected_style)}."
62
+ end
63
+
64
+ # rubocop:disable FormatStringToken
65
+ def message_text(style)
66
+ case style
67
+ when :annotated then 'annotated tokens (like `%<foo>s`)'
68
+ when :template then 'template tokens (like `%{foo}`)'
69
+ end
70
+ end
71
+ # rubocop:enable FormatStringToken
72
+
73
+ def tokens(str_node, &block)
74
+ return if str_node.source == '__FILE__'
75
+
76
+ token_ranges(str_contents(str_node.loc), &block)
77
+ end
78
+
79
+ def str_contents(source_map)
80
+ if source_map.is_a?(Parser::Source::Map::Heredoc)
81
+ source_map.heredoc_body
82
+ elsif source_map.begin
83
+ slice_source(
84
+ source_map.expression,
85
+ source_map.expression.begin_pos + 1,
86
+ source_map.expression.end_pos - 1
87
+ )
88
+ else
89
+ source_map.expression
90
+ end
91
+ end
92
+
93
+ def token_ranges(contents)
94
+ while (offending_match = match_token(contents))
95
+ detected_style, *range = *offending_match
96
+ token, contents = split_token(contents, *range)
97
+ yield(detected_style, token)
98
+ end
99
+ end
100
+
101
+ def match_token(source_range)
102
+ supported_styles.each do |style_name|
103
+ pattern = STYLE_PATTERNS.fetch(style_name)
104
+ match = source_range.source.match(pattern)
105
+ next unless match
106
+
107
+ return [style_name, match.begin(:token), match.end(:token)]
108
+ end
109
+
110
+ nil
111
+ end
112
+
113
+ def split_token(source_range, match_begin, match_end)
114
+ token =
115
+ slice_source(
116
+ source_range,
117
+ source_range.begin_pos + match_begin,
118
+ source_range.begin_pos + match_end
119
+ )
120
+
121
+ remainder =
122
+ slice_source(
123
+ source_range,
124
+ source_range.begin_pos + match_end,
125
+ source_range.end_pos
126
+ )
127
+
128
+ [token, remainder]
129
+ end
130
+
131
+ def slice_source(source_range, new_begin, new_end)
132
+ Parser::Source::Range.new(
133
+ source_range.source_buffer,
134
+ new_begin,
135
+ new_end
136
+ )
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end