rubocop 0.42.0 → 0.43.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 (221) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/assets/output.html.erb +21 -10
  4. data/config/default.yml +32 -2
  5. data/config/disabled.yml +8 -1
  6. data/config/enabled.yml +40 -12
  7. data/lib/rubocop.rb +14 -2
  8. data/lib/rubocop/ast_node.rb +2 -0
  9. data/lib/rubocop/cached_data.rb +13 -11
  10. data/lib/rubocop/cli.rb +5 -5
  11. data/lib/rubocop/config.rb +68 -24
  12. data/lib/rubocop/config_loader.rb +13 -11
  13. data/lib/rubocop/config_loader_resolver.rb +4 -2
  14. data/lib/rubocop/cop/cop.rb +16 -5
  15. data/lib/rubocop/cop/lint/assignment_in_condition.rb +21 -20
  16. data/lib/rubocop/cop/lint/block_alignment.rb +3 -4
  17. data/lib/rubocop/cop/lint/def_end_alignment.rb +2 -3
  18. data/lib/rubocop/cop/lint/duplicate_methods.rb +16 -6
  19. data/lib/rubocop/cop/lint/else_layout.rb +1 -1
  20. data/lib/rubocop/cop/lint/empty_interpolation.rb +1 -1
  21. data/lib/rubocop/cop/lint/end_alignment.rb +4 -6
  22. data/lib/rubocop/cop/lint/eval.rb +1 -1
  23. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +1 -1
  24. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +8 -8
  25. data/lib/rubocop/cop/lint/inherit_exception.rb +22 -7
  26. data/lib/rubocop/cop/lint/literal_in_condition.rb +5 -5
  27. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +3 -5
  28. data/lib/rubocop/cop/lint/next_without_accumulator.rb +1 -1
  29. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +9 -8
  30. data/lib/rubocop/cop/lint/percent_string_array.rb +17 -6
  31. data/lib/rubocop/cop/lint/percent_symbol_array.rb +4 -4
  32. data/lib/rubocop/cop/lint/rand_one.rb +3 -3
  33. data/lib/rubocop/cop/lint/require_parentheses.rb +1 -3
  34. data/lib/rubocop/cop/lint/shadowed_exception.rb +39 -44
  35. data/lib/rubocop/cop/lint/string_conversion_in_interpolation.rb +2 -2
  36. data/lib/rubocop/cop/lint/underscore_prefixed_variable_name.rb +1 -2
  37. data/lib/rubocop/cop/lint/unified_integer.rb +38 -0
  38. data/lib/rubocop/cop/lint/unneeded_disable.rb +51 -38
  39. data/lib/rubocop/cop/lint/unneeded_splat_expansion.rb +114 -0
  40. data/lib/rubocop/cop/lint/useless_assignment.rb +25 -12
  41. data/lib/rubocop/cop/lint/useless_setter_call.rb +27 -28
  42. data/lib/rubocop/cop/lint/void.rb +2 -4
  43. data/lib/rubocop/cop/mixin/access_modifier_node.rb +5 -5
  44. data/lib/rubocop/cop/mixin/array_hash_indentation.rb +19 -17
  45. data/lib/rubocop/cop/mixin/autocorrect_alignment.rb +3 -5
  46. data/lib/rubocop/cop/mixin/configurable_naming.rb +4 -5
  47. data/lib/rubocop/cop/mixin/configurable_numbering.rb +52 -0
  48. data/lib/rubocop/cop/mixin/def_node.rb +28 -0
  49. data/lib/rubocop/cop/mixin/documentation_comment.rb +41 -0
  50. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +18 -13
  51. data/lib/rubocop/cop/mixin/if_node.rb +6 -0
  52. data/lib/rubocop/cop/mixin/match_range.rb +2 -5
  53. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +2 -2
  54. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +40 -28
  55. data/lib/rubocop/cop/mixin/negative_conditional.rb +6 -6
  56. data/lib/rubocop/cop/mixin/percent_literal.rb +1 -5
  57. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +14 -4
  58. data/lib/rubocop/cop/mixin/safe_mode.rb +23 -0
  59. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +2 -4
  60. data/lib/rubocop/cop/mixin/space_inside.rb +1 -3
  61. data/lib/rubocop/cop/mixin/statement_modifier.rb +30 -20
  62. data/lib/rubocop/cop/mixin/trailing_comma.rb +19 -17
  63. data/lib/rubocop/cop/performance/case_when_splat.rb +16 -41
  64. data/lib/rubocop/cop/performance/casecmp.rb +28 -16
  65. data/lib/rubocop/cop/performance/count.rb +58 -34
  66. data/lib/rubocop/cop/performance/detect.rb +3 -7
  67. data/lib/rubocop/cop/performance/double_start_end_with.rb +17 -13
  68. data/lib/rubocop/cop/performance/fixed_size.rb +19 -14
  69. data/lib/rubocop/cop/performance/flat_map.rb +16 -9
  70. data/lib/rubocop/cop/performance/hash_each.rb +2 -3
  71. data/lib/rubocop/cop/performance/lstrip_rstrip.rb +4 -6
  72. data/lib/rubocop/cop/performance/redundant_match.rb +4 -1
  73. data/lib/rubocop/cop/performance/redundant_merge.rb +63 -32
  74. data/lib/rubocop/cop/performance/redundant_sort_by.rb +8 -7
  75. data/lib/rubocop/cop/performance/reverse_each.rb +1 -4
  76. data/lib/rubocop/cop/performance/size.rb +21 -8
  77. data/lib/rubocop/cop/performance/sort_with_block.rb +54 -0
  78. data/lib/rubocop/cop/performance/string_replacement.rb +3 -7
  79. data/lib/rubocop/cop/rails/delegate.rb +2 -3
  80. data/lib/rubocop/cop/rails/find_by.rb +4 -8
  81. data/lib/rubocop/cop/rails/not_null_column.rb +45 -0
  82. data/lib/rubocop/cop/rails/request_referer.rb +3 -3
  83. data/lib/rubocop/cop/rails/safe_navigation.rb +89 -0
  84. data/lib/rubocop/cop/rails/save_bang.rb +78 -9
  85. data/lib/rubocop/cop/rails/scope_args.rb +3 -1
  86. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +2 -3
  87. data/lib/rubocop/cop/rails/validation.rb +1 -1
  88. data/lib/rubocop/cop/security/json_load.rb +36 -0
  89. data/lib/rubocop/cop/style/alias.rb +1 -1
  90. data/lib/rubocop/cop/style/align_hash.rb +25 -14
  91. data/lib/rubocop/cop/style/and_or.rb +13 -3
  92. data/lib/rubocop/cop/style/array_join.rb +3 -3
  93. data/lib/rubocop/cop/style/ascii_comments.rb +1 -2
  94. data/lib/rubocop/cop/style/ascii_identifiers.rb +1 -2
  95. data/lib/rubocop/cop/style/attr.rb +1 -3
  96. data/lib/rubocop/cop/style/block_comments.rb +2 -6
  97. data/lib/rubocop/cop/style/block_delimiters.rb +35 -21
  98. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +4 -4
  99. data/lib/rubocop/cop/style/case_indentation.rb +1 -3
  100. data/lib/rubocop/cop/style/class_methods.rb +3 -4
  101. data/lib/rubocop/cop/style/collection_methods.rb +1 -1
  102. data/lib/rubocop/cop/style/command_literal.rb +15 -8
  103. data/lib/rubocop/cop/style/comment_annotation.rb +1 -2
  104. data/lib/rubocop/cop/style/conditional_assignment.rb +68 -36
  105. data/lib/rubocop/cop/style/copyright.rb +1 -5
  106. data/lib/rubocop/cop/style/def_with_parentheses.rb +3 -5
  107. data/lib/rubocop/cop/style/documentation.rb +28 -56
  108. data/lib/rubocop/cop/style/documentation_method.rb +80 -0
  109. data/lib/rubocop/cop/style/each_for_simple_loop.rb +6 -5
  110. data/lib/rubocop/cop/style/each_with_object.rb +2 -2
  111. data/lib/rubocop/cop/style/else_alignment.rb +10 -9
  112. data/lib/rubocop/cop/style/empty_case_condition.rb +2 -4
  113. data/lib/rubocop/cop/style/empty_else.rb +1 -4
  114. data/lib/rubocop/cop/style/empty_line_between_defs.rb +1 -3
  115. data/lib/rubocop/cop/style/empty_lines_around_access_modifier.rb +2 -5
  116. data/lib/rubocop/cop/style/encoding.rb +28 -14
  117. data/lib/rubocop/cop/style/even_odd.rb +28 -17
  118. data/lib/rubocop/cop/style/extra_spacing.rb +36 -25
  119. data/lib/rubocop/cop/style/file_name.rb +19 -10
  120. data/lib/rubocop/cop/style/first_parameter_indentation.rb +2 -3
  121. data/lib/rubocop/cop/style/for.rb +12 -8
  122. data/lib/rubocop/cop/style/format_string.rb +1 -1
  123. data/lib/rubocop/cop/style/guard_clause.rb +22 -56
  124. data/lib/rubocop/cop/style/hash_syntax.rb +72 -7
  125. data/lib/rubocop/cop/style/if_unless_modifier.rb +23 -19
  126. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +3 -3
  127. data/lib/rubocop/cop/style/indentation_width.rb +30 -16
  128. data/lib/rubocop/cop/style/infinite_loop.rb +16 -13
  129. data/lib/rubocop/cop/style/initial_indentation.rb +23 -18
  130. data/lib/rubocop/cop/style/inline_comment.rb +16 -3
  131. data/lib/rubocop/cop/style/lambda.rb +22 -10
  132. data/lib/rubocop/cop/style/leading_comment_space.rb +12 -1
  133. data/lib/rubocop/cop/style/line_end_concatenation.rb +24 -6
  134. data/lib/rubocop/cop/style/method_call_parentheses.rb +18 -9
  135. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +3 -4
  136. data/lib/rubocop/cop/style/method_def_parentheses.rb +3 -4
  137. data/lib/rubocop/cop/style/method_missing.rb +10 -2
  138. data/lib/rubocop/cop/style/module_function.rb +14 -6
  139. data/lib/rubocop/cop/style/multiline_assignment_layout.rb +2 -5
  140. data/lib/rubocop/cop/style/multiline_block_chain.rb +3 -5
  141. data/lib/rubocop/cop/style/multiline_block_layout.rb +22 -15
  142. data/lib/rubocop/cop/style/multiline_method_call_brace_layout.rb +9 -0
  143. data/lib/rubocop/cop/style/multiline_method_call_indentation.rb +41 -20
  144. data/lib/rubocop/cop/style/multiline_operation_indentation.rb +6 -6
  145. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +3 -5
  146. data/lib/rubocop/cop/style/mutable_constant.rb +21 -13
  147. data/lib/rubocop/cop/style/negated_if.rb +1 -1
  148. data/lib/rubocop/cop/style/negated_while.rb +3 -3
  149. data/lib/rubocop/cop/style/nested_modifier.rb +2 -4
  150. data/lib/rubocop/cop/style/next.rb +4 -4
  151. data/lib/rubocop/cop/style/non_nil_check.rb +18 -10
  152. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +8 -0
  153. data/lib/rubocop/cop/style/numeric_predicate.rb +9 -9
  154. data/lib/rubocop/cop/style/one_line_conditional.rb +11 -1
  155. data/lib/rubocop/cop/style/op_method.rb +1 -1
  156. data/lib/rubocop/cop/style/option_hash.rb +8 -8
  157. data/lib/rubocop/cop/style/optional_arguments.rb +21 -8
  158. data/lib/rubocop/cop/style/parallel_assignment.rb +51 -35
  159. data/lib/rubocop/cop/style/parentheses_around_condition.rb +2 -2
  160. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +1 -1
  161. data/lib/rubocop/cop/style/raise_args.rb +2 -2
  162. data/lib/rubocop/cop/style/redundant_begin.rb +1 -1
  163. data/lib/rubocop/cop/style/redundant_parentheses.rb +26 -15
  164. data/lib/rubocop/cop/style/redundant_return.rb +5 -5
  165. data/lib/rubocop/cop/style/redundant_self.rb +20 -11
  166. data/lib/rubocop/cop/style/regexp_literal.rb +16 -10
  167. data/lib/rubocop/cop/style/rescue_ensure_alignment.rb +8 -6
  168. data/lib/rubocop/cop/style/safe_navigation.rb +125 -0
  169. data/lib/rubocop/cop/style/self_assignment.rb +2 -2
  170. data/lib/rubocop/cop/style/semicolon.rb +9 -10
  171. data/lib/rubocop/cop/style/signal_exception.rb +2 -4
  172. data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
  173. data/lib/rubocop/cop/style/single_line_methods.rb +18 -11
  174. data/lib/rubocop/cop/style/space_after_method_name.rb +2 -3
  175. data/lib/rubocop/cop/style/space_after_not.rb +4 -6
  176. data/lib/rubocop/cop/style/space_around_block_parameters.rb +1 -2
  177. data/lib/rubocop/cop/style/space_around_equals_in_parameter_default.rb +1 -3
  178. data/lib/rubocop/cop/style/space_around_operators.rb +21 -16
  179. data/lib/rubocop/cop/style/space_before_block_braces.rb +2 -12
  180. data/lib/rubocop/cop/style/space_before_first_arg.rb +1 -3
  181. data/lib/rubocop/cop/style/space_inside_array_percent_literal.rb +1 -1
  182. data/lib/rubocop/cop/style/space_inside_block_braces.rb +33 -40
  183. data/lib/rubocop/cop/style/space_inside_hash_literal_braces.rb +38 -23
  184. data/lib/rubocop/cop/style/space_inside_percent_literal_delimiters.rb +1 -1
  185. data/lib/rubocop/cop/style/space_inside_string_interpolation.rb +26 -12
  186. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +2 -4
  187. data/lib/rubocop/cop/style/symbol_array.rb +10 -10
  188. data/lib/rubocop/cop/style/symbol_proc.rb +28 -13
  189. data/lib/rubocop/cop/style/ternary_parentheses.rb +35 -5
  190. data/lib/rubocop/cop/style/trailing_blank_lines.rb +2 -4
  191. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +29 -17
  192. data/lib/rubocop/cop/style/trivial_accessors.rb +6 -6
  193. data/lib/rubocop/cop/style/unless_else.rb +2 -6
  194. data/lib/rubocop/cop/style/unneeded_capital_w.rb +8 -4
  195. data/lib/rubocop/cop/style/unneeded_interpolation.rb +4 -5
  196. data/lib/rubocop/cop/style/unneeded_percent_q.rb +13 -7
  197. data/lib/rubocop/cop/style/variable_number.rb +79 -0
  198. data/lib/rubocop/cop/style/while_until_modifier.rb +1 -1
  199. data/lib/rubocop/cop/style/word_array.rb +25 -15
  200. data/lib/rubocop/cop/style/zero_length_predicate.rb +2 -0
  201. data/lib/rubocop/cop/util.rb +23 -4
  202. data/lib/rubocop/cop/variable_force.rb +59 -25
  203. data/lib/rubocop/cop/variable_force/locatable.rb +8 -6
  204. data/lib/rubocop/cop/variable_force/variable.rb +2 -2
  205. data/lib/rubocop/cop/variable_force/variable_table.rb +3 -3
  206. data/lib/rubocop/formatter/disabled_config_formatter.rb +16 -11
  207. data/lib/rubocop/formatter/formatter_set.rb +12 -10
  208. data/lib/rubocop/formatter/worst_offenders_formatter.rb +4 -4
  209. data/lib/rubocop/node_pattern.rb +79 -35
  210. data/lib/rubocop/options.rb +4 -4
  211. data/lib/rubocop/processed_source.rb +9 -5
  212. data/lib/rubocop/remote_config.rb +14 -10
  213. data/lib/rubocop/result_cache.rb +14 -6
  214. data/lib/rubocop/runner.rb +55 -34
  215. data/lib/rubocop/string_util.rb +9 -5
  216. data/lib/rubocop/target_finder.rb +1 -1
  217. data/lib/rubocop/token.rb +1 -1
  218. data/lib/rubocop/version.rb +1 -1
  219. metadata +15 -4
  220. data/lib/rubocop/cop/lint/useless_array_splat.rb +0 -56
  221. data/lib/rubocop/cop/performance/push_splat.rb +0 -47
