rubocop 0.35.1 → 0.36.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 (385) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +164 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +72 -21
  5. data/bin/rubocop +1 -0
  6. data/config/default.yml +167 -18
  7. data/config/disabled.yml +19 -6
  8. data/config/enabled.yml +159 -14
  9. data/lib/rubocop.rb +67 -26
  10. data/lib/rubocop/ast_node.rb +488 -14
  11. data/lib/rubocop/ast_node/builder.rb +24 -0
  12. data/lib/rubocop/ast_node/sexp.rb +13 -0
  13. data/lib/rubocop/cached_data.rb +58 -0
  14. data/lib/rubocop/cli.rb +47 -10
  15. data/lib/rubocop/comment_config.rb +9 -2
  16. data/lib/rubocop/config.rb +99 -31
  17. data/lib/rubocop/config_loader.rb +23 -14
  18. data/lib/rubocop/config_store.rb +1 -0
  19. data/lib/rubocop/cop/autocorrect_logic.rb +2 -1
  20. data/lib/rubocop/cop/commissioner.rb +3 -5
  21. data/lib/rubocop/cop/cop.rb +23 -17
  22. data/lib/rubocop/cop/corrector.rb +25 -0
  23. data/lib/rubocop/cop/force.rb +1 -0
  24. data/lib/rubocop/cop/ignored_node.rb +3 -2
  25. data/lib/rubocop/cop/lint/ambiguous_operator.rb +2 -1
  26. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +2 -1
  27. data/lib/rubocop/cop/lint/assignment_in_condition.rb +4 -3
  28. data/lib/rubocop/cop/lint/block_alignment.rb +29 -91
  29. data/lib/rubocop/cop/lint/circular_argument_reference.rb +2 -1
  30. data/lib/rubocop/cop/lint/condition_position.rb +2 -1
  31. data/lib/rubocop/cop/lint/debugger.rb +29 -12
  32. data/lib/rubocop/cop/lint/def_end_alignment.rb +16 -18
  33. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +6 -6
  34. data/lib/rubocop/cop/lint/duplicate_methods.rb +98 -74
  35. data/lib/rubocop/cop/lint/duplicated_key.rb +3 -2
  36. data/lib/rubocop/cop/lint/each_with_object_argument.rb +3 -2
  37. data/lib/rubocop/cop/lint/else_layout.rb +2 -1
  38. data/lib/rubocop/cop/lint/empty_ensure.rb +2 -1
  39. data/lib/rubocop/cop/lint/empty_interpolation.rb +2 -1
  40. data/lib/rubocop/cop/lint/end_alignment.rb +77 -39
  41. data/lib/rubocop/cop/lint/end_in_method.rb +2 -1
  42. data/lib/rubocop/cop/lint/ensure_return.rb +2 -1
  43. data/lib/rubocop/cop/lint/eval.rb +2 -1
  44. data/lib/rubocop/cop/lint/float_out_of_range.rb +31 -0
  45. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +14 -30
  46. data/lib/rubocop/cop/lint/handle_exceptions.rb +2 -1
  47. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +85 -0
  48. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +120 -0
  49. data/lib/rubocop/cop/lint/invalid_character_literal.rb +3 -1
  50. data/lib/rubocop/cop/lint/literal_in_condition.rb +6 -9
  51. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +6 -9
  52. data/lib/rubocop/cop/lint/loop.rb +2 -1
  53. data/lib/rubocop/cop/lint/nested_method_definition.rb +19 -3
  54. data/lib/rubocop/cop/lint/next_without_accumulator.rb +38 -0
  55. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +5 -8
  56. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +8 -6
  57. data/lib/rubocop/cop/lint/rand_one.rb +36 -0
  58. data/lib/rubocop/cop/lint/require_parentheses.rb +6 -5
  59. data/lib/rubocop/cop/lint/rescue_exception.rb +3 -2
  60. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +2 -1
  61. data/lib/rubocop/cop/lint/string_conversion_in_interpolation.rb +6 -4
  62. data/lib/rubocop/cop/lint/syntax.rb +9 -5
  63. data/lib/rubocop/cop/lint/underscore_prefixed_variable_name.rb +3 -2
  64. data/lib/rubocop/cop/lint/unneeded_disable.rb +121 -18
  65. data/lib/rubocop/cop/lint/unreachable_code.rb +5 -4
  66. data/lib/rubocop/cop/lint/unused_block_argument.rb +9 -7
  67. data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -1
  68. data/lib/rubocop/cop/lint/useless_access_modifier.rb +56 -29
  69. data/lib/rubocop/cop/lint/useless_assignment.rb +4 -16
  70. data/lib/rubocop/cop/lint/useless_comparison.rb +3 -2
  71. data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +2 -1
  72. data/lib/rubocop/cop/lint/useless_setter_call.rb +14 -20
  73. data/lib/rubocop/cop/lint/void.rb +10 -11
  74. data/lib/rubocop/cop/metrics/abc_size.rb +3 -1
  75. data/lib/rubocop/cop/metrics/block_nesting.rb +2 -1
  76. data/lib/rubocop/cop/metrics/class_length.rb +1 -0
  77. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +4 -2
  78. data/lib/rubocop/cop/metrics/line_length.rb +35 -13
  79. data/lib/rubocop/cop/metrics/method_length.rb +2 -1
  80. data/lib/rubocop/cop/metrics/module_length.rb +1 -0
  81. data/lib/rubocop/cop/metrics/parameter_lists.rb +2 -1
  82. data/lib/rubocop/cop/metrics/perceived_complexity.rb +4 -2
  83. data/lib/rubocop/cop/mixin/access_modifier_node.rb +3 -10
  84. data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -0
  85. data/lib/rubocop/cop/mixin/array_hash_indentation.rb +80 -0
  86. data/lib/rubocop/cop/mixin/array_syntax.rb +2 -1
  87. data/lib/rubocop/cop/mixin/autocorrect_alignment.rb +14 -20
  88. data/lib/rubocop/cop/mixin/autocorrect_unless_changing_ast.rb +5 -4
  89. data/lib/rubocop/cop/mixin/check_assignment.rb +20 -15
  90. data/lib/rubocop/cop/mixin/classish_length.rb +1 -0
  91. data/lib/rubocop/cop/mixin/code_length.rb +1 -0
  92. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +17 -15
  93. data/lib/rubocop/cop/mixin/configurable_max.rb +1 -0
  94. data/lib/rubocop/cop/mixin/configurable_naming.rb +4 -0
  95. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +9 -4
  96. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +24 -16
  97. data/lib/rubocop/cop/mixin/first_element_line_break.rb +3 -2
  98. data/lib/rubocop/cop/mixin/hash_node.rb +15 -0
  99. data/lib/rubocop/cop/mixin/if_node.rb +1 -0
  100. data/lib/rubocop/cop/mixin/method_complexity.rb +1 -0
  101. data/lib/rubocop/cop/mixin/method_preference.rb +1 -0
  102. data/lib/rubocop/cop/mixin/min_body_length.rb +1 -0
  103. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +177 -0
  104. data/lib/rubocop/cop/mixin/negative_conditional.rb +1 -0
  105. data/lib/rubocop/cop/mixin/on_method_def.rb +6 -5
  106. data/lib/rubocop/cop/mixin/on_normal_if_unless.rb +1 -0
  107. data/lib/rubocop/cop/mixin/parentheses.rb +22 -0
  108. data/lib/rubocop/cop/mixin/parser_diagnostic.rb +1 -0
  109. data/lib/rubocop/cop/mixin/percent_literal.rb +1 -0
  110. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +79 -0
  111. data/lib/rubocop/cop/mixin/safe_assignment.rb +1 -0
  112. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +2 -1
  113. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +2 -1
  114. data/lib/rubocop/cop/mixin/space_inside.rb +2 -1
  115. data/lib/rubocop/cop/mixin/statement_modifier.rb +6 -5
  116. data/lib/rubocop/cop/mixin/string_help.rb +2 -9
  117. data/lib/rubocop/cop/mixin/string_literals_help.rb +13 -7
  118. data/lib/rubocop/cop/mixin/surrounding_space.rb +3 -2
  119. data/lib/rubocop/cop/mixin/trailing_comma.rb +134 -0
  120. data/lib/rubocop/cop/mixin/unused_argument.rb +1 -0
  121. data/lib/rubocop/cop/offense.rb +19 -14
  122. data/lib/rubocop/cop/performance/case_when_splat.rb +8 -8
  123. data/lib/rubocop/cop/performance/casecmp.rb +54 -0
  124. data/lib/rubocop/cop/performance/count.rb +10 -9
  125. data/lib/rubocop/cop/performance/detect.rb +6 -5
  126. data/lib/rubocop/cop/performance/double_start_end_with.rb +65 -0
  127. data/lib/rubocop/cop/performance/end_with.rb +55 -0
  128. data/lib/rubocop/cop/performance/fixed_size.rb +1 -0
  129. data/lib/rubocop/cop/performance/flat_map.rb +9 -8
  130. data/lib/rubocop/cop/performance/hash_each.rb +86 -0
  131. data/lib/rubocop/cop/performance/lstrip_rstrip.rb +44 -0
  132. data/lib/rubocop/cop/performance/range_include.rb +40 -0
  133. data/lib/rubocop/cop/performance/redundant_block_call.rb +57 -0
  134. data/lib/rubocop/cop/performance/redundant_match.rb +51 -0
  135. data/lib/rubocop/cop/performance/redundant_merge.rb +85 -0
  136. data/lib/rubocop/cop/performance/redundant_sort_by.rb +45 -0
  137. data/lib/rubocop/cop/performance/reverse_each.rb +3 -2
  138. data/lib/rubocop/cop/performance/sample.rb +6 -5
  139. data/lib/rubocop/cop/performance/size.rb +2 -1
  140. data/lib/rubocop/cop/performance/start_with.rb +58 -0
  141. data/lib/rubocop/cop/performance/string_replacement.rb +18 -23
  142. data/lib/rubocop/cop/performance/times_map.rb +49 -0
  143. data/lib/rubocop/cop/rails/action_filter.rb +4 -3
  144. data/lib/rubocop/cop/rails/date.rb +5 -4
  145. data/lib/rubocop/cop/rails/delegate.rb +3 -2
  146. data/lib/rubocop/cop/rails/find_by.rb +20 -14
  147. data/lib/rubocop/cop/rails/find_each.rb +23 -2
  148. data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +3 -2
  149. data/lib/rubocop/cop/rails/output.rb +4 -2
  150. data/lib/rubocop/cop/rails/pluralization_grammar.rb +3 -2
  151. data/lib/rubocop/cop/rails/read_write_attribute.rb +5 -7
  152. data/lib/rubocop/cop/rails/scope_args.rb +3 -2
  153. data/lib/rubocop/cop/rails/time_zone.rb +14 -10
  154. data/lib/rubocop/cop/rails/validation.rb +4 -3
  155. data/lib/rubocop/cop/severity.rb +8 -7
  156. data/lib/rubocop/cop/style/access_modifier_indentation.rb +5 -4
  157. data/lib/rubocop/cop/style/accessor_method_name.rb +1 -0
  158. data/lib/rubocop/cop/style/alias.rb +84 -24
  159. data/lib/rubocop/cop/style/align_array.rb +2 -1
  160. data/lib/rubocop/cop/style/align_hash.rb +13 -14
  161. data/lib/rubocop/cop/style/align_parameters.rb +3 -2
  162. data/lib/rubocop/cop/style/and_or.rb +9 -7
  163. data/lib/rubocop/cop/style/array_join.rb +5 -5
  164. data/lib/rubocop/cop/style/ascii_comments.rb +2 -1
  165. data/lib/rubocop/cop/style/ascii_identifiers.rb +2 -1
  166. data/lib/rubocop/cop/style/attr.rb +30 -5
  167. data/lib/rubocop/cop/style/auto_resource_cleanup.rb +3 -3
  168. data/lib/rubocop/cop/style/bare_percent_literals.rb +2 -1
  169. data/lib/rubocop/cop/style/begin_block.rb +2 -1
  170. data/lib/rubocop/cop/style/block_comments.rb +2 -1
  171. data/lib/rubocop/cop/style/block_delimiters.rb +10 -9
  172. data/lib/rubocop/cop/style/block_end_newline.rb +3 -2
  173. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +9 -8
  174. data/lib/rubocop/cop/style/case_equality.rb +2 -1
  175. data/lib/rubocop/cop/style/case_indentation.rb +2 -1
  176. data/lib/rubocop/cop/style/character_literal.rb +11 -7
  177. data/lib/rubocop/cop/style/class_and_module_camel_case.rb +2 -1
  178. data/lib/rubocop/cop/style/class_and_module_children.rb +3 -2
  179. data/lib/rubocop/cop/style/class_check.rb +2 -1
  180. data/lib/rubocop/cop/style/class_methods.rb +2 -1
  181. data/lib/rubocop/cop/style/class_vars.rb +2 -1
  182. data/lib/rubocop/cop/style/closing_parenthesis_indentation.rb +3 -2
  183. data/lib/rubocop/cop/style/collection_methods.rb +2 -1
  184. data/lib/rubocop/cop/style/colon_method_call.rb +3 -2
  185. data/lib/rubocop/cop/style/command_literal.rb +8 -7
  186. data/lib/rubocop/cop/style/comment_annotation.rb +3 -2
  187. data/lib/rubocop/cop/style/comment_indentation.rb +4 -6
  188. data/lib/rubocop/cop/style/conditional_assignment.rb +362 -0
  189. data/lib/rubocop/cop/style/constant_name.rb +2 -1
  190. data/lib/rubocop/cop/style/copyright.rb +7 -6
  191. data/lib/rubocop/cop/style/def_with_parentheses.rb +2 -1
  192. data/lib/rubocop/cop/style/deprecated_hash_methods.rb +3 -2
  193. data/lib/rubocop/cop/style/documentation.rb +7 -11
  194. data/lib/rubocop/cop/style/dot_position.rb +3 -2
  195. data/lib/rubocop/cop/style/double_negation.rb +2 -1
  196. data/lib/rubocop/cop/style/each_with_object.rb +4 -3
  197. data/lib/rubocop/cop/style/else_alignment.rb +3 -2
  198. data/lib/rubocop/cop/style/empty_else.rb +4 -3
  199. data/lib/rubocop/cop/style/empty_line_between_defs.rb +2 -1
  200. data/lib/rubocop/cop/style/empty_lines.rb +10 -4
  201. data/lib/rubocop/cop/style/empty_lines_around_access_modifier.rb +13 -5
  202. data/lib/rubocop/cop/style/empty_lines_around_block_body.rb +7 -3
  203. data/lib/rubocop/cop/style/empty_lines_around_class_body.rb +6 -3
  204. data/lib/rubocop/cop/style/empty_lines_around_method_body.rb +4 -3
  205. data/lib/rubocop/cop/style/empty_lines_around_module_body.rb +4 -2
  206. data/lib/rubocop/cop/style/empty_literal.rb +20 -5
  207. data/lib/rubocop/cop/style/encoding.rb +8 -11
  208. data/lib/rubocop/cop/style/end_block.rb +3 -1
  209. data/lib/rubocop/cop/style/end_of_line.rb +2 -1
  210. data/lib/rubocop/cop/style/even_odd.rb +4 -3
  211. data/lib/rubocop/cop/style/extra_spacing.rb +110 -74
  212. data/lib/rubocop/cop/style/file_name.rb +103 -6
  213. data/lib/rubocop/cop/style/first_array_element_line_break.rb +3 -2
  214. data/lib/rubocop/cop/style/first_hash_element_line_break.rb +5 -6
  215. data/lib/rubocop/cop/style/first_method_argument_line_break.rb +14 -1
  216. data/lib/rubocop/cop/style/first_method_parameter_line_break.rb +2 -1
  217. data/lib/rubocop/cop/style/first_parameter_indentation.rb +6 -4
  218. data/lib/rubocop/cop/style/flip_flop.rb +2 -1
  219. data/lib/rubocop/cop/style/for.rb +2 -1
  220. data/lib/rubocop/cop/style/format_string.rb +1 -0
  221. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +89 -0
  222. data/lib/rubocop/cop/style/global_vars.rb +2 -1
  223. data/lib/rubocop/cop/style/guard_clause.rb +63 -11
  224. data/lib/rubocop/cop/style/hash_syntax.rb +10 -10
  225. data/lib/rubocop/cop/style/identical_conditional_branches.rb +93 -0
  226. data/lib/rubocop/cop/style/if_inside_else.rb +49 -0
  227. data/lib/rubocop/cop/style/if_unless_modifier.rb +6 -5
  228. data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -1
  229. data/lib/rubocop/cop/style/indent_array.rb +89 -38
  230. data/lib/rubocop/cop/style/indent_assignment.rb +43 -0
  231. data/lib/rubocop/cop/style/indent_hash.rb +16 -77
  232. data/lib/rubocop/cop/style/indentation_consistency.rb +2 -1
  233. data/lib/rubocop/cop/style/indentation_width.rb +11 -11
  234. data/lib/rubocop/cop/style/infinite_loop.rb +5 -9
  235. data/lib/rubocop/cop/style/initial_indentation.rb +2 -1
  236. data/lib/rubocop/cop/style/inline_comment.rb +2 -1
  237. data/lib/rubocop/cop/style/lambda.rb +14 -11
  238. data/lib/rubocop/cop/style/lambda_call.rb +4 -4
  239. data/lib/rubocop/cop/style/leading_comment_space.rb +2 -1
  240. data/lib/rubocop/cop/style/line_end_concatenation.rb +3 -1
  241. data/lib/rubocop/cop/style/method_call_parentheses.rb +9 -1
  242. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +3 -2
  243. data/lib/rubocop/cop/style/method_def_parentheses.rb +4 -4
  244. data/lib/rubocop/cop/style/method_name.rb +1 -0
  245. data/lib/rubocop/cop/style/missing_else.rb +5 -3
  246. data/lib/rubocop/cop/style/module_function.rb +2 -1
  247. data/lib/rubocop/cop/style/multiline_array_brace_layout.rb +95 -0
  248. data/lib/rubocop/cop/style/multiline_assignment_layout.rb +91 -0
  249. data/lib/rubocop/cop/style/multiline_block_chain.rb +3 -2
  250. data/lib/rubocop/cop/style/multiline_block_layout.rb +11 -9
  251. data/lib/rubocop/cop/style/multiline_if_then.rb +1 -0
  252. data/lib/rubocop/cop/style/multiline_method_call_indentation.rb +137 -0
  253. data/lib/rubocop/cop/style/multiline_operation_indentation.rb +25 -135
  254. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +2 -1
  255. data/lib/rubocop/cop/style/mutable_constant.rb +4 -5
  256. data/lib/rubocop/cop/style/negated_if.rb +3 -3
  257. data/lib/rubocop/cop/style/negated_while.rb +3 -3
  258. data/lib/rubocop/cop/style/nested_modifier.rb +6 -5
  259. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +48 -0
  260. data/lib/rubocop/cop/style/nested_ternary_operator.rb +2 -1
  261. data/lib/rubocop/cop/style/next.rb +79 -15
  262. data/lib/rubocop/cop/style/nil_comparison.rb +5 -5
  263. data/lib/rubocop/cop/style/non_nil_check.rb +5 -5
  264. data/lib/rubocop/cop/style/not.rb +5 -9
  265. data/lib/rubocop/cop/style/numeric_literals.rb +5 -4
  266. data/lib/rubocop/cop/style/one_line_conditional.rb +3 -2
  267. data/lib/rubocop/cop/style/op_method.rb +7 -4
  268. data/lib/rubocop/cop/style/option_hash.rb +13 -7
  269. data/lib/rubocop/cop/style/optional_arguments.rb +3 -2
  270. data/lib/rubocop/cop/style/parallel_assignment.rb +40 -16
  271. data/lib/rubocop/cop/style/parentheses_around_condition.rb +3 -16
  272. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +3 -2
  273. data/lib/rubocop/cop/style/percent_q_literals.rb +3 -6
  274. data/lib/rubocop/cop/style/perl_backrefs.rb +4 -3
  275. data/lib/rubocop/cop/style/predicate_name.rb +1 -0
  276. data/lib/rubocop/cop/style/proc.rb +3 -2
  277. data/lib/rubocop/cop/style/raise_args.rb +2 -1
  278. data/lib/rubocop/cop/style/redundant_begin.rb +2 -1
  279. data/lib/rubocop/cop/style/redundant_exception.rb +5 -5
  280. data/lib/rubocop/cop/style/redundant_freeze.rb +5 -4
  281. data/lib/rubocop/cop/style/redundant_parentheses.rb +80 -0
  282. data/lib/rubocop/cop/style/redundant_return.rb +5 -4
  283. data/lib/rubocop/cop/style/redundant_self.rb +7 -8
  284. data/lib/rubocop/cop/style/regexp_literal.rb +9 -8
  285. data/lib/rubocop/cop/style/rescue_ensure_alignment.rb +3 -2
  286. data/lib/rubocop/cop/style/rescue_modifier.rb +11 -9
  287. data/lib/rubocop/cop/style/self_assignment.rb +4 -5
  288. data/lib/rubocop/cop/style/semicolon.rb +3 -2
  289. data/lib/rubocop/cop/style/send.rb +3 -1
  290. data/lib/rubocop/cop/style/signal_exception.rb +5 -3
  291. data/lib/rubocop/cop/style/single_line_block_params.rb +2 -1
  292. data/lib/rubocop/cop/style/single_line_methods.rb +7 -7
  293. data/lib/rubocop/cop/style/space_after_colon.rb +2 -1
  294. data/lib/rubocop/cop/style/space_after_comma.rb +1 -0
  295. data/lib/rubocop/cop/style/space_after_control_keyword.rb +5 -5
  296. data/lib/rubocop/cop/style/space_after_method_name.rb +3 -2
  297. data/lib/rubocop/cop/style/space_after_not.rb +4 -3
  298. data/lib/rubocop/cop/style/space_after_semicolon.rb +1 -0
  299. data/lib/rubocop/cop/style/space_around_block_parameters.rb +8 -7
  300. data/lib/rubocop/cop/style/space_around_equals_in_parameter_default.rb +1 -0
  301. data/lib/rubocop/cop/style/space_around_operators.rb +72 -32
  302. data/lib/rubocop/cop/style/space_before_block_braces.rb +2 -1
  303. data/lib/rubocop/cop/style/space_before_comma.rb +1 -0
  304. data/lib/rubocop/cop/style/space_before_comment.rb +2 -1
  305. data/lib/rubocop/cop/style/{single_space_before_first_arg.rb → space_before_first_arg.rb} +13 -4
  306. data/lib/rubocop/cop/style/space_before_modifier_keyword.rb +4 -3
  307. data/lib/rubocop/cop/style/space_before_semicolon.rb +1 -0
  308. data/lib/rubocop/cop/style/space_inside_block_braces.rb +3 -2
  309. data/lib/rubocop/cop/style/space_inside_brackets.rb +1 -0
  310. data/lib/rubocop/cop/style/space_inside_hash_literal_braces.rb +4 -1
  311. data/lib/rubocop/cop/style/space_inside_parens.rb +1 -0
  312. data/lib/rubocop/cop/style/space_inside_range_literal.rb +5 -4
  313. data/lib/rubocop/cop/style/space_inside_string_interpolation.rb +8 -17
  314. data/lib/rubocop/cop/style/special_global_vars.rb +97 -52
  315. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +16 -9
  316. data/lib/rubocop/cop/style/string_literals.rb +41 -1
  317. data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +2 -1
  318. data/lib/rubocop/cop/style/string_methods.rb +2 -1
  319. data/lib/rubocop/cop/style/struct_inheritance.rb +3 -2
  320. data/lib/rubocop/cop/style/symbol_array.rb +74 -7
  321. data/lib/rubocop/cop/style/symbol_literal.rb +4 -7
  322. data/lib/rubocop/cop/style/symbol_proc.rb +11 -7
  323. data/lib/rubocop/cop/style/tab.rb +25 -2
  324. data/lib/rubocop/cop/style/trailing_blank_lines.rb +1 -2
  325. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +45 -0
  326. data/lib/rubocop/cop/style/trailing_comma_in_literal.rb +56 -0
  327. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +8 -7
  328. data/lib/rubocop/cop/style/trailing_whitespace.rb +2 -1
  329. data/lib/rubocop/cop/style/trivial_accessors.rb +18 -9
  330. data/lib/rubocop/cop/style/unless_else.rb +2 -1
  331. data/lib/rubocop/cop/style/unneeded_capital_w.rb +4 -3
  332. data/lib/rubocop/cop/style/unneeded_interpolation.rb +87 -0
  333. data/lib/rubocop/cop/style/unneeded_percent_q.rb +23 -7
  334. data/lib/rubocop/cop/style/variable_interpolation.rb +8 -6
  335. data/lib/rubocop/cop/style/variable_name.rb +1 -0
  336. data/lib/rubocop/cop/style/when_then.rb +2 -1
  337. data/lib/rubocop/cop/style/while_until_do.rb +3 -2
  338. data/lib/rubocop/cop/style/while_until_modifier.rb +3 -4
  339. data/lib/rubocop/cop/style/word_array.rb +74 -51
  340. data/lib/rubocop/cop/team.rb +21 -15
  341. data/lib/rubocop/cop/util.rb +102 -69
  342. data/lib/rubocop/cop/variable_force.rb +2 -1
  343. data/lib/rubocop/cop/variable_force/assignment.rb +2 -1
  344. data/lib/rubocop/cop/variable_force/locatable.rb +1 -0
  345. data/lib/rubocop/cop/variable_force/reference.rb +1 -0
  346. data/lib/rubocop/cop/variable_force/scope.rb +2 -1
  347. data/lib/rubocop/cop/variable_force/variable.rb +2 -1
  348. data/lib/rubocop/cop/variable_force/variable_table.rb +2 -1
  349. data/lib/rubocop/error.rb +12 -0
  350. data/lib/rubocop/formatter/base_formatter.rb +10 -1
  351. data/lib/rubocop/formatter/clang_style_formatter.rb +1 -0
  352. data/lib/rubocop/formatter/colorizable.rb +6 -1
  353. data/lib/rubocop/formatter/disabled_config_formatter.rb +29 -15
  354. data/lib/rubocop/formatter/disabled_lines_formatter.rb +3 -1
  355. data/lib/rubocop/formatter/emacs_style_formatter.rb +7 -3
  356. data/lib/rubocop/formatter/file_list_formatter.rb +1 -0
  357. data/lib/rubocop/formatter/formatter_set.rb +10 -19
  358. data/lib/rubocop/formatter/fuubar_style_formatter.rb +2 -1
  359. data/lib/rubocop/formatter/html_formatter.rb +15 -14
  360. data/lib/rubocop/formatter/json_formatter.rb +2 -1
  361. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -0
  362. data/lib/rubocop/formatter/progress_formatter.rb +3 -3
  363. data/lib/rubocop/formatter/simple_text_formatter.rb +50 -17
  364. data/lib/rubocop/formatter/text_util.rb +8 -10
  365. data/lib/rubocop/formatter/worst_offenders_formatter.rb +61 -0
  366. data/lib/rubocop/name_similarity.rb +22 -0
  367. data/lib/rubocop/node_pattern.rb +126 -35
  368. data/lib/rubocop/options.rb +28 -19
  369. data/lib/rubocop/path_util.rb +1 -0
  370. data/lib/rubocop/processed_source.rb +41 -16
  371. data/lib/rubocop/rake_task.rb +6 -9
  372. data/lib/rubocop/remote_config.rb +1 -0
  373. data/lib/rubocop/result_cache.rb +60 -43
  374. data/lib/rubocop/runner.rb +48 -45
  375. data/lib/rubocop/string_util.rb +1 -0
  376. data/lib/rubocop/target_finder.rb +2 -1
  377. data/lib/rubocop/token.rb +1 -0
  378. data/lib/rubocop/version.rb +3 -2
  379. data/lib/rubocop/warning.rb +1 -0
  380. data/relnotes/v0.36.0.md +306 -0
  381. data/rubocop.gemspec +3 -9
  382. metadata +48 -92
  383. data/lib/rubocop/cop/lint/space_before_first_arg.rb +0 -44
  384. data/lib/rubocop/cop/rails/default_scope.rb +0 -33
  385. data/lib/rubocop/cop/style/trailing_comma.rb +0 -182
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module RuboCop
4
5
  module Cop
