rubocop 0.58.2 → 0.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +22 -7
  4. data/config/disabled.yml +33 -4
  5. data/config/enabled.yml +4 -11
  6. data/lib/rubocop.rb +5 -0
  7. data/lib/rubocop/ast/builder.rb +1 -0
  8. data/lib/rubocop/ast/node.rb +11 -33
  9. data/lib/rubocop/ast/node/block_node.rb +8 -1
  10. data/lib/rubocop/ast/node/defined_node.rb +13 -0
  11. data/lib/rubocop/ast/node/mixin/method_dispatch_node.rb +16 -5
  12. data/lib/rubocop/ast/node/mixin/method_identifier_predicates.rb +21 -0
  13. data/lib/rubocop/ast/node/send_node.rb +3 -12
  14. data/lib/rubocop/ast/traversal.rb +10 -0
  15. data/lib/rubocop/cli.rb +4 -1
  16. data/lib/rubocop/config.rb +21 -5
  17. data/lib/rubocop/config_loader.rb +2 -0
  18. data/lib/rubocop/config_loader_resolver.rb +3 -1
  19. data/lib/rubocop/cop/autocorrect_logic.rb +1 -0
  20. data/lib/rubocop/cop/bundler/gem_comment.rb +64 -0
  21. data/lib/rubocop/cop/bundler/ordered_gems.rb +2 -0
  22. data/lib/rubocop/cop/commissioner.rb +2 -0
  23. data/lib/rubocop/cop/cop.rb +3 -0
  24. data/lib/rubocop/cop/corrector.rb +2 -0
  25. data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -0
  26. data/lib/rubocop/cop/correctors/line_break_corrector.rb +2 -0
  27. data/lib/rubocop/cop/correctors/space_corrector.rb +2 -0
  28. data/lib/rubocop/cop/force.rb +1 -0
  29. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -0
  30. data/lib/rubocop/cop/generator.rb +1 -0
  31. data/lib/rubocop/cop/layout/access_modifier_indentation.rb +1 -0
  32. data/lib/rubocop/cop/layout/class_structure.rb +4 -0
  33. data/lib/rubocop/cop/layout/closing_heredoc_indentation.rb +5 -4
  34. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +35 -0
  35. data/lib/rubocop/cop/layout/else_alignment.rb +1 -0
  36. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +2 -0
  37. data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +1 -0
  38. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +5 -2
  39. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +1 -0
  40. data/lib/rubocop/cop/layout/end_of_line.rb +1 -0
  41. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -0
  42. data/lib/rubocop/cop/layout/indent_array.rb +1 -0
  43. data/lib/rubocop/cop/layout/indent_heredoc.rb +3 -0
  44. data/lib/rubocop/cop/layout/indentation_width.rb +2 -0
  45. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +1 -0
  46. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -1
  47. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +34 -11
  48. data/lib/rubocop/cop/layout/space_after_method_name.rb +1 -0
  49. data/lib/rubocop/cop/layout/space_after_not.rb +1 -1
  50. data/lib/rubocop/cop/layout/space_around_keyword.rb +3 -1
  51. data/lib/rubocop/cop/layout/space_around_operators.rb +1 -0
  52. data/lib/rubocop/cop/layout/space_before_comment.rb +1 -0
  53. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +16 -8
  54. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +2 -0
  55. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +1 -1
  56. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +2 -0
  57. data/lib/rubocop/cop/layout/tab.rb +1 -0
  58. data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -0
  59. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +1 -0
  60. data/lib/rubocop/cop/lint/duplicate_methods.rb +9 -1
  61. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +1 -0
  62. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +1 -0
  63. data/lib/rubocop/cop/lint/interpolation_check.rb +2 -0
  64. data/lib/rubocop/cop/lint/literal_as_condition.rb +3 -6
  65. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -0
  66. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -0
  67. data/lib/rubocop/cop/lint/rescue_exception.rb +1 -0
  68. data/lib/rubocop/cop/lint/rescue_type.rb +1 -0
  69. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +2 -2
  70. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +2 -0
  71. data/lib/rubocop/cop/lint/script_permission.rb +1 -0
  72. data/lib/rubocop/cop/lint/shadowed_argument.rb +3 -0
  73. data/lib/rubocop/cop/lint/shadowed_exception.rb +2 -0
  74. data/lib/rubocop/cop/lint/unneeded_cop_disable_directive.rb +1 -0
  75. data/lib/rubocop/cop/lint/unneeded_cop_enable_directive.rb +1 -0
  76. data/lib/rubocop/cop/lint/unneeded_require_statement.rb +1 -0
  77. data/lib/rubocop/cop/lint/unneeded_splat_expansion.rb +1 -1
  78. data/lib/rubocop/cop/lint/unreachable_code.rb +2 -0
  79. data/lib/rubocop/cop/lint/useless_assignment.rb +1 -0
  80. data/lib/rubocop/cop/lint/useless_setter_call.rb +3 -0
  81. data/lib/rubocop/cop/lint/void.rb +1 -0
  82. data/lib/rubocop/cop/message_annotator.rb +1 -0
  83. data/lib/rubocop/cop/metrics/block_length.rb +1 -0
  84. data/lib/rubocop/cop/metrics/block_nesting.rb +1 -0
  85. data/lib/rubocop/cop/metrics/line_length.rb +6 -1
  86. data/lib/rubocop/cop/metrics/method_length.rb +1 -0
  87. data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -0
  88. data/lib/rubocop/cop/mixin/classish_length.rb +1 -0
  89. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +1 -0
  90. data/lib/rubocop/cop/mixin/configurable_formatting.rb +1 -0
  91. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +12 -6
  92. data/lib/rubocop/cop/mixin/empty_parameter.rb +1 -0
  93. data/lib/rubocop/cop/mixin/ignored_methods.rb +19 -0
  94. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +25 -1
  95. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +5 -3
  96. data/lib/rubocop/cop/mixin/percent_literal.rb +2 -0
  97. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +2 -0
  98. data/lib/rubocop/cop/mixin/safe_assignment.rb +2 -1
  99. data/lib/rubocop/cop/mixin/statement_modifier.rb +6 -1
  100. data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -0
  101. data/lib/rubocop/cop/mixin/surrounding_space.rb +4 -0
  102. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -0
  103. data/lib/rubocop/cop/mixin/uncommunicative_name.rb +2 -0
  104. data/lib/rubocop/cop/naming/ascii_identifiers.rb +1 -0
  105. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +1 -0
  106. data/lib/rubocop/cop/naming/file_name.rb +4 -1
  107. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -0
  108. data/lib/rubocop/cop/naming/predicate_name.rb +1 -0
  109. data/lib/rubocop/cop/naming/uncommunicative_block_param_name.rb +1 -0
  110. data/lib/rubocop/cop/naming/uncommunicative_method_param_name.rb +1 -0
  111. data/lib/rubocop/cop/naming/variable_name.rb +1 -0
  112. data/lib/rubocop/cop/performance/case_when_splat.rb +11 -7
  113. data/lib/rubocop/cop/performance/casecmp.rb +33 -42
  114. data/lib/rubocop/cop/performance/chain_array_allocation.rb +77 -0
  115. data/lib/rubocop/cop/performance/compare_with_block.rb +3 -0
  116. data/lib/rubocop/cop/performance/regexp_match.rb +1 -0
  117. data/lib/rubocop/cop/performance/sample.rb +2 -0
  118. data/lib/rubocop/cop/performance/size.rb +8 -2
  119. data/lib/rubocop/cop/performance/string_replacement.rb +1 -0
  120. data/lib/rubocop/cop/rails/active_support_aliases.rb +1 -0
  121. data/lib/rubocop/cop/rails/bulk_change_table.rb +9 -2
  122. data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +1 -0
  123. data/lib/rubocop/cop/rails/delegate.rb +7 -2
  124. data/lib/rubocop/cop/rails/dynamic_find_by.rb +1 -0
  125. data/lib/rubocop/cop/rails/find_each.rb +7 -2
  126. data/lib/rubocop/cop/rails/http_positional_arguments.rb +1 -1
  127. data/lib/rubocop/cop/rails/http_status.rb +2 -0
  128. data/lib/rubocop/cop/rails/inverse_of.rb +4 -0
  129. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +1 -0
  130. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +1 -0
  131. data/lib/rubocop/cop/rails/reversible_migration.rb +1 -0
  132. data/lib/rubocop/cop/rails/save_bang.rb +189 -38
  133. data/lib/rubocop/cop/rails/time_zone.rb +1 -0
  134. data/lib/rubocop/cop/security/eval.rb +1 -0
  135. data/lib/rubocop/cop/security/json_load.rb +2 -2
  136. data/lib/rubocop/cop/security/open.rb +6 -3
  137. data/lib/rubocop/cop/severity.rb +1 -0
  138. data/lib/rubocop/cop/style/and_or.rb +3 -3
  139. data/lib/rubocop/cop/style/ascii_comments.rb +1 -0
  140. data/lib/rubocop/cop/style/block_delimiters.rb +2 -4
  141. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +2 -3
  142. data/lib/rubocop/cop/style/class_and_module_children.rb +3 -0
  143. data/lib/rubocop/cop/style/class_vars.rb +1 -1
  144. data/lib/rubocop/cop/style/colon_method_definition.rb +1 -0
  145. data/lib/rubocop/cop/style/commented_keyword.rb +2 -0
  146. data/lib/rubocop/cop/style/conditional_assignment.rb +2 -0
  147. data/lib/rubocop/cop/style/copyright.rb +7 -2
  148. data/lib/rubocop/cop/style/date_time.rb +40 -7
  149. data/lib/rubocop/cop/style/double_negation.rb +1 -1
  150. data/lib/rubocop/cop/style/empty_case_condition.rb +8 -0
  151. data/lib/rubocop/cop/style/empty_else.rb +2 -0
  152. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -0
  153. data/lib/rubocop/cop/style/eval_with_location.rb +2 -0
  154. data/lib/rubocop/cop/style/for.rb +56 -10
  155. data/lib/rubocop/cop/style/format_string_token.rb +1 -1
  156. data/lib/rubocop/cop/style/if_with_semicolon.rb +1 -0
  157. data/lib/rubocop/cop/style/inverse_methods.rb +1 -0
  158. data/lib/rubocop/cop/style/lambda.rb +1 -0
  159. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +3 -5
  160. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +3 -5
  161. data/lib/rubocop/cop/style/method_def_parentheses.rb +2 -2
  162. data/lib/rubocop/cop/style/missing_else.rb +1 -0
  163. data/lib/rubocop/cop/style/multiline_memoization.rb +1 -0
  164. data/lib/rubocop/cop/style/multiline_method_signature.rb +65 -0
  165. data/lib/rubocop/cop/style/multiple_comparison.rb +1 -0
  166. data/lib/rubocop/cop/style/nil_comparison.rb +45 -5
  167. data/lib/rubocop/cop/style/not.rb +1 -1
  168. data/lib/rubocop/cop/style/numeric_predicate.rb +5 -0
  169. data/lib/rubocop/cop/style/one_line_conditional.rb +1 -1
  170. data/lib/rubocop/cop/style/or_assignment.rb +2 -0
  171. data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
  172. data/lib/rubocop/cop/style/random_with_offset.rb +1 -0
  173. data/lib/rubocop/cop/style/redundant_begin.rb +13 -0
  174. data/lib/rubocop/cop/style/redundant_conditional.rb +1 -0
  175. data/lib/rubocop/cop/style/redundant_parentheses.rb +6 -1
  176. data/lib/rubocop/cop/style/redundant_return.rb +1 -0
  177. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -0
  178. data/lib/rubocop/cop/style/rescue_standard_error.rb +1 -0
  179. data/lib/rubocop/cop/style/safe_navigation.rb +4 -0
  180. data/lib/rubocop/cop/style/semicolon.rb +4 -0
  181. data/lib/rubocop/cop/style/signal_exception.rb +1 -0
  182. data/lib/rubocop/cop/style/string_hash_keys.rb +1 -0
  183. data/lib/rubocop/cop/style/symbol_proc.rb +1 -8
  184. data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +1 -0
  185. data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -0
  186. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +1 -0
  187. data/lib/rubocop/cop/style/unneeded_condition.rb +13 -2
  188. data/lib/rubocop/cop/style/unneeded_percent_q.rb +2 -0
  189. data/lib/rubocop/cop/style/word_array.rb +13 -1
  190. data/lib/rubocop/cop/team.rb +1 -0
  191. data/lib/rubocop/cop/variable_force.rb +5 -0
  192. data/lib/rubocop/cop/variable_force/assignment.rb +4 -0
  193. data/lib/rubocop/cop/variable_force/branch.rb +4 -0
  194. data/lib/rubocop/cop/variable_force/branchable.rb +2 -0
  195. data/lib/rubocop/cop/variable_force/scope.rb +6 -0
  196. data/lib/rubocop/cop/variable_force/variable_table.rb +1 -0
  197. data/lib/rubocop/file_finder.rb +2 -0
  198. data/lib/rubocop/formatter/disabled_config_formatter.rb +4 -4
  199. data/lib/rubocop/formatter/file_list_formatter.rb +1 -0
  200. data/lib/rubocop/formatter/simple_text_formatter.rb +1 -0
  201. data/lib/rubocop/options.rb +16 -0
  202. data/lib/rubocop/path_util.rb +16 -1
  203. data/lib/rubocop/processed_source.rb +4 -0
  204. data/lib/rubocop/remote_config.rb +6 -1
  205. data/lib/rubocop/result_cache.rb +1 -0
  206. data/lib/rubocop/rspec/cop_helper.rb +3 -5
  207. data/lib/rubocop/rspec/shared_examples.rb +1 -9
  208. data/lib/rubocop/runner.rb +4 -0
  209. data/lib/rubocop/target_finder.rb +2 -0
  210. data/lib/rubocop/version.rb +1 -1
  211. metadata +7 -2