@@ -0,0 +1,54 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module RuboCop
5
+ module Cop
6
+ module Performance
7
+ # This cop identifies places where `sort { |a, b| a.foo <=> b.foo }`
8
+ # can be replaced by `sort_by(&:foo)`.
9
+ #
10
+ # @example
11
+ # @bad
12
+ # array.sort { |a, b| a.foo <=> b.foo }
13
+ #
14
+ # @good
15
+ # array.sort_by(&:foo)
16
+ # array.sort_by { |v| v.foo }
17
+ # array.sort_by do |var|
18
+ # var.foo
19
+ # end
20
+ class SortWithBlock < Cop
21
+ MSG = 'Use `sort_by(&:%s)` instead of ' \
22
+ '`sort { |%s, %s| %s.%s <=> %s.%s }`.'.freeze
23
+
24
+ def_node_matcher :sort, <<-END
25
+ (block $(send _ :sort) (args (arg $_a) (arg $_b)) (send (send (lvar _a) $_m) :<=> (send (lvar _b) $_m)))
26
+ END
27
+
28
+ def on_block(node)
29
+ sort(node) do |send, var_a, var_b, method|
30
+ range = sort_range(send, node)
31
+ add_offense(node, range,
32
+ format(MSG, method, var_a, var_b,
33
+ var_a, method, var_b, method))
34
+ end
35
+ end
36
+
37
+ def autocorrect(node)
38
+ send, = *node
39
+
40
+ lambda do |corrector|
41
+ method = node.children.last.children.last.children.last
42
+ corrector.replace(sort_range(send, node), "sort_by(&:#{method})")
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def sort_range(send, node)
49
+ range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -160,9 +160,7 @@ module RuboCop
160
160
  end