@@ -21,7 +22,7 @@ module RuboCop
21
22
  include IfNode
22
23
 
23
24
  MSG = 'Use parentheses in the method call to avoid confusion about ' \
24
- 'precedence.'
25
+ 'precedence.'.freeze
25
26
 
26
27
  def on_send(node)
27
28
  _receiver, method_name, *args = *node
@@ -31,10 +32,10 @@ module RuboCop
31
32
 
32
33
  if ternary_op?(args.first)
33
34
  check_ternary(args.first, node)
34
- else
35
+ elsif predicate?(method_name)
35
36
  # We're only checking predicate methods. There would be false
36
37
  # positives otherwise.
37
- check_send(args.last, node) if predicate?(method_name)
38
+ check_send(args.last, node)
38
39
  end
39
40
  end
40
41
 
@@ -44,10 +45,10 @@ module RuboCop
44
45
  condition, = *arg
45
46
  return unless offense?(condition)
46
47
 
47
- expr = node.loc.expression
48
+ expr = node.source_range
48
49
  range = Parser::Source::Range.new(expr.source_buffer,
49
50
  expr.begin_pos,
50
- condition.loc.expression.end_pos)
51
+ condition.source_range.end_pos)
51
52
  add_offense(range, range)
52
53
  end
53
54
 
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module RuboCop
4
5
  module Cop