@@ -165,6 +165,7 @@ module RuboCop
165
165
 
166
166
  while node && node.send_type?
167
167
  break if extract_method(node) == :localtime
168
+
168
169
  node = node.parent
169
170
  end
170
171
 
@@ -21,6 +21,7 @@ module RuboCop
21
21
  def on_send(node)
22
22
  eval?(node) do |code|
23
23
  return if code.dstr_type? && code.recursive_literal?
24
+
24
25
  add_offense(node, location: :selector)
25
26
  end
26
27
  end
@@ -15,11 +15,11 @@ module RuboCop
15
15
  # Other similar issues may apply.
16
16
  #
17
17
  # @example
18
- # # always offense
18
+ # # bad
19
19
  # JSON.load("{}")
20
20
  # JSON.restore("{}")
21
21
  #
22
- # # no offense
22
+ # # good
23
23
  # JSON.parse("{}")
24
24
  #
25
25
  class JSONLoad < Cop
@@ -4,11 +4,12 @@ module RuboCop
4
4
  module Cop
5
5
  module Security
6
6
  # This cop checks for the use of `Kernel#open`.
7
+ #
7
8
  # `Kernel#open` enables not only file access but also process invocation
8
- # by prefixing a pipe symbol (e.g., `open("| ls")`). So, it may lead to
9
+ # by prefixing a pipe symbol (e.g., `open("| ls")`). So, it may lead to
9
10
  # a serious security risk by using variable input to the argument of
