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,30 +1,339 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
- require 'astrolabe/node'
4
-
5
- module Astrolabe
6
- # RuboCop's extensions to Astrolabe::Node (which extends Parser::AST::Node)
4
+ module RuboCop
5
+ # `RuboCop::Node` is a subclass of `Parser::AST::Node`. It provides access to
6
+ # parent nodes and an object-oriented way to traverse an AST with the power
7
+ # of `Enumerable`.
7
8
  #
8
- # Contribute as much of this as possible to the `astrolabe` gem
9
- # If any of it is accepted, it can be deleted from here
9
+ # It has predicate methods for every node type, like this:
10
10
  #
11
- class Node
12
- # def_matcher can be used to define a pattern-matching method on Node:
13
- class << self
14
- extend RuboCop::NodePattern::Macros
11
+ # @example
12
+ # node.send_type? # Equivalent to: `node.type == :send`
13
+ # node.op_asgn_type? # Equivalent to: `node.type == :op_asgn`
14
+ #
15
+ # # Non-word characters (other than a-zA-Z0-9_) in type names are omitted.
16
+ # node.defined_type? # Equivalent to: `node.type == :defined?`
17
+ #
18
+ # # Find the first lvar node under the receiver node.
19
+ # lvar_node = node.each_descendant.find(&:lvar_type?)
20
+ #
21
+ class Node < Parser::AST::Node # rubocop:disable Metrics/ClassLength
22
+ include RuboCop::Sexp
23
+
24
+ COMPARISON_OPERATORS = [:==, :===, :!=, :<=, :>=, :>, :<, :<=>].freeze
25
+
26
+ TRUTHY_LITERALS = [:str, :dstr, :xstr, :int, :float, :sym, :dsym, :array,
27
+ :hash, :regexp, :true, :irange, :erange, :complex,
28
+ :rational].freeze
29
+ FALSEY_LITERALS = [:false, :nil].freeze
30
+ LITERALS = (TRUTHY_LITERALS + FALSEY_LITERALS).freeze
31
+ BASIC_LITERALS = LITERALS - [:dstr, :xstr, :dsym, :array, :hash, :irange,
32
+ :erange].freeze
33
+ MUTABLE_LITERALS = [:str, :dstr, :xstr, :array, :hash].freeze
34
+ IMMUTABLE_LITERALS = (LITERALS - MUTABLE_LITERALS).freeze
35
+
36
+ VARIABLES = [:ivar, :gvar, :cvar, :lvar].freeze
37
+ REFERENCES = [:nth_ref, :back_ref].freeze
38
+ KEYWORDS = [:alias, :and, :break, :case, :class, :def, :defs, :defined?,
39
+ :kwbegin, :do, :else, :ensure, :for, :if, :module, :next, :not,
40
+ :or, :postexe, :redo, :rescue, :retry, :return, :self, :super,
41
+ :zsuper, :then, :undef, :until, :when, :while, :yield].freeze
42
+ OPERATOR_KEYWORDS = [:and, :or].freeze
43
+ SPECIAL_KEYWORDS = %w(__FILE__ __LINE__ __ENCODING__).freeze
15
44
 
16
- # define both Node.method_name(node), and also node.method_name
45
+ # def_matcher can be used to define a pattern-matching method on Node
46
+ class << self
17
47
  def def_matcher(method_name, pattern_str)