@@ -6,7 +7,7 @@ module RuboCop
6
7
  # This cop checks for *rescue* blocks targeting the Exception class.
7
8
  class RescueException < Cop
8
9
  MSG = 'Avoid rescuing the `Exception` class. ' \
9
- 'Perhaps you meant to rescue `StandardError`?'
10
+ 'Perhaps you meant to rescue `StandardError`?'.freeze
10
11
 
11
12
  def on_resbody(node)
12
13
  return unless node.children.first
@@ -17,7 +18,7 @@ module RuboCop
17
18
  end
18
19
 
19
20
  def targets_exception?(rescue_arg_node)
20
- Util.const_name(rescue_arg_node) == 'Exception'
21
+ rescue_arg_node.const_name == 'Exception'
21
22
  end
22
23
  end
23
24
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module RuboCop
4
5
  module Cop
@@ -8,7 +9,7 @@ module RuboCop
8
9
  # This is a mimic of the warning
9
10
  # "shadowing outer local variable - foo" from `ruby -cw`.
10
11
  class ShadowingOuterLocalVariable < Cop
11
- MSG = 'Shadowing outer local variable - `%s`.'
12
+ MSG = 'Shadowing outer local variable - `%s`.'.freeze
12
13
 
13
14
  def join_force?(force_class)