10
- # `Kernel#open`. It would be better to use `File.open` or `IO.popen`
11
- # explicitly.
11
+ # `Kernel#open`. It would be better to use `File.open`, `IO.popen` or
12
+ # `URI#open` explicitly.
12
13
  #
13
14
  # @example
14
15
  # # bad
@@ -17,6 +18,7 @@ module RuboCop
17
18
  # # good
18
19
  # File.open(something)
19
20
  # IO.popen(something)
21
+ # URI.parse(something).open
20
22
  class Open < Cop
21
23
  MSG = 'The use of `Kernel#open` is a serious security risk.'.freeze
22
24
 
@@ -39,6 +41,7 @@ module RuboCop
39
41
  def on_send(node)
40
42
  open?(node) do |code|
41
43
  return if safe?(code)
44
+
42
45
  add_offense(node, location: :selector)
43
46
  end
44
47
  end
@@ -34,6 +34,7 @@ module RuboCop
34
34
  unless NAMES.include?(name)
35
35
  raise ArgumentError, "Unknown severity: #{name}"
36
36
  end
37
+
37
38
  @name = name.freeze
38
39
  freeze
39
40
  end
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # This cop checks for uses of `and` and `or`, and suggests using `&&` and
7
- # `|| instead`. It can be configured to check only in conditions, or in
7
+ # `||` instead. It can be configured to check only in conditions, or in
8
8
  # all contexts.