161
161
 
162
162
  def range(node)
163
- Parser::Source::Range.new(node.source_range.source_buffer,
164
- node.loc.selector.begin_pos,
165
- node.source_range.end_pos)
163
+ range_between(node.loc.selector.begin_pos, node.source_range.end_pos)
166
164
  end
167
165
 
168
166
  def replacement_method(method, first_source, second_source)
@@ -192,10 +190,8 @@ module RuboCop
192
190
  end
193
191
 
194
192
  def remove_second_param(corrector, node, first_param)
195
- end_range =
196
- Parser::Source::Range.new(node.source_range.source_buffer,
197
- first_param.source_range.end_pos,
198
- node.source_range.end_pos)
193
+ end_range = range_between(first_param.source_range.end_pos,
194
+ node.source_range.end_pos)
199
195
 
200
196
  corrector.replace(end_range, method_suffix(node))
201
197
  end
@@ -61,9 +61,8 @@ module RuboCop
61
61
  end
62
62
 
63
63
  def delegate?(body)
64
- receiver, * = *body
65
- return false unless receiver.is_a? Parser::AST::Node
66
- return false unless receiver.type == :send
64
+ receiver, = *body
65
+ return false unless receiver.is_a?(Node) && receiver.send_type?
67
66
  receiver.child_nodes.empty?