14
15
  force_class == VariableForce
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module RuboCop
4
5
  module Cop
@@ -10,8 +11,9 @@ module RuboCop
10
11
  #
11
12
  # "result is #{something.to_s}"
12
13
  class StringConversionInInterpolation < Cop
13
- MSG_DEFAULT = 'Redundant use of `Object#to_s` in interpolation.'
14
- MSG_SELF = 'Use `self` instead of `Object#to_s` in interpolation.'
14
+ MSG_DEFAULT = 'Redundant use of `Object#to_s` in interpolation.'.freeze
15
+ MSG_SELF = 'Use `self` instead of `Object#to_s` in ' \
16
+ 'interpolation.'.freeze
15
17
 
16
18
  def on_dstr(node)
17
19
  node.children.select { |n| n.type == :begin }.each do |begin_node|
@@ -35,9 +37,9 @@ module RuboCop
35
37
  lambda do |corrector|
36
38
  receiver, _method_name, *_args = *node
37
39
  corrector.replace(
38
- node.loc.expression,
40
+ node.source_range,
39
41
  if receiver
40
- receiver.loc.expression.source
42
+ receiver.source
41
43
  else
42
44
  'self'
43
45
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module RuboCop
4
5
  module Cop
@@ -6,10 +7,11 @@ module RuboCop
6
7
  # This is actually not a cop and inspects nothing. It just provides
7
8
  # methods to repack Parser's diagnostics/errors into RuboCop's offenses.
8
9
  module Syntax
9
- PseudoSourceRange = Struct.new(:line, :column, :source_line)
10
+ PseudoSourceRange = Struct.new(:line, :column, :source_line, :begin_pos,
11
+ :end_pos)
10
12
 
11
13
  COP_NAME = 'Syntax'.freeze
12
- ERROR_SOURCE_RANGE = PseudoSourceRange.new(1, 0, '').freeze
14
+ ERROR_SOURCE_RANGE = PseudoSourceRange.new(1, 0, '', 0, 1).freeze
13
15
 
14
16
  def self.offenses_from_processed_source(processed_source)
15
17
  offenses = []
@@ -19,17 +21,19 @@ module RuboCop
19
21
  end
20
22
 
21
23
  processed_source.diagnostics.each do |diagnostic|
22
- offenses << offense_from_diagnostic(diagnostic)
24
+ offenses << offense_from_diagnostic(diagnostic,
25
+ processed_source.ruby_version)
23
26
  end
24
27
 
25
28
  offenses
26
29
  end
27
30
 
28
- def self.offense_from_diagnostic(diagnostic)
31
+ def self.offense_from_diagnostic(diagnostic, ruby_version)
29
32
  Offense.new(
30
33
  diagnostic.level,
31
34
  diagnostic.location,
32
- diagnostic.message,
35
+ "#{diagnostic.message}\n(Using Ruby #{ruby_version} parser; " \
36
+ 'configure using `TargetRubyVersion` parameter, under `AllCops`)',
33
37
  COP_NAME
34
38
  )
35
39
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module RuboCop
4
5
  module Cop
@@ -6,7 +7,7 @@ module RuboCop
6
7
  # This cop checks for underscore-prefixed variables that are actually
7
8
  # used.
8
9
  class UnderscorePrefixedVariableName < Cop
9
- MSG = 'Do not use prefix `_` for a variable that is used.'
10
+ MSG = 'Do not use prefix `_` for a variable that is used.'.freeze
10
11
 
11
12
  def join_force?(force_class)
12
13
  force_class == VariableForce
@@ -26,7 +27,7 @@ module RuboCop
26
27
  node = variable.declaration_node
27
28
 
28
29
  location = if node.type == :match_with_lvasgn
29
- node.children.first.loc.expression
30
+ node.children.first.source_range
30
31
  else
31
32
  node.loc.name
32
33
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module RuboCop
4
5
  module Cop
@@ -13,59 +14,161 @@ module RuboCop
13
14
  # execution. The reason it can't be implemented as a normal cop is that
14
15
  # it depends on the results of all other cops to do its work.
15
16
  class UnneededDisable < Cop
16
- COP_NAME = 'Lint/UnneededDisable'
17
+ include NameSimilarity
18
+
19
+ COP_NAME = 'Lint/UnneededDisable'.freeze
17
20
 
18
21
  def check(offenses, cop_disabled_line_ranges, comments)
19
- unneeded_cops = {}
22
+ unneeded_cops = Hash.new { |h, k| h[k] = Set.new }
20
23
  disabled_ranges = cop_disabled_line_ranges[COP_NAME] || [0..0]
21
24
 
22
25
  cop_disabled_line_ranges.each do |cop, line_ranges|
23
- cop_offenses = offenses.select { |o| o.cop_name == cop }
26
+ line_ranges.each_cons(2) do |previous_range, range|
27
+ next if previous_range.end != range.begin
28
+
29
+ # If a cop is disabled in a range that begins on the same line as
30
+ # the end of the previous range, it means that the cop was
31
+ # already disabled by an earlier comment. So it's unneeded
32
+ # whether there are offenses or not.
33
+ comment = comments.find { |c| c.loc.line == range.begin }
34
+ unneeded_cops[comment].add(cop)
35
+ end
36
+
24
37
  line_ranges.each do |line_range|
25
38
  comment = comments.find { |c| c.loc.line == line_range.begin }
26
- unneeded_cop = find_unneeded(comment, offenses, cop, cop_offenses,
27
- line_range)
28
39
 
29
40
  unless all_disabled?(comment)
30
41
  next if ignore_offense?(disabled_ranges, line_range)
31
42
  end
32
43
 
33
- if unneeded_cop
34
- unneeded_cops[comment.loc.expression] ||= Set.new
35
- unneeded_cops[comment.loc.expression].add(unneeded_cop)
36
- end
44
+ unneeded_cop = find_unneeded(comment, offenses, cop, line_range)
45
+ unneeded_cops[comment].add(unneeded_cop) if unneeded_cop
37
46
  end
38
47
  end
39
48
 
40
49
  add_offenses(unneeded_cops)
41
50
  end
42
51
 
52
+ def autocorrect(args)
53
+ lambda do |corrector|
54
+ ranges, range = *args # ranges are sorted by position
55
+
56
+ if range.source.start_with?('#')
57
+ # eat the entire comment and following newline
58
+ corrector.remove(range_with_surrounding_space(range, :right))
59
+ else
60
+ # is there any cop between this one and the end of the line, which
61
+ # is NOT being removed?
62
+
63
+ if ends_its_line?(ranges.last) && trailing_range?(ranges, range)
64
+ # eat the comma on the left
65
+ range = range_with_surrounding_space(range, :left)
66
+ range = range_with_surrounding_comma(range, :left)
67
+ end
68
+
69
+ range = range_with_surrounding_comma(range, :right)
70
+ # eat following spaces up to EOL, but not the newline itself
71
+ range = range_with_surrounding_space(range, :right, nil, false)
72
+
73
+ corrector.remove(range)
74
+ end
75
+ end
76
+ end
77
+
43
78
  private
44
79
 
45
- def find_unneeded(comment, offenses, cop, cop_offenses, line_range)
80
+ def find_unneeded(comment, offenses, cop, line_range)
46
81
  if all_disabled?(comment)
47
- 'all cops' if offenses.none? { |o| line_range.include?(o.line) }
48
- elsif cop_offenses.none? { |o| line_range.include?(o.line) }
49
- cop
82
+ 'all' if offenses.none? { |o| line_range.cover?(o.line) }
83
+ else
84
+ cop_offenses = offenses.select { |o| o.cop_name == cop }
85
+ cop if cop_offenses.none? { |o| line_range.cover?(o.line) }
50
86
  end
51
87
  end
52
88
 
53
89
  def all_disabled?(comment)
54
- comment.loc.expression.source =~ /rubocop:disable\s+all\b/
90
+ comment.text =~ /rubocop:disable\s+all\b/
55
91
  end
56
92
 
57
93
  def ignore_offense?(disabled_ranges, line_range)
58
94
  disabled_ranges.any? do |range|
59
- range.include?(line_range.min) && range.include?(line_range.max)
95
+ range.cover?(line_range.min) && range.cover?(line_range.max)
60
96
  end
61
97
  end
62
98
 
99
+ def directive_count(comment)
100
+ match = comment.text.match(CommentConfig::COMMENT_DIRECTIVE_REGEXP)
101
+ _, cops_string = match.captures
102
+ cops_string.split(/,\s*/).size
103
+ end
104
+
63
105
  def add_offenses(unneeded_cops)
64
- unneeded_cops.each do |range, cops|
65
- add_offense(range, range,
66
- "Unnecessary disabling of #{cops.sort.join(', ')}.")
106
+ unneeded_cops.each do |comment, cops|
107
+ # Is the entire rubocop:disable line useless, or should just
108
+ # some of the mentioned cops be removed?
109
+ if all_disabled?(comment) ||
110
+ directive_count(comment) == cops.size
111
+ location = comment.loc.expression
112
+ cop_list = cops.sort.map { |c| describe(c) }
113
+ add_offense([[location], location], location,
114
+ "Unnecessary disabling of #{cop_list.join(', ')}.")
115
+ else
116
+ cop_ranges = cops.map { |c| [c, cop_range(comment, c)] }
117
+ cop_ranges.sort_by! { |_, r| r.begin_pos }
118
+ ranges = cop_ranges.map { |_, r| r }
119
+
120
+ cop_ranges.each do |cop, range|
121
+ add_offense([ranges, range], range,
122
+ "Unnecessary disabling of #{describe(cop)}.")
123
+ end
124
+ end
67
125
  end
68
126
  end
127
+
128
+ def cop_range(comment, cop)
129
+ matching_range(comment.loc.expression, cop) ||
130
+ matching_range(comment.loc.expression, cop.split('/').last) ||
131
+ fail("Couldn't find #{cop} in comment: #{comment.text}")
132
+ end
133
+
134
+ def matching_range(haystack, needle)
135
+ offset = (haystack.source =~ Regexp.new(Regexp.escape(needle)))
136
+ return unless offset
137
+ offset += haystack.begin_pos
138
+ Parser::Source::Range.new(haystack.source_buffer, offset,
139
+ offset + needle.size)
140
+ end
141
+
142
+ def trailing_range?(ranges, range)
143
+ ranges
144
+ .drop_while { |r| !r.equal?(range) }
145
+ .each_cons(2)
146
+ .map { |r1, r2| r1.end.join(r2.begin).source }
147
+ .all? { |intervening| intervening =~ /\A\s*,\s*\Z/ }
148
+ end
149
+
150
+ def describe(cop)
151
+ if cop == 'all'
152
+ 'all cops'
153
+ elsif all_cop_names.include?(cop)
154
+ "`#{cop}`"
155
+ else
156
+ similar = find_similar_name(cop, [])
157
+ if similar
158
+ "`#{cop}` (did you mean `#{similar}`?)"
159
+ else
160
+ "`#{cop}` (unknown cop)"
161
+ end
162
+ end
163
+ end
164
+
165
+ def collect_variable_like_names(scope)
166
+ all_cop_names.each { |name| scope << name }
167
+ end
168
+
169
+ def all_cop_names
170
+ @all_cop_names ||= Cop.all.map(&:cop_name)
171
+ end
69
172
  end
70
173
  end
71
174
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module RuboCop
4
5
  module Cop
@@ -7,10 +8,10 @@ module RuboCop
7
8
  # The check are based on the presence of flow of control
8
9
  # statement in non-final position in *begin*(implicit) blocks.
9
10
  class UnreachableCode < Cop
10
- MSG = 'Unreachable code detected.'
11
+ MSG = 'Unreachable code detected.'.freeze
11
12
 
12
- NODE_TYPES = [:return, :next, :break, :retry, :redo]
13
- FLOW_COMMANDS = [:throw, :raise, :fail]
13
+ NODE_TYPES = [:return, :next, :break, :retry, :redo].freeze
14
+ FLOW_COMMANDS = [:throw, :raise, :fail].freeze
14
15
 
15
16
  def on_begin(node)
16
17
  expressions = *node
@@ -24,7 +25,7 @@ module RuboCop
24
25
  private
25
26
 
26
27
  def flow_command?(node)
27
- FLOW_COMMANDS.any? { |c| command?(c, node) }
28
+ FLOW_COMMANDS.any? { |c| node.command?(c) }
28
29
  end
29
30
  end
30
31
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module RuboCop
4
5
  module Cop
@@ -26,7 +27,8 @@ module RuboCop
26
27
  end
27
28
 
28
29
  def message(variable)
29
- message = "Unused #{variable_type(variable)} - `#{variable.name}`."
30
+ message = String.new("Unused #{variable_type(variable)} - " \
31
+ "`#{variable.name}`.")
30
32
 
31
33
  return message if variable.explicit_block_local_variable?
32
34
 
@@ -35,11 +37,11 @@ module RuboCop
35
37
  scope = variable.scope
36
38
  all_arguments = scope.variables.each_value.select(&:block_argument?)
37
39
 
38
- if lambda?(scope.node)
39
- message << message_for_lambda(variable, all_arguments)
40
- else
41
- message << message_for_normal_block(variable, all_arguments)
42
- end
40
+ message << if scope.node.lambda?
41
+ message_for_lambda(variable, all_arguments)
42
+ else
43
+ message_for_normal_block(variable, all_arguments)
44
+ end
43
45
 
44
46
  message
45
47
  end
@@ -65,7 +67,7 @@ module RuboCop
65
67
  end
66
68
 
67
69
  def message_for_lambda(variable, all_arguments)
68
- message = message_for_underscore_prefix(variable)
70
+ message = String.new(message_for_underscore_prefix(variable))
69
71
 
70
72
  if all_arguments.none?(&:referenced?)
71
73
  message << ' Also consider using a proc without arguments ' \
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module RuboCop
4
5
  module Cop
@@ -27,7 +28,7 @@ module RuboCop
27
28
  end
28
29
 
29
30
  def message(variable)
30
- message = "Unused method argument - `#{variable.name}`."
31
+ message = String.new("Unused method argument - `#{variable.name}`.")
31
32
 
32
33
  unless variable.keyword_argument?
33
34
  message << " If it's necessary, use `_` or `_#{variable.name}` " \
@@ -1,56 +1,83 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
2
4
  module RuboCop
3
5
  module Cop
4
6
  module Lint
5
- # This cop checks for access modifiers without any code.
7
+ # This cop checks for redundant access modifiers, including those with
8
+ # no code, those which are repeated, and leading `public` modifiers in
9
+ # a class or module body.
6
10
  #
7
11
  # @example
12
+ #
8
13
  # class Foo
9
- # private # This is useless
14
+ # public # this is redundant
15
+ #
16
+ # def method
17
+ # end
10
18
  #
11
- # def self.some_method
19
+ # private # this is not redundant
20
+ # def method2
12
21
  # end
22
+ #
23
+ # private # this is redundant
13
24
  # end
14
25
  class UselessAccessModifier < Cop
15
- include AccessModifierNode
16
-
17
- MSG = 'Useless `%s` access modifier.'
26
+ MSG = 'Useless `%s` access modifier.'.freeze
18
27
 
19
- def on_class(node)
20
- _name, _base_class, body = *node
21
- return unless body
28
+ def_node_matcher :access_modifier, <<-PATTERN
29
+ (send nil ${:public :protected :private})
30
+ PATTERN
22
31
 
23
- body_nodes = body.type == :begin ? body.children : [body]
32
+ def_node_matcher :method_definition?, <<-PATTERN
33
+ {def (send nil {:attr :attr_reader :attr_writer :attr_accessor} ...)}
34
+ PATTERN
24
35
 
25
- body_nodes.each do |child_node|
26
- check_for_access_modifier(child_node) ||
27
- check_for_instance_method(child_node)
28
- end
36
+ def on_class(node)
37
+ check_node(node.children[2]) # class body
38
+ end
29
39
 
30
- add_offense_for_access_modifier
40
+ def on_module(node)
41
+ check_node(node.children[1]) # module body
31
42
  end
32
43
 
33
44
  private
34
45
 
35
- def add_offense_for_access_modifier
36
- return unless @access_modifier_node
37
-
38
- _, modifier = *@access_modifier_node
39
- message = format(MSG, modifier)
40
- add_offense(@access_modifier_node, :expression, message)
46
+ def check_node(node)
47
+ return if node.nil?
48
+ if node.begin_type?
49
+ check_scope(node)
50
+ elsif (vis = access_modifier(node))
51
+ add_offense(node, :expression, format(MSG, vis))
52
+ end
41
53
  end
42
54
 
43
- def check_for_instance_method(node)
44
- return unless node.type == :def || node.type == :send
55
+ def check_scope(node, cur_vis = :public)
56
+ unused = nil
45
57
 
46
- @access_modifier_node = nil
47
- end
58
+ node.children.each do |child|
59
+ if (new_vis = access_modifier(child))
60
+ # does this modifier just repeat the existing visibility?
61
+ if new_vis == cur_vis
62
+ add_offense(child, :expression, format(MSG, cur_vis))
63
+ else
64
+ # was the previous modifier never applied to any defs?
65
+ add_offense(unused, :expression, format(MSG, cur_vis)) if unused
66
+ # once we have already warned about a certain modifier, don't
67
+ # warn again even if it is never applied to any method defs
68
+ unused = child
69
+ end
70
+ cur_vis = new_vis
71
+ elsif method_definition?(child)
72
+ unused = nil
73
+ elsif child.kwbegin_type?
74
+ cur_vis = check_scope(child, cur_vis)
75
+ end
76
+ end
48
77
 
49
- def check_for_access_modifier(node)
50
- return unless modifier_node?(node)
78
+ add_offense(unused, :expression, format(MSG, cur_vis)) if unused
51
79
 
52
- add_offense_for_access_modifier
53
- @access_modifier_node = node
80
+ cur_vis
54
81
  end
55
82
  end
56
83
  end