9
9
  #
10
10
  # @example EnforcedStyle: always (default)
@@ -110,11 +110,11 @@ module RuboCop
110
110
  # recurse down a level and add parens to 'obj.method arg'
111
111
  # however, 'not x' also parses as (send x :!)
112
112
  def correct_not(node, receiver, corrector)
113
- if node.keyword_bang?
113
+ if node.prefix_bang?
114
114
  return unless receiver.send_type?
115
115
 
116
116
  correct_send(receiver, corrector)
117
- elsif node.keyword_not?
117
+ elsif node.prefix_not?
118
118
  correct_other(node, corrector)
119
119
  else
120
120
  raise 'unrecognized unary negation operator'
@@ -24,6 +24,7 @@ module RuboCop
24
24
  processed_source.each_comment do |comment|
25
25
  next if comment.text.ascii_only?
26
26
  next if only_allowed_non_ascii_chars?(comment.text)
27
+
27
28
  add_offense(comment, location: first_offense_range(comment))
28
29
  end
29
30
  end
@@ -73,6 +73,7 @@ module RuboCop
73
73
  #
74
74
  class BlockDelimiters < Cop
75
75
  include ConfigurableEnforcedStyle
76
+ include IgnoredMethods
76
77
 
77
78
  def on_send(node)
78
79
  return unless node.arguments?
