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
@@ -72,15 +72,15 @@ module RuboCop
72
72
  def on_method_def(_node, _method_name, _args, body)
73
73
  return unless body
74
74
 
75
- if body.type == :return
75
+ if body.return_type?
76
76
  check_return_node(body)
77
- elsif body.type == :begin
77
+ elsif body.begin_type?
78
78
  expressions = *body
79
79
  last_expr = expressions.last
80
80
 
81
- if last_expr && last_expr.type == :return
82
- check_return_node(last_expr)
83
- end
81
+ return unless last_expr && last_expr.return_type?
82
+
83
+ check_return_node(last_expr)
84
84
  end
85
85
  end
86
86
 
@@ -49,7 +49,7 @@ module RuboCop
49
49
  def initialize(config = nil, options = nil)
50
50
  super
51
51
  @allowed_send_nodes = []
52
- @local_variables = []
52
+ @local_variables_scopes = Hash.new { |hash, key| hash[key] = [] }
53
53
  end
54
54
 
55
55
  # Assignment of self.x
@@ -68,12 +68,12 @@ module RuboCop
68
68
 
69
69
  # Using self.x to distinguish from local variable x
70
70
 
71
- def on_def(_node)
72
- @local_variables = []
71
+ def on_def(node)
72
+ add_scope(node)
73
73
  end
74
74
 
75
- def on_defs(_node)
76
- @local_variables = []
75
+ def on_defs(node)
76
+ add_scope(node)
77
77
  end
78
78
 
79
79
  def on_args(node)
@@ -86,7 +86,7 @@ module RuboCop
86
86
 
87
87
  def on_lvasgn(node)
88
88
  lhs, _rhs = *node
89
- @local_variables << lhs
89
+ @local_variables_scopes[node] << lhs
90
90
  end
91
91
 
92
92
  # Detect offenses
@@ -97,8 +97,7 @@ module RuboCop
97
97
  return unless regular_method_call?(node)
98
98
 
99
99
  return if @allowed_send_nodes.include?(node) ||
100
- @local_variables.include?(method_name)
101
-
100
+ @local_variables_scopes[node].include?(method_name)
102
101
  add_offense(node, :expression)
103
102
  end
104
103
 
@@ -112,6 +111,13 @@ module RuboCop
112
111
 
113
112
  private
114
113
 
114
+ def add_scope(node)
115
+ local_variables = []
116
+ node.descendants.each do |child_node|
117
+ @local_variables_scopes[child_node] = local_variables
118
+ end
119
+ end
120
+
115
121
  def regular_method_call?(node)
116
122
  _receiver, method_name, *_args = *node
117
123
 
@@ -123,7 +129,7 @@ module RuboCop
123
129
 
124
130
  def on_argument(node)
125
131
  name, = *node
126
- @local_variables << name
132
+ @local_variables_scopes[node] << name
127
133
  end
128
134
 
129
135
  def keyword?(method_name)
@@ -139,10 +145,13 @@ module RuboCop
139
145
  end
140
146
 
141
147
  def allow_self(node)
142
- return unless node.type == :send
148
+ return unless node.send_type?
143
149
 
144
150
  receiver, _method_name, *_args = *node
145
- @allowed_send_nodes << node if receiver && receiver.type == :self
151
+
152
+ return unless receiver && receiver.self_type?
153
+
154
+ @allowed_send_nodes << node
146
155
  end
147
156
  end
148
157
  end
@@ -46,23 +46,30 @@ module RuboCop
46
46
  private
47
47
 
48
48
  def check_slash_literal(node)
49
- return if style == :slashes && !contains_disallowed_slash?(node)
50
- return if style == :mixed &&
51
- node.single_line? &&
52
- !contains_disallowed_slash?(node)
49
+ return if allowed_slash_literal?(node)
53
50
 
54
51
  add_offense(node, :expression, MSG_USE_PERCENT_R)
55
52
  end
56
53
 
57
54
  def check_percent_r_literal(node)
58
- return if style == :slashes && contains_disallowed_slash?(node)
59
- return if style == :percent_r
60
- return if style == :mixed && node.multiline?
61
- return if style == :mixed && contains_disallowed_slash?(node)
55
+ return if allowed_percent_r_literal?(node)
62
56
 