68
67
  end
69
68
 
@@ -26,9 +26,8 @@ module RuboCop
26
26
  return unless (recv_and_method = where_first(node))
27
27
  receiver, second_method = *recv_and_method
28
28
 
29
- range = Parser::Source::Range.new(node.source_range.source_buffer,
30
- receiver.loc.selector.begin_pos,
31
- node.loc.selector.end_pos)
29
+ range = range_between(receiver.loc.selector.begin_pos,
30
+ node.loc.selector.end_pos)
32
31
 
33
32
  add_offense(node, range, format(MSG, second_method))
34
33
  end
@@ -41,11 +40,8 @@ module RuboCop
41
40
  return if second_method == :first
42
41
 
43
42
  where_loc = receiver.loc.selector
44
- first_loc = Parser::Source::Range.new(
45
- node.source_range.source_buffer,
46
- node.loc.dot.begin_pos,
47
- node.loc.selector.end_pos
48
- )
43
+ first_loc = range_between(node.loc.dot.begin_pos,
44
+ node.loc.selector.end_pos)
49
45
 
50
46
  lambda do |corrector|
51
47
  corrector.replace(where_loc, 'find_by')
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module RuboCop
5
+ module Cop
6
+ module Rails
7
+ # This cop checks for add_column call with NOT NULL constraint
8
+ # in migration file.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # add_column :users, :name, :string, null: false
13
+ #
14
+ # # good
15
+ # add_column :users, :name, :string, null: true
16
+ # add_column :users, :name, :string, null: false, default: ''
17
+ class NotNullColumn < Cop
18
+ MSG = 'Do not add a NOT NULL column without a default value'.freeze
19
+
20
+ def_node_matcher :add_not_null_column?, <<-PATTERN
21
+ (send nil :add_column _ _ _ (hash $...))
22
+ PATTERN
23
+
24
+ def_node_matcher :null_false?, <<-PATTERN
25
+ (pair (sym :null) (false))
26
+ PATTERN
27
+
28
+ def_node_matcher :has_default?, <<-PATTERN
29
+ (pair (sym :default) !(:nil))
30
+ PATTERN
31
+
32
+ def on_send(node)
33
+ pairs = add_not_null_column?(node)
34
+ return unless pairs
35
+ return if pairs.any? { |pair| has_default?(pair) }
36
+
37
+ null_false = pairs.find { |pair| null_false?(pair) }
38
+ return unless null_false
39
+
40
+ add_offense(null_false, :expression)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -10,9 +10,9 @@ module RuboCop
10
10
  include ConfigurableEnforcedStyle