@@ -189,6 +190,7 @@ module RuboCop
189
190
  # In that case, one of the K/V pairs could contain a block node
190
191
  # which could change in meaning if do...end replaced {...}
191
192
  return if node.braces?
193
+
192
194
  node.each_child_node { |child| get_blocks(child, &block) }
193
195
  when :pair
194
196
  node.each_child_node { |child| get_blocks(child, &block) }
@@ -240,10 +242,6 @@ module RuboCop
240
242
  node.send_node.arguments? && !node.send_node.parenthesized?
241
243
  end
242
244
 
243
- def ignored_method?(method_name)
244
- cop_config['IgnoredMethods'].map(&:to_sym).include?(method_name)
245
- end
246
-
247
245
  def functional_method?(method_name)
248
246
  cop_config['FunctionalMethods'].map(&:to_sym).include?(method_name)
249
247
  end
@@ -127,13 +127,12 @@ module RuboCop
127
127
  end
128
128
 
129
129
  def remove_braces_with_whitespace(corrector, node, space)
130
- right_brace_and_space = right_brace_and_space(node.loc.end, space)
131
-
132
130
  if node.multiline?
133
131
  remove_braces_with_range(corrector,
134
132
  left_whole_line_range(node.loc.begin),
135
133
  right_whole_line_range(node.loc.end))
136
134
  else
135
+ right_brace_and_space = right_brace_and_space(node.loc.end, space)
137
136
  left_brace_and_space = left_brace_and_space(node.loc.begin, space)
138
137
  remove_braces_with_range(corrector,
139
138
  left_brace_and_space,
@@ -155,7 +154,7 @@ module RuboCop
155
154
  end
156
155
 
157
156
  def right_whole_line_range(loc_end)
158
- if range_by_whole_lines(loc_end).source.strip == '}'
157
+ if range_by_whole_lines(loc_end).source.strip =~ /}\s*,?\z/
159
158
  range_by_whole_lines(loc_end, include_final_newline: true)
160
159
  else
161
160
  loc_end
@@ -33,6 +33,7 @@ module RuboCop
33
33
  def on_class(node)
34
34
  _name, superclass, body = *node
35
35
  return if superclass && style != :nested
36
+
36
37
  check_style(node, body)
37
38
  end
38
39
 
@@ -129,11 +130,13 @@ module RuboCop
129
130
 
130
131
  def check_nested_style(node)
131
132
  return unless compact_node_name?(node)
133
+
132
134
  add_offense(node, location: :name, message: NESTED_MSG)
133
135
  end
134
136
 
135
137
  def check_compact_style(node, body)
136
138
  return unless one_child?(body) && !compact_node_name?(node)
139
+
137
140
  add_offense(node, location: :name, message: COMPACT_MSG)
138
141
  end
139
142
 
@@ -26,7 +26,7 @@ module RuboCop
26
26
  #
27
27
  # class A
28
28
  # def test
29
- # @@test # you can access class variable without offence
29
+ # @@test # you can access class variable without offense
30
30
  # end