63
57
  add_offense(node, :expression, MSG_USE_SLASHES)
64
58
  end
65
59
 
60
+ def allowed_slash_literal?(node)
61
+ style == :slashes && !contains_disallowed_slash?(node) ||
62
+ style == :mixed && node.single_line? &&
63
+ !contains_disallowed_slash?(node)
64
+ end
65
+
66
+ def allowed_percent_r_literal?(node)
67
+ style == :slashes && contains_disallowed_slash?(node) ||
68
+ style == :percent_r ||
69
+ style == :mixed && node.multiline? ||
70
+ style == :mixed && contains_disallowed_slash?(node)
71
+ end
72
+
66
73
  def contains_disallowed_slash?(node)
67
74
  !allow_inner_slashes? && contains_slash?(node)
68
75
  end
@@ -76,8 +83,7 @@ module RuboCop
76
83
  end
77
84
 
78
85
  def node_body(node)
79
- string_parts = node.children.select { |child| child.type == :str }
80
- string_parts.map(&:source).join
86
+ node.each_child_node(:str).map(&:source).join
81
87
  end
82
88
 
83
89
  def slash_literal?(node)
@@ -42,12 +42,7 @@ module RuboCop
42
42
  end
43
43
 
44
44
  def autocorrect(node)
45
- source_buffer = node.loc.keyword.source_buffer
46
- begin_pos = node.loc.keyword.begin_pos
47
- current_column = node.loc.keyword.column
48
- whitespace = Parser::Source::Range.new(source_buffer,
49
- begin_pos - current_column,
50
- begin_pos)
45
+ whitespace = whitespace_range(node)
51
46
  return false unless whitespace.source.strip.empty?
52
47
 
53
48
  new_column = ancestor_node(node).loc.end.column
@@ -76,6 +71,13 @@ module RuboCop
76
71
  @modifier_locations.include?(node.loc.keyword)
77
72
  end
78
73
 
74
+ def whitespace_range(node)
75
+ begin_pos = node.loc.keyword.begin_pos
76
+ current_column = node.loc.keyword.column
77
+
78
+ range_between(begin_pos - current_column, begin_pos)
79
+ end
80
+
79
81
  def ancestor_node(node)
80
82
  node.each_ancestor(:kwbegin, :def, :defs, :class, :module).first
81
83
  end