11
11
 
12
12
  def on_send(node)
13
- if offense?(node)
14
- add_offense(node.source_range, node.source_range, message)
15
- end
13
+ return unless offense?(node)
14
+
15
+ add_offense(node.source_range, node.source_range, message)
16
16
  end
17
17
 
18
18
  def autocorrect(node)
@@ -0,0 +1,89 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module RuboCop
5
+ module Cop
6
+ module Rails
7
+ # This cop converts usages of `try!` to `&.`. It can also be configured
8
+ # to convert `try`. It will convert code to use safe navigation if the
9
+ # target Ruby version is set to 2.3+
10
+ #
11
+ # @example
12
+ # # ConvertTry: false
13
+ # # bad
14
+ # foo.try!(:bar)
15
+ # foo.try!(:bar, baz)
16
+ # foo.try!(:bar) { |e| e.baz }
17
+ #
18
+ # foo.try!(:[], 0)
19
+ #
20
+ # # good
21
+ # foo.try(:bar)
22
+ # foo.try(:bar, baz)
23
+ # foo.try(:bar) { |e| e.baz }
24
+ #
25
+ # foo&.bar
26
+ # foo&.bar(baz)
27
+ # foo&.bar { |e| e.baz }
28
+ #
29
+ #
30
+ # # ConvertTry: true
31
+ # # bad
32
+ # foo.try!(:bar)
33
+ # foo.try!(:bar, baz)
34
+ # foo.try!(:bar) { |e| e.baz }
35
+ # foo.try(:bar)
36
+ # foo.try(:bar, baz)
37
+ # foo.try(:bar) { |e| e.baz }
38
+ #
39
+ # # good
40
+ # foo&.bar
41
+ # foo&.bar(baz)
42
+ # foo&.bar { |e| e.baz }
43
+ class SafeNavigation < Cop
44
+ MSG = 'Use safe navigation (`&.`) instead of `%s`.'.freeze
45
+
46
+ def_node_matcher :try_call, <<-PATTERN
47
+ (send _ ${:try :try!} $_ ...)
48
+ PATTERN
49
+
50
+ def on_send(node)
51
+ return if target_ruby_version < 2.3
52
+
53
+ try_call(node) do |try_method, method_to_try|
54
+ return if try_method == :try && !cop_config['ConvertTry']
55
+ return unless method_to_try.sym_type?
56
+ method, = *method_to_try
57
+ return unless method =~ /\w+[=!?]?/
58
+ add_offense(node, :expression, format(MSG, try_method))
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def autocorrect(node)
65
+ _receiver, _try, method_node, *params = *node
66
+ method = method_node.source[1..-1]
67
+ range = range_between(node.loc.dot.begin_pos,
68
+ node.loc.expression.end_pos)
69
+
70
+ lambda do |corrector|
71
+ corrector.replace(range, replacement(method, params))
72
+ end
73
+ end
74
+
75
+ def replacement(method, params)
76
+ new_params = params.map(&:source).join(', ')
77
+
78
+ if method.end_with?('=')
79
+ "&.#{method[0...-1]} = #{new_params}"
80
+ elsif params.empty?
81
+ "&.#{method}"
82
+ else
83
+ "&.#{method}(#{new_params})"
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -8,10 +8,12 @@ module RuboCop
8
8
  # should be used instead of save because the model might have failed to