31
31
  # end
32
32
  #
@@ -24,6 +24,7 @@ module RuboCop
24
24
 
25
25
  def on_defs(node)
26
26
  return unless node.loc.operator.source == '::'
27
+
27
28
  add_offense(node, location: :operator)
28
29
  end
29
30
 
@@ -47,6 +47,7 @@ module RuboCop
47
47
  line = processed_source.lines[line_position - 1]
48
48
  next if heredoc_lines.any? { |r| r.include?(line_position) }
49
49
  next unless offensive?(line)
50
+
50
51
  range = source_range(processed_source.buffer,
51
52
  line_position,
52
53
  (location.column)...(location.last_column))
@@ -74,6 +75,7 @@ module RuboCop
74
75
 
75
76
  def extract_heredoc_lines(ast)
76
77
  return [] unless ast
78
+
77
79
  ast.each_node(:str, :dstr, :xstr).select(&:heredoc?).map do |node|
78
80
  body = node.location.heredoc_body
79
81
  (body.first_line...body.last_line)
@@ -69,6 +69,7 @@ module RuboCop
69
69
 
70
70
  def expand_elsif(node, elsif_branches = [])
71
71
  return [] if node.nil? || !node.if_type?
72
+
72
73
  _condition, elsif_branch, else_branch = *node
73
74
  elsif_branches << elsif_branch
74
75
  if else_branch && else_branch.if_type?
@@ -100,6 +101,7 @@ module RuboCop
100
101
  def assignment_rhs_exist?(node)
101
102
  parent = node.parent
102
103
  return true unless parent
104
+
103
105
  !(parent.mlhs_type? || parent.resbody_type?)
104
106
  end
105
107
  end
@@ -6,12 +6,12 @@ module RuboCop
6
6
  # Check that a copyright notice was given in each source file.
7
7
  #
8
8
  # The default regexp for an acceptable copyright notice can be found in
9
- # config/default.yml. The default can be changed as follows:
9
+ # config/default.yml. The default can be changed as follows:
10
10
  #
11
11
  # Style/Copyright:
12
12
  # Notice: '^Copyright (\(c\) )?2\d{3} Acme Inc'
13
13
  #
14
- # This regex string is treated as an unanchored regex. For each file
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
16
16
  # an offense is reported.
17
17
  #
@@ -26,6 +26,7 @@ module RuboCop
26
26
  def investigate(processed_source)
27
27
  return if notice.empty?
28
28
  return if notice_found?(processed_source)
29
+
29
30
  range = source_range(processed_source.buffer, 1, 0)
30
31
  add_offense(insert_notice_before(processed_source),
31
32
  location: range, message: format(MSG, notice: notice))
@@ -33,6 +34,7 @@ module RuboCop
33
34
 
34
35
  def autocorrect(token)
35
36
  raise Warning, AUTOCORRECT_EMPTY_WARNING if autocorrect_notice.empty?
37
+
36
38
  regex = Regexp.new(notice)
37
39
  unless autocorrect_notice =~ regex
38
40
  raise Warning, "AutocorrectNotice '#{autocorrect_notice}' must " \
@@ -64,12 +66,14 @@ module RuboCop
64
66
 
65
67
  def shebang_token?(processed_source, token_index)
66
68
  return false if token_index >= processed_source.tokens.size
69
+
67
70
  token = processed_source.tokens[token_index]
68
71
  token.comment? && token.text =~ /^#!.*$/
69
72
  end
70
73
 
71
74
  def encoding_token?(processed_source, token_index)
72
75
  return false if token_index >= processed_source.tokens.size
76
+
73
77
  token = processed_source.tokens[token_index]
74
78
  token.comment? && token.text =~ /^#.*coding\s?[:=]\s?(?:UTF|utf)-8/
75
79
  end
@@ -79,6 +83,7 @@ module RuboCop
79
83
  notice_regexp = Regexp.new(notice)
80
84
  processed_source.each_token do |token|
81
85
  break unless token.comment?
86
+
82
87
  notice_found = !(token.text =~ notice_regexp).nil?
83
88
  break if notice_found
84
89
  end