18
- singleton_class.def_node_matcher method_name, pattern_str
19
- class_eval("def #{method_name}; Node.#{method_name}(self); end")
48
+ compiler = RuboCop::NodePattern::Compiler.new(pattern_str, 'self')
49
+ src = "def #{method_name}(" \
50
+ "#{compiler.emit_param_list});" \
51
+ "#{compiler.emit_method_code};end"
52
+
53
+ file, lineno = *caller.first.split(':')
54
+ class_eval(src, file, lineno.to_i)
55
+ end
56
+ end
57
+
58
+ # @see http://rubydoc.info/gems/ast/AST/Node:initialize
59
+ def initialize(type, children = [], properties = {})
60
+ @mutable_attributes = {}
61
+
62
+ # ::AST::Node#initialize freezes itself.
63
+ super
64
+
65
+ # #parent= may be invoked multiple times for a node because there are
66
+ # pending nodes while constructing AST and they are replaced later.
67
+ # For example, `lvar` and `send` type nodes are initially created as an
68
+ # `ident` type node and fixed to the appropriate type later.
69
+ # So, the #parent attribute needs to be mutable.
70
+ each_child_node do |child_node|
71
+ child_node.parent = self
72
+ end
73
+ end
74
+
75
+ Parser::Meta::NODE_TYPES.each do |node_type|
76
+ method_name = "#{node_type.to_s.gsub(/\W/, '')}_type?"
77
+ define_method(method_name) do
78
+ type == node_type
79
+ end
80
+ end
81
+
82
+ # Returns the parent node, or `nil` if the receiver is a root node.
83
+ #
84
+ # @return [Node, nil] the parent node or `nil`
85
+ def parent
86
+ @mutable_attributes[:parent]
87
+ end
88
+
89
+ def parent=(node)
90
+ @mutable_attributes[:parent] = node
91
+ end
92
+
93
+ protected :parent=
94
+
95
+ # Returns the index of the receiver node in its siblings.
96
+ #
97
+ # @return [Integer] the index of the receiver node in its siblings
98
+ def sibling_index
99
+ parent.children.index { |sibling| sibling.equal?(self) }
100
+ end
101
+
102
+ # Calls the given block for each ancestor node from parent to root.
103
+ # If no block is given, an `Enumerator` is returned.
104
+ #
105
+ # @overload each_ancestor
106
+ # Yield all nodes.
107
+ # @overload each_ancestor(type)
108
+ # Yield only nodes matching the type.
109
+ # @param [Symbol] type a node type
110
+ # @overload each_ancestor(type_a, type_b, ...)
111
+ # Yield only nodes matching any of the types.
112
+ # @param [Symbol] type_a a node type
113
+ # @param [Symbol] type_b a node type
114
+ # @overload each_ancestor(types)
115
+ # Yield only nodes matching any of types in the array.
116
+ # @param [Array<Symbol>] types an array containing node types
117
+ # @yieldparam [Node] node each ancestor node
118
+ # @return [self] if a block is given
119
+ # @return [Enumerator] if no block is given
120
+ def each_ancestor(*types, &block)
121
+ return to_enum(__method__, *types) unless block_given?
122
+
123
+ types.flatten!
124
+
125
+ if types.empty?
126
+ visit_ancestors(&block)
127
+ else
128
+ visit_ancestors_with_types(types, &block)
20
129
  end