@@ -0,0 +1,125 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module RuboCop
5
+ module Cop
6
+ module Style
7
+ # This cop transforms usages of a method call safeguarded by a non `nil`
8
+ # check for the variable whose method is being called to
9
+ # safe navigation (`&.`).
10
+ #
11
+ # @example
12
+ # # bad
13
+ # foo.bar if foo
14
+ # foo.bar(param1, param2) if foo
15
+ # foo.bar { |e| e.something } if foo
16
+ # foo.bar(param) { |e| e.something } if foo
17
+ #
18
+ # foo.bar if !foo.nil?
19
+ # foo.bar unless !foo
20
+ # foo.bar unless foo.nil?
21
+ #
22
+ # foo && foo.bar
23
+ # foo && foo.bar(param1, param2)
24
+ # foo && foo.bar { |e| e.something }
25
+ # foo && foo.bar(param) { |e| e.something }
26
+ #
27
+ # foo.nil? || foo.bar
28
+ # !foo || foo.bar
29
+ #
30
+ # # good
31
+ # foo&.bar
32
+ # foo&.bar(param1, param2)
33
+ # foo&.bar { |e| e.something }
34
+ # foo&.bar(param) { |e| e.something }
35
+ #
36
+ # # Methods that `nil` will `respond_to?` should not be converted to
37
+ # # use safe navigation
38
+ # foo.to_i if foo
39
+ class SafeNavigation < Cop
40
+ MSG = 'Use safe navigation (`&.`) instead of checking if an object ' \
41
+ 'exists before calling the method.'.freeze
42
+ NIL_METHODS = nil.methods.freeze
43
+
44
+ def_node_matcher :safe_navigation_candidate, <<-PATTERN
45
+ {
46
+ (if
47
+ {(send (send $_ :nil?) :!) $_}
48
+ {(send $_ $_ ...) (block (send $_ $_ ...) ...)}
49
+ ...)
50
+ (if
51
+ (send $_ {:nil? :!}) nil
52
+ {(send $_ $_ ...) (block (send $_ $_ ...) ...)}
53
+ ...)
54
+ (and
55
+ {(send (send $_ :nil?) :!) $_}
56
+ {(send $_ $_ ...) (block (send $_ $_ ...) ...)}
57
+ ...)
58
+ (or
59
+ (send $_ {:nil? :!})
60
+ {(send $_ $_ ...) (block (send $_ $_ ...) ...)}
61
+ ...)
62
+ }
63
+ PATTERN
64
+
65
+ def on_if(node)
66
+ check_node(node)
67
+ end
68
+
69
+ def on_and(node)
70
+ check_node(node)
71
+ end
72
+
73
+ def on_or(node)
74
+ check_node(node)
75
+ end
76
+
77
+ def check_node(node)
78
+ return if target_ruby_version < 2.3
79
+ checked_variable, receiver, method = safe_navigation_candidate(node)
80
+ return unless receiver == checked_variable
81
+ return if NIL_METHODS.include?(method)
82
+ return unless method =~ /\w+[=!?]?/
83
+ add_offense(node, :expression)
84
+ end
85
+
86
+ def autocorrect(node)
87
+ lambda do |corrector|
88
+ if node.loc.respond_to?(:keyword) && node.loc.keyword.is?('unless')
89
+ _variable_check, _else, method_call = *node
90
+ else
91
+ _variable_check, method_call = *node
92
+ end
93
+
94
+ if method_call.block_type?
95
+ method, = *method_call
96
+ corrector.insert_before(method.loc.dot, '&')
97
+ else
98
+ corrector.insert_before(method_call.loc.dot, '&')
99
+ end
100
+
101
+ corrector.remove(range(node, method_call))
102
+ end
103
+ end
104
+
105
+ private
106
+
107
+ def range(node, method_call)
108
+ source_buffer = node.loc.expression.source_buffer
109
+ node_expression = node.loc.expression
110
+ method_expression = method_call.loc.expression
111
+
112
+ if node.and_type? || node.or_type?
113
+ Parser::Source::Range.new(source_buffer,
114
+ node_expression.begin_pos,
115
+ method_expression.begin_pos)
116
+ else
117
+ Parser::Source::Range.new(source_buffer,
118
+ method_expression.end_pos,
119
+ node_expression.end_pos)
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
@@ -35,7 +35,7 @@ module RuboCop
35
35
  var_name, rhs = *node
36
36
  return unless rhs
37
37
 
38
- if rhs.type == :send
38
+ if rhs.send_type?
39
39
  check_send_node(node, rhs, var_name, var_type)
40
40
  elsif [:and, :or].include?(rhs.type)
41
41
  check_boolean_node(node, rhs, var_name, var_type)
@@ -65,7 +65,7 @@ module RuboCop
65
65
  def autocorrect(node)
66
66
  _var_name, rhs = *node
67
67
 
68
- if rhs.type == :send
68
+ if rhs.send_type?
69
69
  autocorrect_send_node(node, rhs)
70
70
  elsif [:and, :or].include?(rhs.type)
71
71
  autocorrect_boolean_node(node, rhs)
@@ -40,21 +40,20 @@ module RuboCop
40
40
  private
41
41
 
42
42
  def check_for_line_terminator_or_opener
43
- tokens_for_lines = @processed_source.tokens.group_by do |token|
44
- token.pos.line
45
- end
43
+ each_semicolon { |line, column| convention_on(line, column, true) }
44
+ end
46
45
 
46
+ def each_semicolon
47
47
  tokens_for_lines.each do |line, tokens|
48
- if tokens.last.type == :tSEMI
49
- convention_on(line, tokens.last.pos.column, true)
50
- end
51
-
52
- if tokens.first.type == :tSEMI
53
- convention_on(line, tokens.first.pos.column, true)
54
- end
48
+ yield line, tokens.last.pos.column if tokens.last.type == :tSEMI
49
+ yield line, tokens.first.pos.column if tokens.first.type == :tSEMI
55
50
  end
56
51
  end
57
52
 
53
+ def tokens_for_lines
54
+ @processed_source.tokens.group_by { |token| token.pos.line }
55
+ end
56
+
58
57
  def convention_on(line, column, autocorrect)