@@ -3,8 +3,11 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # This cop checks for uses of `DateTime` that should be replaced by
7
- # `Date` or `Time`.
6
+ # This cop checks for consistent usage of the `DateTime` class over the
7
+ # `Time` class. This cop is disabled by default since these classes,
8
+ # although highly overlapping, have particularities that make them not
9
+ # replaceable in certain situations when dealing with multiple timezones
10
+ # and/or DST.
8
11
  #
9
12
  # @example
10
13
  #
@@ -17,13 +20,30 @@ module RuboCop
17
20
  # # bad - uses `DateTime` for modern date
18
21
  # DateTime.iso8601('2016-06-29')
19
22
  #
20
- # # good - uses `Date` for modern date
21
- # Date.iso8601('2016-06-29')
23
+ # # good - uses `Time` for modern date
24
+ # Time.iso8601('2016-06-29')
22
25
  #
23
26
  # # good - uses `DateTime` with start argument for historical date
24
27
  # DateTime.iso8601('1751-04-23', Date::ENGLAND)
28
+ #
29
+ # @example AllowCoercion: false (default)
30
+ #
31
+ # # bad - coerces to `DateTime`
32
+ # something.to_datetime
33
+ #
34
+ # # good - coerces to `Time`
35
+ # something.to_time
36
+ #
37
+ # @example AllowCoercion: true
38
+ #
39
+ # # good
40
+ # something.to_datetime
41
+ #
42
+ # # good
43
+ # something.to_time
25
44
  class DateTime < Cop
26
- MSG = 'Prefer Date or Time over DateTime.'.freeze
45
+ CLASS_MSG = 'Prefer Time over DateTime.'.freeze
46
+ COERCION_MSG = 'Do not use #to_datetime.'.freeze
27
47
 
28
48
  def_node_matcher :date_time?, <<-PATTERN
29
49
  (send (const {nil? (cbase)} :DateTime) ...)
@@ -33,10 +53,23 @@ module RuboCop
33
53
  (send _ _ _ (const (const nil? :Date) _))
34
54
  PATTERN
35
55
 
56
+ def_node_matcher :to_datetime?, <<-PATTERN
57
+ (send _ :to_datetime)
58
+ PATTERN
59
+
36
60
  def on_send(node)
37
- return unless date_time?(node)
61
+ return unless date_time?(node) ||
62
+ (to_datetime?(node) && disallow_coercion?)
38
63
  return if historic_date?(node)
39
- add_offense(node)
64
+
65
+ message = to_datetime?(node) ? COERCION_MSG : CLASS_MSG
66
+ add_offense(node, message: message)
67
+ end
68
+
69
+ private
70
+
71
+ def disallow_coercion?
72
+ !cop_config['AllowCoercion']
40
73
  end
41
74
  end
42
75
  end
@@ -25,7 +25,7 @@ module RuboCop
25
25
  def_node_matcher :double_negative?, '(send (send _ :!) :!)'
26
26
 
27
27
  def on_send(node)
28
- return unless double_negative?(node) && node.keyword_bang?
28
+ return unless double_negative?(node) && node.prefix_bang?
29
29
 
30
30
  add_offense(node, location: :selector)
31
31
  end
@@ -43,6 +43,14 @@ module RuboCop
43
43
 
44
44
  def on_case(case_node)
45
45
  return if case_node.condition
46
+ return if case_node.when_branches.any? do |when_branch|
47
+ when_branch.each_descendant.any?(&:return_type?)
48
+ end
49
+
50
+ if (else_branch = case_node.else_branch)
51
+ return if else_branch.return_type? ||
52
+ else_branch.each_descendant.any?(&:return_type?)
53
+ end
46
54
 
47
55
  add_offense(case_node, location: :keyword)
48
56
  end
@@ -148,12 +148,14 @@ module RuboCop
148
148
 
149
149
  def else_line_range(loc)
150
150
  return 0..0 if loc.else.nil? || loc.end.nil?
151
+
151
152
  loc.else.first_line..loc.end.first_line
152
153
  end
153
154
 
154
155
  def base_node(node)
155
156
  return node if node.case_type?
156
157
  return node unless node.elsif?
158
+
157
159
  node.each_ancestor(:if, :case, :when).find(-> { node }) do |parent|
158
160
  parent.loc.end
159
161
  end
@@ -25,6 +25,7 @@ module RuboCop
25
25
  def on_block(node)