9
9
  # save and an exception is better than unhandled failure.
10
10
  #
11
- # This will ignore calls that are assigned to a variable or used as the
12
- # condition in an if/unless statement. It will also ignore any call with
13
- # more than 2 arguments as that is likely not an Active Record call or
14
- # if a Model.update(id, attributes) call.
11
+ # This will ignore calls that return a boolean for success if the result
12
+ # is assigned to a variable or used as the condition in an if/unless
13
+ # statement. It will also ignore calls that return a model assigned to a
14
+ # variable that has a call to `persisted?`. Finally, it will ignore any
15
+ # call with more than 2 arguments as that is likely not an Active Record
16
+ # call or a Model.update(id, attributes) call.
15
17
  #
16
18
  # @example
17
19
  #
@@ -29,17 +31,57 @@ module RuboCop
29
31
  # user.update!(name: 'Joe')
30
32
  # user.find_or_create_by!(name: 'Joe')
31
33
  # user.destroy!
34
+ #
35
+ # user = User.find_or_create_by(name: 'Joe')
36
+ # unless user.persisted?
37
+ # . . .
38
+ # end
32
39
  class SaveBang < Cop
33
40
  MSG = 'Use `%s` instead of `%s` if the return value is not checked.'
34
41
  .freeze
42
+ CREATE_MSG = (MSG +
43
+ ' Or check `persisted?` on model returned from `%s`.')
44
+ .freeze
45
+ CREATE_CONDITIONAL_MSG = '`%s` returns a model which is always truthy.'
46
+ .freeze
47
+
48
+ CREATE_PERSIST_METHODS = [:create,
49
+ :first_or_create, :find_or_create_by].freeze
50
+ MODIFY_PERSIST_METHODS = [:save, :update, :destroy].freeze
51
+ PERSIST_METHODS = (CREATE_PERSIST_METHODS +
52
+ MODIFY_PERSIST_METHODS).freeze
53
+
54
+ def join_force?(force_class)
55
+ force_class == VariableForce
56
+ end
35
57
 