130
+
131
+ self
132
+ end
133
+
134
+ # Returns an array of ancestor nodes.
135
+ # This is a shorthand for `node.each_ancestor.to_a`.
136
+ #
137
+ # @return [Array<Node>] an array of ancestor nodes
138
+ def ancestors
139
+ each_ancestor.to_a
140
+ end
141
+
142
+ # Calls the given block for each child node.
143
+ # If no block is given, an `Enumerator` is returned.
144
+ #
145
+ # Note that this is different from `node.children.each { |child| ... }`
146
+ # which yields all children including non-node elements.
147
+ #
148
+ # @overload each_child_node
149
+ # Yield all nodes.
150
+ # @overload each_child_node(type)
151
+ # Yield only nodes matching the type.
152
+ # @param [Symbol] type a node type
153
+ # @overload each_child_node(type_a, type_b, ...)
154
+ # Yield only nodes matching any of the types.
155
+ # @param [Symbol] type_a a node type
156
+ # @param [Symbol] type_b a node type
157
+ # @overload each_child_node(types)
158
+ # Yield only nodes matching any of types in the array.
159
+ # @param [Array<Symbol>] types an array containing node types
160
+ # @yieldparam [Node] node each child node
161
+ # @return [self] if a block is given
162
+ # @return [Enumerator] if no block is given
163
+ def each_child_node(*types)
164
+ return to_enum(__method__, *types) unless block_given?
165
+
166
+ types.flatten!
167
+
168
+ children.each do |child|
169
+ next unless child.is_a?(Node)
170
+ yield child if types.empty? || types.include?(child.type)
171
+ end
172
+
173
+ self
174
+ end
175
+
176
+ # Returns an array of child nodes.
177
+ # This is a shorthand for `node.each_child_node.to_a`.
178
+ #
179
+ # @return [Array<Node>] an array of child nodes
180
+ def child_nodes
181
+ each_child_node.to_a
182
+ end
183
+
184
+ # Calls the given block for each descendant node with depth first order.
185
+ # If no block is given, an `Enumerator` is returned.
186
+ #
187
+ # @overload each_descendant
188
+ # Yield all nodes.
189
+ # @overload each_descendant(type)
190
+ # Yield only nodes matching the type.
191
+ # @param [Symbol] type a node type
192
+ # @overload each_descendant(type_a, type_b, ...)
193
+ # Yield only nodes matching any of the types.
194
+ # @param [Symbol] type_a a node type
195
+ # @param [Symbol] type_b a node type
196
+ # @overload each_descendant(types)
197
+ # Yield only nodes matching any of types in the array.
198
+ # @param [Array<Symbol>] types an array containing node types
199
+ # @yieldparam [Node] node each descendant node
200
+ # @return [self] if a block is given
201
+ # @return [Enumerator] if no block is given
202
+ def each_descendant(*types, &block)
203
+ return to_enum(__method__, *types) unless block_given?
204
+
205
+ types.flatten!
206
+
207
+ if types.empty?
208
+ visit_descendants(&block)
209
+ else
210
+ visit_descendants_with_types(types, &block)
211
+ end
212
+
213
+ self
214
+ end
215
+
216
+ # Returns an array of descendant nodes.
217
+ # This is a shorthand for `node.each_descendant.to_a`.
218
+ #
219
+ # @return [Array<Node>] an array of descendant nodes
220
+ def descendants
221
+ each_descendant.to_a
222
+ end
223
+
224
+ # Calls the given block for the receiver and each descendant node in
225
+ # depth-first order.
226
+ # If no block is given, an `Enumerator` is returned.
227
+ #
228
+ # This method would be useful when you treat the receiver node as the root
229
+ # of a tree and want to iterate over all nodes in the tree.
230
+ #
231
+ # @overload each_node
232
+ # Yield all nodes.
233
+ # @overload each_node(type)
234
+ # Yield only nodes matching the type.
235
+ # @param [Symbol] type a node type
236
+ # @overload each_node(type_a, type_b, ...)
237
+ # Yield only nodes matching any of the types.
238
+ # @param [Symbol] type_a a node type
239
+ # @param [Symbol] type_b a node type
240
+ # @overload each_node(types)
241
+ # Yield only nodes matching any of types in the array.
242
+ # @param [Array<Symbol>] types an array containing node types
243
+ # @yieldparam [Node] node each node
244
+ # @return [self] if a block is given
245
+ # @return [Enumerator] if no block is given
246
+ def each_node(*types, &block)
247
+ return to_enum(__method__, *types) unless block_given?
248
+
249
+ types.flatten!
250
+
251
+ yield self if types.empty? || types.include?(type)
252
+
253
+ if types.empty?
254
+ visit_descendants(&block)
255
+ else
256
+ visit_descendants_with_types(types, &block)
257
+ end
258
+
259
+ self
260
+ end
261
+
262
+ def source
263
+ loc.expression.source
264
+ end
265
+
266
+ def source_range
267
+ loc.expression
21
268
  end
22
269
 
23
270
  ## Destructuring
24
271
 
272
+ def_matcher :receiver, '{(send $_ ...) (block (send $_ ...) ...)}'
25
273
  def_matcher :method_name, '{(send _ $_ ...) (block (send _ $_ ...) ...)}'
274
+ def_matcher :method_args, '{(send _ _ $...) (block (send _ _ $...) ...)}'
26
275
  # Note: for masgn, #asgn_rhs will be an array node
27
276
  def_matcher :asgn_rhs, '[assignment? (... $_)]'