59
58
  range = source_range(@processed_source.buffer, line, column)
60
59
  # Don't attempt to autocorrect if semicolon is separating statements
@@ -84,11 +84,9 @@ module RuboCop
84
84
  end
85
85
 
86
86
  def check_send(method_name, node)
87
- return unless node
87
+ return unless node && command_or_kernel_call?(method_name, node)
88
88
 
89
- if command_or_kernel_call?(method_name, node)
90
- add_offense(node, :selector, message(method_name))
91
- end
89
+ add_offense(node, :selector, message(method_name))
92
90
  end
93
91
 
94
92
  def command_or_kernel_call?(name, node)
@@ -25,7 +25,7 @@ module RuboCop
25
25
 
26
26
  return if args.empty?
27
27
  # discard cases with argument destructuring
28
- return true unless args.all? { |n| n.type == :arg }
28
+ return true unless args.all?(&:arg_type?)
29
29
  return if args_match?(method_name, args)
30
30
 
31
31
  add_offense(args_node, :expression, message(method_name))
@@ -31,26 +31,33 @@ module RuboCop
31
31
 
32
32
  def autocorrect(node)
33
33
  body = @body
34
- eol_comment = processed_source.comments.find do |c|
35
- c.loc.line == node.source_range.line
36
- end
34
+
37
35
  lambda do |corrector|
38
- if body
39
- if body.type == :begin
40
- body.children.each do |part|
41
- break_line_before(part.source_range, node, corrector, 1)
42
- end
43
- else
44
- break_line_before(body.source_range, node, corrector, 1)
45
- end
36
+ each_part(body) do |part|
37
+ break_line_before(part, node, corrector, 1)
46
38
  end
47
39
 
48
40
  break_line_before(node.loc.end, node, corrector, 0)
49
41
 
42
+ eol_comment = end_of_line_comment(node.source_range.line)
50
43
  move_comment(eol_comment, node, corrector) if eol_comment
51
44
  end
52
45
  end
53
46
 
47
+ def end_of_line_comment(line)
48
+ processed_source.comments.find { |c| c.loc.line == line }
49
+ end
50
+
51
+ def each_part(body)
52
+ return unless body
53
+
54
+ if body.begin_type?
55
+ body.each_child_node { |part| yield part.source_range }
56
+ else
57
+ yield body.source_range
58
+ end
59
+ end
60
+
54
61
  def break_line_before(range, node, corrector, indent_steps)
55
62
  corrector.insert_before(
56
63
  range,
@@ -22,9 +22,8 @@ module RuboCop
22
22
  def on_method_def(_node, _method_name, args, _body)
23
23
  return unless args.loc.begin && args.loc.begin.is?('(')
24
24
  expr = args.source_range
25
- pos_before_left_paren = Parser::Source::Range.new(expr.source_buffer,
26
- expr.begin_pos - 1,
27
- expr.begin_pos)
25
+ pos_before_left_paren = range_between(expr.begin_pos - 1,
26
+ expr.begin_pos)
28
27
  return unless pos_before_left_paren.source =~ /\s/
29
28
 
30
29
  add_offense(pos_before_left_paren, pos_before_left_paren)
@@ -16,9 +16,9 @@ module RuboCop
16
16
  MSG = 'Do not leave space between `!` and its argument.'.freeze
17
17
 
18
18
  def on_send(node)
19
- if node.keyword_bang? && whitespace_after_bang_op?(node)
20
- add_offense(node, :expression)
21
- end
19
+ return unless node.keyword_bang? && whitespace_after_bang_op?(node)
20
+
21
+ add_offense(node, :expression)
22
22
  end
23
23
 
24
24
  def whitespace_after_bang_op?(node)
@@ -29,9 +29,7 @@ module RuboCop
29
29
  def autocorrect(node)
30
30
  lambda do |corrector|
31
31
  receiver, _method_name, *_args = *node
32
- space_range =
33
- Parser::Source::Range.new(node.loc.selector.source_buffer,
34
- node.loc.selector.end_pos,
32
+ space_range = range_between(node.loc.selector.end_pos,
35
33
  receiver.source_range.begin_pos)
36
34
  corrector.remove(space_range)
37
35
  end