26
26
  send_node = node.send_node
27
27
  return unless send_node.send_type?
28
+
28
29
  check(node) if node.send_node.stabby_lambda?
29
30
  end
30
31
 
@@ -124,6 +124,7 @@ module RuboCop
124
124
 
125
125
  def add_offense_for_same_line(node, line_node)
126
126
  return if special_line_keyword?(line_node)
127
+
127
128
  add_offense(
128
129
  node,
129
130
  location: line_node.loc.expression,
@@ -134,6 +135,7 @@ module RuboCop
134
135
  def add_offense_for_different_line(node, line_node, line_diff)
135
136
  sign = line_diff > 0 ? :+ : :-
136
137
  return if line_with_offset?(line_node, sign, line_diff.abs)
138
+
137
139
  add_offense(
138
140
  node,
139
141
  location: line_node.loc.expression,
@@ -43,12 +43,24 @@ module RuboCop
43
43
  include RangeHelp
44
44
 
45
45
  EACH_LENGTH = 'each'.length
46
+ PREFER_EACH = 'Prefer `each` over `for`.'.freeze
47
+ PREFER_FOR = 'Prefer `for` over `each`.'.freeze
48
+
49
+ def_node_matcher :deconstruct_for, <<-PATTERN
50
+ (for $_item $_enumerable _block)
51
+ PATTERN
52
+
53
+ def_node_matcher :deconstruct_each, <<-PATTERN
54
+ (block (send $_enumerable :each) $_ _block)
55
+ PATTERN
56
+
57
+ def_node_matcher :extract_variables, <<-PATTERN
58
+ (args $_)
59
+ PATTERN
46
60
 
47
61
  def on_for(node)
48
62
  if style == :each
49
- msg = 'Prefer `each` over `for`.'
50
-
51
- add_offense(node, location: :keyword, message: msg) do
63
+ add_offense(node, message: PREFER_EACH) do
52
64
  opposite_style_detected
53
65
  end
54
66
  else
@@ -63,23 +75,57 @@ module RuboCop
63
75
  !node.send_node.arguments?
64
76
 
65
77
  if style == :for
66
- incorrect_style_detected(node.send_node)
78
+ incorrect_style_detected(node)
67
79
  else
68
80
  correct_style_detected
69
81
  end
70
82
  end
71
83
 
72
- private
84
+ def autocorrect(node)
85
+ if style == :each
86
+ autocorrect_to_each(node)
87
+ else
88
+ autocorrect_to_for(node)
89
+ end
90
+ end
73
91
 
74
- def incorrect_style_detected(method)
75
- end_pos = method.source_range.end_pos
76
- range = range_between(end_pos - EACH_LENGTH, end_pos)
77
- msg = 'Prefer `for` over `each`.'
92
+ private
78
93
 
79
- add_offense(range, location: range, message: msg) do
94
+ def incorrect_style_detected(node)
95
+ add_offense(node, message: PREFER_FOR) do
80
96
  opposite_style_detected
81
97
  end
82
98
  end
99
+
100
+ def autocorrect_to_each(node)
101
+ item, enumerable = deconstruct_for(node)
102
+ replacement_range = replacement_range(node, node.loc.begin.end_pos)
103
+ correction = "#{enumerable.source}.each do |#{item.source}|"
104
+
105
+ ->(corrector) { corrector.replace(replacement_range, correction) }
106
+ end
107
+
108
+ def autocorrect_to_for(node)
109
+ enumerable, items = deconstruct_each(node)
110
+ variables = extract_variables(items)
111
+
112
+ if variables.nil?
113
+ replacement_range = replacement_range(node, node.loc.begin.end_pos)
114
+ correction = "for _ in #{enumerable.source} do"
115
+ else
116
+ replacement_range = replacement_range(node,
117
+ items.loc.expression.end_pos)
118
+ correction = "for #{variables.source} in #{enumerable.source} do"
119
+ end
120
+
121
+ ->(corrector) { corrector.replace(replacement_range, correction) }
122
+ end
123
+
124
+ def replacement_range(node, end_pos)
125
+ Parser::Source::Range.new(node.loc.expression.source_buffer,
126
+ node.loc.expression.begin_pos,
127
+ end_pos)
128
+ end
83
129
  end
84
130
  end
85
131
  end