277
+ def_matcher :str_content, '(str $_)'
278
+
279
+ def const_name
280
+ return unless const_type?
281
+ namespace, name = *self
282
+ if namespace && !namespace.cbase_type?
283
+ "#{namespace.const_name}::#{name}"
284
+ else
285
+ name.to_s
286
+ end
287
+ end
288
+
289
+ def_matcher :defined_module0, <<-PATTERN
290
+ {(class (const $_ $_) ...)
291
+ (module (const $_ $_) ...)
292
+ (casgn $_ $_ (send (const nil {:Class :Module}) :new ...))
293
+ (casgn $_ $_ (block (send (const nil {:Class :Module}) :new ...) ...))}
294
+ PATTERN
295
+ private :defined_module0
296
+
297
+ def defined_module
298
+ namespace, name = *defined_module0
299
+ s(:const, namespace, name) if name
300
+ end
301
+
302
+ def defined_module_name
303
+ (const = defined_module) && const.const_name
304
+ end
305
+
306
+ ## Searching the AST
307
+
308
+ def parent_module_name
309
+ # what class or module is this method/constant/etc definition in?
310
+ # returns nil if answer cannot be determined
311
+ ancestors = each_ancestor(:class, :module, :sclass, :casgn, :block)
312
+ result = ancestors.map do |ancestor|
313
+ case ancestor.type
314
+ when :class, :module, :casgn
315
+ # TODO: if constant name has cbase (leading ::), then we don't need
316
+ # to keep traversing up through nested classes/modules
317
+ ancestor.defined_module_name
318
+ when :sclass
319
+ obj = ancestor.children[0]
320
+ # TODO: look for constant definition and see if it is nested
321
+ # inside a class or module
322
+ return "#<Class:#{obj.const_name}>" if obj.const_type?
323
+ return "#<Class:#{ancestor.parent_module_name}>" if obj.self_type?
324
+ return nil
325
+ else # block
326
+ # Known DSL methods which eval body inside an anonymous class/module
327
+ return nil if [:describe, :it].include?(ancestor.method_name) &&
328
+ ancestor.receiver.nil?
329
+ if ancestor.method_name == :class_eval
330
+ return nil unless ancestor.receiver.const_type?
331
+ ancestor.receiver.const_name
332
+ end
333
+ end
334
+ end.compact.reverse.join('::')
335
+ result.empty? ? 'Object' : result
336
+ end
28
337
 
29
338
  ## Predicates
30
339
 
@@ -38,11 +347,176 @@ module Astrolabe
38
347
  end
39
348
 
40
349
  def asgn_method_call?
41
- method_name != :== && method_name.to_s.end_with?('=')
350
+ !COMPARISON_OPERATORS.include?(method_name) &&
351
+ method_name.to_s.end_with?('=')
42
352
  end
43
353
 
44
354
  def_matcher :equals_asgn?, '{lvasgn ivasgn cvasgn gvasgn casgn masgn}'
45
355
  def_matcher :shorthand_asgn?, '{op_asgn or_asgn and_asgn}'
46
356
  def_matcher :assignment?, '{equals_asgn? shorthand_asgn? asgn_method_call?}'