36
- PERSIST_METHODS = [:save, :create, :update, :destroy,
37
- :first_or_create, :find_or_create_by].freeze
58
+ def after_leaving_scope(scope, _variable_table)
59
+ scope.variables.each do |_name, variable|
60
+ variable.assignments.each do |assignment|
61
+ check_assignment(assignment)
62
+ end
63
+ end
64
+ end
65
+
66
+ def check_assignment(assignment)
67
+ node = right_assignment_node(assignment)
68
+ return unless node
69
+ return unless CREATE_PERSIST_METHODS.include?(node.method_name)
70
+ return unless expected_signature?(node)
71
+ return if persisted_referenced?(assignment)
72
+
73
+ add_offense(node, node.loc.selector,
74
+ format(CREATE_MSG,
75
+ "#{node.method_name}!",
76
+ node.method_name.to_s,
77
+ node.method_name.to_s))
78
+ end
38
79
 
39
80
  def on_send(node)
40
81
  return unless PERSIST_METHODS.include?(node.method_name)
41
- return if return_value_used?(node)
42
82
  return unless expected_signature?(node)
83
+ return if return_value_assigned?(node)
84
+ return if check_used_in_conditional(node)
43
85
 
44
86
  add_offense(node, node.loc.selector,
45
87
  format(MSG,
@@ -56,11 +98,38 @@ module RuboCop
56
98
 
57
99
  private
58
100
 
101
+ def right_assignment_node(assignment)
102
+ node = assignment.node.child_nodes.first
103
+ return node unless node && node.block_type?
104
+ node.child_nodes.first
105
+ end
106
+
107
+ def persisted_referenced?(assignment)
108
+ return unless assignment.referenced?
109
+ assignment.variable.references.any? do |reference|
110
+ reference.node.parent.method_name == :persisted?
111
+ end
112
+ end
113
+
114
+ def check_used_in_conditional(node)
115
+ return false unless node.parent
116
+ return false unless node.parent.if_type? && node.sibling_index.zero?
117
+
118
+ unless MODIFY_PERSIST_METHODS.include?(node.method_name)
119
+ add_offense(node, node.loc.selector,
120
+ format(CREATE_CONDITIONAL_MSG,
121
+ node.method_name.to_s))
122
+ end
123
+
124
+ true
125
+ end
126
+
59
127
  # Ignore simple assignment or if condition
60
- def return_value_used?(node)
128
+ def return_value_assigned?(node)
61
129
  return false unless node.parent
62
130
  node.parent.lvasgn_type? ||
63
- (node.parent.if_type? && node.sibling_index.zero?)
131
+ (node.parent.block_type? && node.parent.parent &&
132
+ node.parent.parent.lvasgn_type?)
64
133
  end
65
134
 
66
135
  # Check argument signature as no arguments or one hash
@@ -26,7 +26,9 @@ module RuboCop
26
26
 
27
27
  second_arg = args[1]
28
28
 
29
- add_offense(second_arg, :expression) if second_arg.type == :send
29
+ return unless second_arg.send_type?
30
+
31
+ add_offense(second_arg, :expression)
30
32
  end
31
33
  end
32
34
  end
@@ -72,9 +72,8 @@ module RuboCop
72
72
  end
73
73
 
74
74
  def dot_method_with_whitespace(method, node)
75
- Parser::Source::Range.new(node.loc.expression.source_buffer,
76
- dot_method_begin_pos(method, node),
77
- node.loc.selector.end_pos)
75
+ range_between(dot_method_begin_pos(method, node),
76
+ node.loc.selector.end_pos)
78
77
  end
79
78
 
80
79
  def dot_method_begin_pos(method, node)
@@ -51,7 +51,7 @@ module RuboCop
51
51
 
52
52
  def correct_validate_type(corrector, node)
53
53
  _receiver, method_name, *args = *node
54
- options = args.find { |arg| arg.type != :sym }
54
+ options = args.find { |arg| !arg.sym_type? }
55
55
  validate_type = method_name.to_s.split('_')[1]
56
56
 
57
57
  if options
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module RuboCop
5
+ module Cop
6
+ module Security
7
+ # This cop checks for the use of unsecure JSON methods.
8
+ #
9
+ # @example
10
+ # # always offense
11
+ # JSON.load("{}")
12
+ # JSON.restore("{}")
13
+ #
14
+ # # no offense
15
+ # JSON.parse("{}")
16
+ #
17
+ class JSONLoad < Cop
18
+ MSG = 'Prefer `JSON.parse` instead of `JSON#%s`.'.freeze
19
+
20
+ def_node_matcher :json_load, <<-END
21
+ (send (const nil :JSON) ${:load :restore} ...)
22
+ END
23
+
24
+ def on_send(node)
25
+ json_load(node) do |method|
26
+ add_offense(node, :selector, format(MSG, method))
27
+ end
28
+ end
29
+
30
+ def autocorrect(node)
31
+ ->(corrector) { corrector.replace(node.loc.selector, 'parse') }
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end