357
+
358
+ def literal?
359
+ LITERALS.include?(type)
360
+ end
361
+
362
+ def basic_literal?
363
+ BASIC_LITERALS.include?(type)
364
+ end
365
+
366
+ def truthy_literal?
367
+ TRUTHY_LITERALS.include?(type)
368
+ end
369
+
370
+ def falsey_literal?
371
+ FALSEY_LITERALS.include?(type)
372
+ end
373
+
374
+ def mutable_literal?
375
+ MUTABLE_LITERALS.include?(type)
376
+ end
377
+
378
+ def immutable_literal?
379
+ IMMUTABLE_LITERALS.include?(type)
380
+ end
381
+
382
+ def variable?
383
+ VARIABLES.include?(type)
384
+ end
385
+
386
+ def reference?
387
+ REFERENCES.include?(type)
388
+ end
389
+
390
+ def keyword?
391
+ return true if special_keyword? || keyword_not?
392
+ return false unless KEYWORDS.include?(type)
393
+
394
+ !OPERATOR_KEYWORDS.include?(type) || loc.operator.is?(type.to_s)
395
+ end
396
+
397
+ def special_keyword?
398
+ SPECIAL_KEYWORDS.include?(source)
399
+ end
400
+
401
+ def keyword_not?
402
+ _receiver, method_name, *args = *self
403
+ args.empty? && method_name == :! && loc.selector.is?('not'.freeze)
404
+ end
405
+
406
+ def_matcher :command?, '(send nil %1 ...)'
407
+ def_matcher :lambda?, '(block (send nil :lambda) ...)'
408
+ def_matcher :proc?, <<-PATTERN
409
+ {(block (send nil :proc) ...)
410
+ (block (send (const nil :Proc) :new) ...)
411
+ (send (const nil :Proc) :new)}
412
+ PATTERN
413
+ def_matcher :lambda_or_proc?, '{lambda? proc?}'
414
+
415
+ def_matcher :class_constructor?, <<-PATTERN
416
+ { (send (const nil {:Class :Module}) :new ...)
417
+ (block (send (const nil {:Class :Module}) :new ...) ...)}
418
+ PATTERN
419
+
420
+ def_matcher :module_definition?, <<-PATTERN
421
+ {class module (casgn _ _ class_constructor?)}
422
+ PATTERN
423
+
424
+ # Some expressions are evaluated for their value, some for their side
425
+ # effects, and some for both
426
+ # If we know that an expression is useful only for its side effects, that
427
+ # means we can transform it in ways which preserve the side effects, but
428
+ # change the return value
429
+ # So, does the return value of this node matter? If we changed it to
430
+ # `(...; nil)`, might that affect anything?
431
+ #
432
+ def value_used?
433
+ # Be conservative and return true if we're not sure
434
+ return false if parent.nil?
435
+ index = parent.children.index { |child| child.equal?(self) }
436
+
437
+ case parent.type
438
+ when :array, :block, :defined?, :dstr, :dsym, :eflipflop, :erange, :float,
439
+ :hash, :iflipflop, :irange, :not, :pair, :regexp, :str, :sym, :when,
440
+ :xstr
441
+ parent.value_used?
442
+ when :begin, :kwbegin
443
+ # the last child node determines the value of the parent
444
+ index == parent.children.size - 1 ? parent.value_used? : false
445
+ when :for
446
+ # `for var in enum; body; end`
447
+ # (for <var> <enum> <body>)
448
+ index == 2 ? parent.value_used? : true
449
+ when :case, :if
450
+ # (case <condition> <when...>)
451
+ # (if <condition> <truebranch> <falsebranch>)
452
+ index == 0 ? true : parent.value_used?
453
+ when :while, :until, :while_post, :until_post
454
+ # (while <condition> <body>) -> always evaluates to `nil`
455
+ index == 0
456
+ else
457
+ true
458
+ end
459
+ end
460
+
461
+ # Some expressions are evaluated for their value, some for their side
462
+ # effects, and some for both
463
+ # If we know that expressions are useful only for their return values, and
464
+ # have no side effects, that means we can reorder them, change the number
465
+ # of times they are evaluated, or replace them with other expressions which
466
+ # are equivalent in value
467
+ # So, is evaluation of this node free of side effects?
468
+ #
469
+ def pure?
470
+ # Be conservative and return false if we're not sure
471
+ case type
472
+ when :__FILE__, :__LINE__, :const, :cvar, :defined?, :false, :float,
473
+ :gvar, :int, :ivar, :lvar, :nil, :str, :sym, :true
474
+ true
475
+ when :and, :array, :begin, :case, :dstr, :dsym, :eflipflop, :ensure,
476
+ :erange, :for, :hash, :if, :iflipflop, :irange, :kwbegin, :not, :or,
477
+ :pair, :regexp, :until, :until_post, :when, :while, :while_post
478
+ child_nodes.all?(&:pure?)
479
+ else
480
+ false
481
+ end
482
+ end
483
+
484
+ protected
485
+
486
+ def visit_descendants(&block)
487
+ children.each do |child|
488
+ next unless child.is_a?(Node)
489
+ yield child
490
+ child.visit_descendants(&block)
491
+ end
492
+ end
493
+
494
+ def visit_descendants_with_types(types, &block)
495
+ children.each do |child|
496
+ next unless child.is_a?(Node)
497
+ yield child if types.include?(child.type)
498
+ child.visit_descendants_with_types(types, &block)
499
+ end
500
+ end
501
+
502
+ private
503
+
504
+ def visit_ancestors
505
+ last_node = self
506
+
507
+ while (current_node = last_node.parent)
508
+ yield current_node
509
+ last_node = current_node
510
+ end
511
+ end
512
+
513
+ def visit_ancestors_with_types(types)
514
+ last_node = self
515
+
516
+ while (current_node = last_node.parent)
517
+ yield current_node if types.include?(current_node.type)
518
+ last_node = current_node
519
+ end
520
+ end
47
521
  end
48
522
  end