rubocop 0.12.0 → 0.13.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 (279) hide show
  1. data/CHANGELOG.md +40 -0
  2. data/README.md +49 -8
  3. data/config/default.yml +40 -0
  4. data/config/enabled.yml +37 -9
  5. data/lib/rubocop.rb +24 -10
  6. data/lib/rubocop/cli.rb +41 -106
  7. data/lib/rubocop/config.rb +3 -2
  8. data/lib/rubocop/cop/commissioner.rb +15 -5
  9. data/lib/rubocop/cop/cop.rb +47 -32
  10. data/lib/rubocop/cop/lint/assignment_in_condition.rb +2 -2
  11. data/lib/rubocop/cop/lint/block_alignment.rb +30 -9
  12. data/lib/rubocop/cop/lint/empty_ensure.rb +1 -1
  13. data/lib/rubocop/cop/lint/end_alignment.rb +4 -4
  14. data/lib/rubocop/cop/lint/end_in_method.rb +1 -1
  15. data/lib/rubocop/cop/lint/ensure_return.rb +1 -1
  16. data/lib/rubocop/cop/lint/eval.rb +1 -3
  17. data/lib/rubocop/cop/lint/handle_exceptions.rb +1 -1
  18. data/lib/rubocop/cop/lint/literal_in_condition.rb +6 -4
  19. data/lib/rubocop/cop/lint/loop.rb +1 -1
  20. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
  21. data/lib/rubocop/cop/lint/rescue_exception.rb +1 -1
  22. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -8
  23. data/lib/rubocop/cop/lint/unreachable_code.rb +1 -1
  24. data/lib/rubocop/cop/lint/useless_assignment.rb +57 -60
  25. data/lib/rubocop/cop/lint/useless_comparison.rb +1 -1
  26. data/lib/rubocop/cop/lint/useless_setter_call.rb +85 -0
  27. data/lib/rubocop/cop/lint/void.rb +6 -8
  28. data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +1 -1
  29. data/lib/rubocop/cop/rails/read_attribute.rb +1 -1
  30. data/lib/rubocop/cop/rails/validation.rb +1 -1
  31. data/lib/rubocop/cop/style/access_control.rb +4 -6
  32. data/lib/rubocop/cop/style/alias.rb +1 -3
  33. data/lib/rubocop/cop/style/align_array.rb +47 -0
  34. data/lib/rubocop/cop/style/align_hash.rb +145 -0
  35. data/lib/rubocop/cop/style/align_parameters.rb +9 -3
  36. data/lib/rubocop/cop/style/and_or.rb +3 -4
  37. data/lib/rubocop/cop/style/ascii_comments.rb +1 -3
  38. data/lib/rubocop/cop/style/ascii_identifiers.rb +1 -1
  39. data/lib/rubocop/cop/style/attr.rb +1 -4
  40. data/lib/rubocop/cop/style/begin_block.rb +1 -1
  41. data/lib/rubocop/cop/style/block_comments.rb +1 -1
  42. data/lib/rubocop/cop/style/block_nesting.rb +2 -2
  43. data/lib/rubocop/cop/style/blocks.rb +14 -2
  44. data/lib/rubocop/cop/style/case_equality.rb +1 -3
  45. data/lib/rubocop/cop/style/case_indentation.rb +1 -1
  46. data/lib/rubocop/cop/style/character_literal.rb +1 -2
  47. data/lib/rubocop/cop/style/class_and_module_camel_case.rb +1 -1
  48. data/lib/rubocop/cop/style/class_methods.rb +1 -3
  49. data/lib/rubocop/cop/style/{avoid_class_vars.rb → class_vars.rb} +6 -2
  50. data/lib/rubocop/cop/style/collection_methods.rb +7 -9
  51. data/lib/rubocop/cop/style/colon_method_call.rb +1 -2
  52. data/lib/rubocop/cop/style/comment_annotation.rb +6 -6
  53. data/lib/rubocop/cop/style/constant_name.rb +1 -3
  54. data/lib/rubocop/cop/style/def_parentheses.rb +4 -12
  55. data/lib/rubocop/cop/style/documentation.rb +2 -2
  56. data/lib/rubocop/cop/style/dot_position.rb +2 -4
  57. data/lib/rubocop/cop/style/empty_line_between_defs.rb +21 -6
  58. data/lib/rubocop/cop/style/empty_lines.rb +1 -1
  59. data/lib/rubocop/cop/style/empty_literal.rb +3 -12
  60. data/lib/rubocop/cop/style/encoding.rb +6 -6
  61. data/lib/rubocop/cop/style/end_block.rb +1 -1
  62. data/lib/rubocop/cop/style/end_of_line.rb +5 -5
  63. data/lib/rubocop/cop/style/even_odd.rb +2 -2
  64. data/lib/rubocop/cop/style/favor_join.rb +1 -3
  65. data/lib/rubocop/cop/style/favor_modifier.rb +7 -3
  66. data/lib/rubocop/cop/style/favor_sprintf.rb +1 -1
  67. data/lib/rubocop/cop/style/favor_unless_over_negated_if.rb +1 -1
  68. data/lib/rubocop/cop/style/final_newline.rb +23 -0
  69. data/lib/rubocop/cop/style/{avoid_for.rb → for.rb} +2 -2
  70. data/lib/rubocop/cop/style/{avoid_global_vars.rb → global_vars.rb} +19 -6
  71. data/lib/rubocop/cop/style/hash_methods.rb +3 -5
  72. data/lib/rubocop/cop/style/hash_syntax.rb +4 -4
  73. data/lib/rubocop/cop/style/if_then_else.rb +1 -1
  74. data/lib/rubocop/cop/style/indentation_width.rb +4 -4
  75. data/lib/rubocop/cop/style/lambda.rb +2 -2
  76. data/lib/rubocop/cop/style/leading_comment_space.rb +1 -1
  77. data/lib/rubocop/cop/style/line_length.rb +7 -8
  78. data/lib/rubocop/cop/style/method_and_variable_snake_case.rb +1 -1
  79. data/lib/rubocop/cop/style/method_call_parentheses.rb +1 -4
  80. data/lib/rubocop/cop/style/method_length.rb +4 -4
  81. data/lib/rubocop/cop/style/module_function.rb +1 -3
  82. data/lib/rubocop/cop/style/multiline_block_chain.rb +44 -0
  83. data/lib/rubocop/cop/style/nil_comparison.rb +1 -3
  84. data/lib/rubocop/cop/style/not.rb +1 -1
  85. data/lib/rubocop/cop/style/numeric_literals.rb +26 -6
  86. data/lib/rubocop/cop/style/op_method.rb +2 -2
  87. data/lib/rubocop/cop/style/parameter_lists.rb +4 -4
  88. data/lib/rubocop/cop/style/parentheses_around_condition.rb +2 -2
  89. data/lib/rubocop/cop/style/perl_backrefs.rb +26 -0
  90. data/lib/rubocop/cop/style/proc.rb +1 -3
  91. data/lib/rubocop/cop/style/reduce_arguments.rb +7 -5
  92. data/lib/rubocop/cop/style/redundant_begin.rb +1 -1
  93. data/lib/rubocop/cop/style/redundant_return.rb +9 -2
  94. data/lib/rubocop/cop/style/redundant_self.rb +9 -2
  95. data/lib/rubocop/cop/style/regexp_literal.rb +7 -8
  96. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -1
  97. data/lib/rubocop/cop/style/semicolon.rb +10 -10
  98. data/lib/rubocop/cop/style/signal_exception.rb +2 -4
  99. data/lib/rubocop/cop/style/single_line_methods.rb +2 -4
  100. data/lib/rubocop/cop/style/space_after_comma_etc.rb +1 -1
  101. data/lib/rubocop/cop/style/space_after_control_keyword.rb +1 -1
  102. data/lib/rubocop/cop/style/space_after_method_name.rb +1 -1
  103. data/lib/rubocop/cop/style/space_before_modifier_keyword.rb +34 -0
  104. data/lib/rubocop/cop/style/{avoid_perlisms.rb → special_global_vars.rb} +17 -8
  105. data/lib/rubocop/cop/style/string_literals.rb +1 -2
  106. data/lib/rubocop/cop/style/surrounding_space.rb +9 -8
  107. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  108. data/lib/rubocop/cop/style/symbol_name.rb +9 -2
  109. data/lib/rubocop/cop/style/tab.rb +5 -5
  110. data/lib/rubocop/cop/style/ternary_operator.rb +2 -6
  111. data/lib/rubocop/cop/style/trailing_blank_lines.rb +32 -0
  112. data/lib/rubocop/cop/style/trailing_whitespace.rb +5 -6
  113. data/lib/rubocop/cop/style/trivial_accessors.rb +5 -5
  114. data/lib/rubocop/cop/style/unless_else.rb +1 -1
  115. data/lib/rubocop/cop/style/variable_interpolation.rb +1 -3
  116. data/lib/rubocop/cop/style/when_then.rb +1 -4
  117. data/lib/rubocop/cop/style/while_until_do.rb +7 -5
  118. data/lib/rubocop/cop/style/word_array.rb +1 -1
  119. data/lib/rubocop/cop/team.rb +100 -0
  120. data/lib/rubocop/cop/variable_inspector.rb +323 -235
  121. data/lib/rubocop/cop/variable_inspector/assignment.rb +103 -0
  122. data/lib/rubocop/cop/variable_inspector/locatable.rb +162 -0
  123. data/lib/rubocop/cop/variable_inspector/reference.rb +31 -0
  124. data/lib/rubocop/cop/variable_inspector/scope.rb +70 -0
  125. data/lib/rubocop/cop/variable_inspector/variable.rb +87 -0
  126. data/lib/rubocop/cop/variable_inspector/variable_table.rb +129 -0
  127. data/lib/rubocop/formatter/json_formatter.rb +8 -8
  128. data/lib/rubocop/formatter/progress_formatter.rb +4 -4
  129. data/lib/rubocop/processed_source.rb +22 -1
  130. data/lib/rubocop/version.rb +1 -1
  131. data/rubocop.gemspec +1 -1
  132. data/spec/rubocop/cli_spec.rb +32 -30
  133. data/spec/rubocop/config_spec.rb +4 -6
  134. data/spec/rubocop/cop/commissioner_spec.rb +4 -5
  135. data/spec/rubocop/cop/cop_spec.rb +8 -26
  136. data/spec/rubocop/cop/lint/assignment_in_condition_spec.rb +5 -9
  137. data/spec/rubocop/cop/lint/block_alignment_spec.rb +105 -57
  138. data/spec/rubocop/cop/lint/empty_ensure_spec.rb +1 -1
  139. data/spec/rubocop/cop/lint/end_alignment_spec.rb +1 -1
  140. data/spec/rubocop/cop/lint/end_in_method_spec.rb +1 -1
  141. data/spec/rubocop/cop/lint/ensure_return_spec.rb +1 -1
  142. data/spec/rubocop/cop/lint/eval_spec.rb +3 -3
  143. data/spec/rubocop/cop/lint/handle_exceptions_spec.rb +2 -2
  144. data/spec/rubocop/cop/lint/literal_in_condition_spec.rb +1 -1
  145. data/spec/rubocop/cop/lint/loop_spec.rb +1 -1
  146. data/spec/rubocop/cop/lint/parentheses_as_grouped_expression_spec.rb +1 -1
  147. data/spec/rubocop/cop/lint/rescue_exception_spec.rb +5 -5
  148. data/spec/rubocop/cop/lint/unreachable_code_spec.rb +1 -1
  149. data/spec/rubocop/cop/lint/useless_assignment_spec.rb +1545 -108
  150. data/spec/rubocop/cop/lint/useless_comparison_spec.rb +1 -1
  151. data/spec/rubocop/cop/lint/useless_setter_call_spec.rb +101 -0
  152. data/spec/rubocop/cop/lint/void_spec.rb +1 -1
  153. data/spec/rubocop/cop/offence_spec.rb +4 -4
  154. data/spec/rubocop/cop/rails/has_and_belongs_to_many_spec.rb +1 -1
  155. data/spec/rubocop/cop/rails/read_attribute_spec.rb +1 -1
  156. data/spec/rubocop/cop/rails/validation_spec.rb +1 -1
  157. data/spec/rubocop/cop/style/access_control_spec.rb +20 -20
  158. data/spec/rubocop/cop/style/alias_spec.rb +3 -3
  159. data/spec/rubocop/cop/style/align_array_spec.rb +62 -0
  160. data/spec/rubocop/cop/style/align_hash_spec.rb +267 -0
  161. data/spec/rubocop/cop/style/align_parameters_spec.rb +2 -2
  162. data/spec/rubocop/cop/style/and_or_spec.rb +1 -1
  163. data/spec/rubocop/cop/style/ascii_comments_spec.rb +2 -2
  164. data/spec/rubocop/cop/style/ascii_identifiers_spec.rb +2 -2
  165. data/spec/rubocop/cop/style/attr_spec.rb +1 -1
  166. data/spec/rubocop/cop/style/begin_block_spec.rb +1 -1
  167. data/spec/rubocop/cop/style/block_comments_spec.rb +1 -1
  168. data/spec/rubocop/cop/style/block_nesting_spec.rb +3 -3
  169. data/spec/rubocop/cop/style/blocks_spec.rb +25 -1
  170. data/spec/rubocop/cop/style/case_equality_spec.rb +1 -1
  171. data/spec/rubocop/cop/style/case_indentation_spec.rb +5 -5
  172. data/spec/rubocop/cop/style/character_literal_spec.rb +1 -1
  173. data/spec/rubocop/cop/style/class_and_module_camel_case_spec.rb +1 -1
  174. data/spec/rubocop/cop/style/class_methods_spec.rb +1 -1
  175. data/spec/rubocop/cop/style/class_vars_spec.rb +25 -0
  176. data/spec/rubocop/cop/style/collection_methods_spec.rb +5 -6
  177. data/spec/rubocop/cop/style/colon_method_call_spec.rb +0 -3
  178. data/spec/rubocop/cop/style/comment_annotation_spec.rb +20 -18
  179. data/spec/rubocop/cop/style/constant_name_spec.rb +1 -1
  180. data/spec/rubocop/cop/style/def_with_parentheses_spec.rb +1 -1
  181. data/spec/rubocop/cop/style/def_without_parentheses_spec.rb +1 -1
  182. data/spec/rubocop/cop/style/documentation_spec.rb +1 -1
  183. data/spec/rubocop/cop/style/dot_position_spec.rb +5 -5
  184. data/spec/rubocop/cop/style/empty_line_between_defs_spec.rb +35 -4
  185. data/spec/rubocop/cop/style/empty_lines_spec.rb +1 -1
  186. data/spec/rubocop/cop/style/empty_literal_spec.rb +7 -7
  187. data/spec/rubocop/cop/style/encoding_spec.rb +11 -5
  188. data/spec/rubocop/cop/style/end_block_spec.rb +1 -1
  189. data/spec/rubocop/cop/style/end_of_line_spec.rb +4 -3
  190. data/spec/rubocop/cop/style/even_odd_spec.rb +1 -1
  191. data/spec/rubocop/cop/style/favor_join_spec.rb +2 -2
  192. data/spec/rubocop/cop/style/favor_modifier_spec.rb +13 -10
  193. data/spec/rubocop/cop/style/favor_sprintf_spec.rb +4 -4
  194. data/spec/rubocop/cop/style/favor_unless_over_negated_if_spec.rb +1 -1
  195. data/spec/rubocop/cop/style/favor_until_over_negated_while_spec.rb +4 -4
  196. data/spec/rubocop/cop/style/final_newline_spec.rb +25 -0
  197. data/spec/rubocop/cop/style/{avoid_for_spec.rb → for_spec.rb} +8 -12
  198. data/spec/rubocop/cop/style/{avoid_global_vars_spec.rb → global_vars_spec.rb} +13 -3
  199. data/spec/rubocop/cop/style/hash_methods_spec.rb +1 -1
  200. data/spec/rubocop/cop/style/hash_syntax_spec.rb +20 -9
  201. data/spec/rubocop/cop/style/if_with_semicolon_spec.rb +3 -3
  202. data/spec/rubocop/cop/style/indentation_width_spec.rb +19 -19
  203. data/spec/rubocop/cop/style/lambda_spec.rb +6 -6
  204. data/spec/rubocop/cop/style/leading_comment_space_spec.rb +1 -1
  205. data/spec/rubocop/cop/style/line_length_spec.rb +3 -3
  206. data/spec/rubocop/cop/style/method_and_variable_snake_case_spec.rb +8 -9
  207. data/spec/rubocop/cop/style/method_call_parentheses_spec.rb +1 -1
  208. data/spec/rubocop/cop/style/method_length_spec.rb +18 -17
  209. data/spec/rubocop/cop/style/module_function_spec.rb +1 -1
  210. data/spec/rubocop/cop/style/multiline_block_chain_spec.rb +84 -0
  211. data/spec/rubocop/cop/style/multiline_if_then_spec.rb +2 -2
  212. data/spec/rubocop/cop/style/nil_comparison_spec.rb +1 -1
  213. data/spec/rubocop/cop/style/not_spec.rb +1 -1
  214. data/spec/rubocop/cop/style/numeric_literals_spec.rb +15 -25
  215. data/spec/rubocop/cop/style/one_line_conditional_spec.rb +2 -2
  216. data/spec/rubocop/cop/style/op_method_spec.rb +3 -3
  217. data/spec/rubocop/cop/style/parameter_lists_spec.rb +5 -5
  218. data/spec/rubocop/cop/style/parentheses_around_condition_spec.rb +4 -8
  219. data/spec/rubocop/cop/style/perl_backrefs_spec.rb +23 -0
  220. data/spec/rubocop/cop/style/proc_spec.rb +1 -1
  221. data/spec/rubocop/cop/style/reduce_arguments_spec.rb +18 -11
  222. data/spec/rubocop/cop/style/redundant_begin_spec.rb +1 -1
  223. data/spec/rubocop/cop/style/redundant_return_spec.rb +16 -1
  224. data/spec/rubocop/cop/style/redundant_self_spec.rb +6 -1
  225. data/spec/rubocop/cop/style/regexp_literal_spec.rb +19 -23
  226. data/spec/rubocop/cop/style/rescue_modifier_spec.rb +3 -3
  227. data/spec/rubocop/cop/style/semicolon_spec.rb +3 -3
  228. data/spec/rubocop/cop/style/signal_exception_spec.rb +1 -1
  229. data/spec/rubocop/cop/style/single_line_methods_spec.rb +22 -18
  230. data/spec/rubocop/cop/style/space_after_colon_spec.rb +4 -4
  231. data/spec/rubocop/cop/style/space_after_comma_spec.rb +4 -4
  232. data/spec/rubocop/cop/style/space_after_control_keyword_spec.rb +1 -1
  233. data/spec/rubocop/cop/style/space_after_method_name_spec.rb +1 -1
  234. data/spec/rubocop/cop/style/space_after_semicolon_spec.rb +3 -3
  235. data/spec/rubocop/cop/style/space_around_braces_spec.rb +13 -12
  236. data/spec/rubocop/cop/style/space_around_equals_in_default_parameter_spec.rb +3 -3
  237. data/spec/rubocop/cop/style/space_around_operators_spec.rb +25 -25
  238. data/spec/rubocop/cop/style/space_before_modifier_keyword_spec.rb +53 -0
  239. data/spec/rubocop/cop/style/space_inside_brackets_spec.rb +9 -9
  240. data/spec/rubocop/cop/style/space_inside_hash_literal_braces_spec.rb +47 -61
  241. data/spec/rubocop/cop/style/space_inside_parens_spec.rb +4 -4
  242. data/spec/rubocop/cop/style/special_global_vars_spec.rb +52 -0
  243. data/spec/rubocop/cop/style/string_literals_spec.rb +5 -5
  244. data/spec/rubocop/cop/style/symbol_array_spec.rb +1 -1
  245. data/spec/rubocop/cop/style/symbol_name_spec.rb +27 -18
  246. data/spec/rubocop/cop/style/tab_spec.rb +1 -1
  247. data/spec/rubocop/cop/style/ternary_operator_spec.rb +2 -2
  248. data/spec/rubocop/cop/style/trailing_blank_lines_spec.rb +24 -0
  249. data/spec/rubocop/cop/style/trailing_whitespace_spec.rb +7 -7
  250. data/spec/rubocop/cop/style/trivial_accessors_spec.rb +6 -14
  251. data/spec/rubocop/cop/style/unless_else_spec.rb +3 -3
  252. data/spec/rubocop/cop/style/variable_interpolation_spec.rb +5 -5
  253. data/spec/rubocop/cop/style/when_then_spec.rb +15 -15
  254. data/spec/rubocop/cop/style/while_until_do_spec.rb +3 -3
  255. data/spec/rubocop/cop/style/word_array_spec.rb +1 -1
  256. data/spec/rubocop/cop/team_spec.rb +158 -0
  257. data/spec/rubocop/cop/variable_inspector/assignment_spec.rb +217 -0
  258. data/spec/rubocop/cop/variable_inspector/locatable_spec.rb +740 -0
  259. data/spec/rubocop/cop/variable_inspector/scope_spec.rb +191 -0
  260. data/spec/rubocop/cop/variable_inspector/variable_spec.rb +79 -0
  261. data/spec/rubocop/cop/variable_inspector/variable_table_spec.rb +275 -0
  262. data/spec/rubocop/cop/variable_inspector_spec.rb +13 -533
  263. data/spec/rubocop/formatter/clang_style_formatter_spec.rb +4 -4
  264. data/spec/rubocop/formatter/disabled_config_formatter_spec.rb +1 -1
  265. data/spec/rubocop/formatter/emacs_style_formatter_spec.rb +3 -3
  266. data/spec/rubocop/formatter/file_list_formatter_spec.rb +3 -3
  267. data/spec/rubocop/formatter/progress_formatter_spec.rb +1 -1
  268. data/spec/spec_helper.rb +5 -1
  269. data/spec/support/ast_helper.rb +15 -0
  270. data/spec/support/shared_context.rb +18 -0
  271. data/spec/support/shared_examples.rb +1 -1
  272. metadata +95 -32
  273. checksums.yaml +0 -7
  274. data/lib/rubocop/cop/lint/unused_local_variable.rb +0 -32
  275. data/lib/rubocop/cop/style/avoid_perl_backrefs.rb +0 -19
  276. data/spec/rubocop/cop/lint/unused_local_variable_spec.rb +0 -588
  277. data/spec/rubocop/cop/style/avoid_class_vars_spec.rb +0 -27
  278. data/spec/rubocop/cop/style/avoid_perl_backrefs_spec.rb +0 -20
  279. data/spec/rubocop/cop/style/avoid_perlisms_spec.rb +0 -47
@@ -22,7 +22,7 @@ module Rubocop
22
22
 
23
23
  symbol_array = array_elems.all? { |e| e.type == :sym }
24
24
 
25
- add_offence(:convention, node.loc.expression, MSG) if symbol_array
25
+ convention(node, :expression) if symbol_array
26
26
  end
27
27
  end
28
28
  end
@@ -5,13 +5,19 @@ module Rubocop
5
5
  module Style
6
6
  # This cop checks whether symbol names are snake_case.
7
7
  # There's also an option to accept CamelCase symbol names as well.
8
+ # There's also an option to accept symbol names with dots as well.
8
9
  class SymbolName < Cop
9
10
  MSG = 'Use snake_case for symbols.'
10
11
  SNAKE_CASE = /^[\da-z_]+[!?=]?$/
12
+ SNAKE_CASE_WITH_DOTS = /^[\da-z_\.]+[!?=]?$/
11
13
  CAMEL_CASE = /^[A-Z][A-Za-z\d]*$/
12
14
 
13
15
  def allow_camel_case?
14
- self.class.config['AllowCamelCase']
16
+ cop_config['AllowCamelCase']
17
+ end
18
+
19
+ def allow_dots?
20
+ cop_config['AllowDots']
15
21
  end
16
22
 
17
23
  def on_send(node)
@@ -30,7 +36,8 @@ module Rubocop
30
36
  return unless sym_name =~ /^[a-zA-Z]/
31
37
  return if sym_name =~ SNAKE_CASE
32
38
  return if allow_camel_case? && sym_name =~ CAMEL_CASE
33
- add_offence(:convention, node.loc.expression, MSG)
39
+ return if allow_dots? && sym_name =~ SNAKE_CASE_WITH_DOTS
40
+ convention(node, :expression)
34
41
  end
35
42
  end
36
43
  end
@@ -12,11 +12,11 @@ module Rubocop
12
12
  match = line.match(/^( *)\t/)
13
13
  if match
14
14
  spaces = match.captures[0]
15
- add_offence(:convention,
16
- source_range(processed_source.buffer,
17
- processed_source[0...index],
18
- spaces.length, 8),
19
- MSG)
15
+ convention(nil,
16
+ source_range(processed_source.buffer,
17
+ processed_source[0...index],
18
+ spaces.length, 8),
19
+ MSG)
20
20
  end
21
21
  end
22
22
  end
@@ -14,9 +14,7 @@ module Rubocop
14
14
  # discard non-ternary ops
15
15
  return unless loc.respond_to?(:question)
16
16
 
17
- if loc.line != loc.colon.line
18
- add_offence(:convention, loc.expression, MSG)
19
- end
17
+ convention(node, :expression) if loc.line != loc.colon.line
20
18
  end
21
19
  end
22
20
 
@@ -33,9 +31,7 @@ module Rubocop
33
31
 
34
32
  node.children.each do |child|
35
33
  on_node(:if, child) do |c|
36
- if c.loc.respond_to?(:question)
37
- add_offence(:convention, c.loc.expression, MSG)
38
- end
34
+ convention(c, :expression) if c.loc.respond_to?(:question)
39
35
  end
40
36
  end
41
37
  end
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+
3
+ module Rubocop
4
+ module Cop
5
+ module Style
6
+ # This cop looks for trailing blank lines in the source code.
7
+ class TrailingBlankLines < Cop
8
+ MSG = '%d trailing blank lines detected.'
9
+
10
+ def investigate(processed_source)
11
+ blank_lines = 0
12
+
13
+ processed_source.lines.reverse_each do |line|
14
+ if line.blank?
15
+ blank_lines += 1
16
+ else
17
+ break
18
+ end
19
+ end
20
+
21
+ if blank_lines > 0
22
+ convention(nil,
23
+ source_range(processed_source.buffer,
24
+ processed_source[0...-blank_lines],
25
+ 0, 1),
26
+ format(MSG, blank_lines))
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -10,12 +10,11 @@ module Rubocop
10
10
  def investigate(processed_source)
11
11
  processed_source.lines.each_with_index do |line, index|
12
12
  if line =~ /.*[ \t]+$/
13
- add_offence(:convention,
14
- source_range(processed_source.buffer,
15
- processed_source[0...index],
16
- line.rstrip.length,
17
- line.length - line.rstrip.length),
18
- MSG)
13
+ convention(nil,
14
+ source_range(processed_source.buffer,
15
+ processed_source[0...index],
16
+ line.rstrip.length,
17
+ line.length - line.rstrip.length))
19
18
  end
20
19
  end
21
20
  end
@@ -29,22 +29,22 @@ module Rubocop
29
29
  'writer'
30
30
  end
31
31
  if kind
32
- add_offence(:convention, node.loc.keyword,
33
- sprintf(MSG, kind, kind))
32
+ convention(node, :keyword,
33
+ sprintf(MSG, kind, kind))
34
34
  end
35
35
 
36
36
  end
37
37
 
38
38
  def exact_name_match?
39
- TrivialAccessors.config['ExactNameMatch']
39
+ cop_config['ExactNameMatch']
40
40
  end
41
41
 
42
42
  def allow_predicates?
43
- TrivialAccessors.config['AllowPredicates']
43
+ cop_config['AllowPredicates']
44
44
  end
45
45
 
46
46
  def whitelist
47
- whitelist = TrivialAccessors.config['Whitelist']
47
+ whitelist = cop_config['Whitelist']
48
48
  Array(whitelist).map(&:to_sym) + [:initialize]
49
49
  end
50
50
 
@@ -15,7 +15,7 @@ module Rubocop
15
15
  return unless loc.respond_to?(:keyword) && loc.respond_to?(:else)
16
16
 
17
17
  if loc.keyword.is?('unless') && loc.else
18
- add_offence(:convention, loc.expression, MSG)
18
+ convention(node, :expression)
19
19
  end
20
20
  end
21
21
  end
@@ -12,9 +12,7 @@ module Rubocop
12
12
  var = (v.type == :nth_ref ? '$' : '') + v.to_a[0].to_s
13
13
 
14
14
  if node.loc.expression.source.include?("##{var}")
15
- add_offence(:convention,
16
- v.loc.expression,
17
- sprintf(MSG, var, var))
15
+ convention(v, :expression, sprintf(MSG, var, var))
18
16
  end
19
17
  end
20
18
  end
@@ -8,10 +8,7 @@ module Rubocop
8
8
  MSG = 'Never use "when x;". Use "when x then" instead.'
9
9
 
10
10
  def on_when(node)
11
- if node.loc.begin && node.loc.begin.is?(';')
12
- add_offence(:convention, node.loc.begin, MSG)
13
- do_autocorrect(node)
14
- end
11
+ convention(node, :begin) if node.loc.begin && node.loc.begin.is?(';')
15
12
  end
16
13
 
17
14
  def autocorrect_action(node)
@@ -18,10 +18,8 @@ module Rubocop
18
18
 
19
19
  if length > 1
20
20
  if node.loc.begin && node.loc.begin.is?('do')
21
- add_offence(:convention,
22
- node.loc.begin,
23
- error_message(node.type))
24
- do_autocorrect(node)
21
+ convention(node, :begin,
22
+ error_message(node.type))
25
23
  end
26
24
  end
27
25
  end
@@ -34,7 +32,11 @@ module Rubocop
34
32
 
35
33
  def autocorrect_action(node)
36
34
  @corrections << lambda do |corrector|
37
- corrector.remove(node.loc.begin)
35
+ condition_node, = *node
36
+ end_of_condition_range = condition_node.loc.expression.end
37
+ do_range = node.loc.begin
38
+ whitespaces_and_do_range = end_of_condition_range.join(do_range)
39
+ corrector.remove(whitespaces_and_do_range)
38
40
  end
39
41
  end
40
42
  end
@@ -19,7 +19,7 @@ module Rubocop
19
19
  string_array = array_elems.all? { |e| e.type == :str }
20
20
 
21
21
  if string_array && !complex_content?(array_elems)
22
- add_offence(:convention, node.loc.expression, MSG)
22
+ convention(node, :expression)
23
23
  end
24
24
  end
25
25
 
@@ -0,0 +1,100 @@
1
+ # encoding: utf-8
2
+
3
+ module Rubocop
4
+ module Cop
5
+ # FIXME
6
+ class Team
7
+ attr_reader :errors
8
+
9
+ def initialize(cop_classes, config, options = nil)
10
+ @cop_classes = cop_classes
11
+ @config = config
12
+ @options = options || { autocorrect: false, debug: false }
13
+ @errors = []
14
+ end
15
+
16
+ def autocorrect?
17
+ @options[:autocorrect]
18
+ end
19
+
20
+ def debug?
21
+ @options[:debug]
22
+ end
23
+
24
+ def inspect_file(file)
25
+ begin
26
+ processed_source = SourceParser.parse_file(file)
27
+ rescue Encoding::UndefinedConversionError, ArgumentError => e
28
+ handle_error(e,
29
+ "An error occurred while parsing #{file}.".color(:red))
30
+ return []
31
+ end
32
+
33
+ offences = processed_source.diagnostics.map do |diagnostic|
34
+ Offence.from_diagnostic(diagnostic)
35
+ end
36
+
37
+ # If we got any syntax errors, return only the syntax offences.
38
+ # Parser may return nil for AST even though there are no syntax errors.
39
+ # e.g. sources which contain only comments
40
+ if offences.any? { |o| [:error, :fatal].include?(o.severity) }
41
+ return offences
42
+ end
43
+
44
+ commissioner = Commissioner.new(cops)
45
+ offences += commissioner.investigate(processed_source)
46
+ process_commissioner_errors(file, commissioner.errors)
47
+ autocorrect(processed_source.buffer, cops)
48
+ offences.sort
49
+ end
50
+
51
+ def cops
52
+ @cops ||= begin
53
+ @cop_classes.reduce([]) do |instances, cop_class|
54
+ next instances unless @config.cop_enabled?(cop_class)
55
+ instances << cop_class.new(@config, @options)
56
+ end
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def autocorrect(buffer, cops)
63
+ return unless autocorrect?
64
+
65
+ corrections = cops.reduce([]) do |array, cop|
66
+ array.concat(cop.corrections)
67
+ array
68
+ end
69
+
70
+ corrector = Corrector.new(buffer, corrections)
71
+ new_source = corrector.rewrite
72
+
73
+ unless new_source == buffer.source
74
+ filename = buffer.name
75
+ File.open(filename, 'w') { |f| f.write(new_source) }
76
+ end
77
+ end
78
+
79
+ def process_commissioner_errors(file, file_errors)
80
+ file_errors.each do |cop, errors|
81
+ errors.each do |e|
82
+ handle_error(e,
83
+ "An error occurred while #{cop.name}".color(:red) +
84
+ " cop was inspecting #{file}.".color(:red))
85
+ end
86
+ end
87
+ end
88
+
89
+ def handle_error(e, message)
90
+ @errors << message
91
+ warn message
92
+ if debug?
93
+ puts e.message, e.backtrace
94
+ else
95
+ warn 'To see the complete backtrace run rubocop -d.'
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -6,296 +6,320 @@ module Rubocop
6
6
  # This is intended to be used as mix-in, and the user class may override
7
7
  # some of hook methods.
8
8
  module VariableInspector
9
- VARIABLE_ASSIGNMENT_TYPES = [:lvasgn, :match_with_lvasgn].freeze
10
- ARGUMENT_DECLARATION_TYPES = [
11
- :arg, :optarg, :restarg, :blockarg,
12
- :kwarg, :kwoptarg, :kwrestarg,
13
- :shadowarg
9
+ VARIABLE_ASSIGNMENT_TYPE = :lvasgn
10
+ REGEXP_NAMED_CAPTURE_TYPE = :match_with_lvasgn
11
+ VARIABLE_ASSIGNMENT_TYPES =
12
+ [VARIABLE_ASSIGNMENT_TYPE, REGEXP_NAMED_CAPTURE_TYPE].freeze
13
+
14
+ METHOD_ARGUMENT_DECLARATION_TYPES = [
15
+ :arg, :optarg, :restarg,
16
+ :kwarg, :kwoptarg, :kwrestarg
14
17
  ].freeze
15
- VARIABLE_DECLARATION_TYPES =
16
- (VARIABLE_ASSIGNMENT_TYPES + ARGUMENT_DECLARATION_TYPES).freeze
17
- VARIABLE_USE_TYPES = [:lvar].freeze
18
- SCOPE_TYPES = [:module, :class, :sclass, :def, :defs, :block].freeze
19
-
20
- # A VariableEntry represents existance of a local variable.
21
- # This holds a variable declaration node,
22
- # and some states of the variable.
23
- class VariableEntry
24
- attr_reader :node
25
- attr_accessor :used
26
- alias_method :used?, :used
27
-
28
- def initialize(node, name = nil)
29
- unless VARIABLE_DECLARATION_TYPES.include?(node.type)
30
- fail ArgumentError,
31
- "Node type must be any of #{VARIABLE_DECLARATION_TYPES}, " +
32
- "passed #{node.type}"
33
- end
34
- @node = node
35
- @name = name.to_sym if name
36
- @used = false
37
- end
18
+ BLOCK_ARGUMENT_DECLARATION_TYPE = :blockarg
19
+ ARGUMENT_DECLARATION_TYPES = (
20
+ METHOD_ARGUMENT_DECLARATION_TYPES + [BLOCK_ARGUMENT_DECLARATION_TYPE]
21
+ ).freeze
22
+ BLOCK_LOCAL_VARIABLE_DECLARATION_TYPE = :shadowarg
23
+ DECLARATION_TYPES = (
24
+ ARGUMENT_DECLARATION_TYPES + [BLOCK_LOCAL_VARIABLE_DECLARATION_TYPE]
25
+ ).freeze
38
26
 
39
- def name
40
- @name || @node.children.first
41
- end
27
+ LOGICAL_OPERATOR_ASSIGNMENT_TYPES = [:or_asgn, :and_asgn].freeze
28
+ OPERATOR_ASSIGNMENT_TYPES =
29
+ (LOGICAL_OPERATOR_ASSIGNMENT_TYPES + [:op_asgn]).freeze
30
+
31
+ MULTIPLE_ASSIGNMENT_TYPE = :masgn
32
+
33
+ VARIABLE_REFERENCE_TYPE = :lvar
34
+
35
+ POST_CONDITION_LOOP_TYPES = [:while_post, :until_post].freeze
36
+ LOOP_TYPES = (POST_CONDITION_LOOP_TYPES + [:while, :until, :for]).freeze
37
+
38
+ RESCUE_TYPE = :rescue
39
+
40
+ ZERO_ARITY_SUPER_TYPE = :zsuper
41
+
42
+ TWISTED_SCOPE_TYPES = [:block, :class, :sclass, :defs].freeze
43
+ SCOPE_TYPES = (TWISTED_SCOPE_TYPES + [:module, :def]).freeze
44
+
45
+ def variable_table
46
+ @variable_table ||= VariableTable.new(self)
42
47
  end
43
48
 
44
- # A Scope represents a context of local variable visibility.
45
- # This is a place where local variables belong to.
46
- # A scope instance holds a scope node and variable entries.
47
- class Scope
48
- attr_reader :node, :variable_entries
49
-
50
- def initialize(node)
51
- # Accept begin node for top level scope.
52
- unless SCOPE_TYPES.include?(node.type) || node.type == :begin
53
- fail ArgumentError,
54
- "Node type must be any of #{SCOPE_TYPES}, " +
55
- "passed #{node.type}"
56
- end
57
- @node = node
58
- @variable_entries = {}
49
+ # Starting point.
50
+ def inspect_variables(root_node)
51
+ return unless root_node
52
+
53
+ # Wrap with begin node if it's standalone node.
54
+ unless root_node.type == :begin
55
+ root_node = Parser::AST::Node.new(:begin, [root_node])
59
56
  end
57
+
58
+ inspect_variables_in_scope(root_node)
60
59
  end
61
60
 
62
- # A VariableTable manages the lifetime of all scopes and local variables
63
- # in a program.
64
- # This holds scopes as stack structure, and provides a way to add local
65
- # variables to current scope and find local variables by considering
66
- # variable visibility of the current scope.
67
- class VariableTable
68
- def initialize(hook_receiver = nil)
69
- @hook_receiver = hook_receiver
70
- end
61
+ # This is called for each scope recursively.
62
+ def inspect_variables_in_scope(scope_node)
63
+ variable_table.push_scope(scope_node)
64
+ process_children(scope_node)
65
+ variable_table.pop_scope
66
+ end
71
67
 
72
- def invoke_hook(hook_name, *args)
73
- @hook_receiver.send(hook_name, *args) if @hook_receiver
68
+ def process_children(origin_node)
69
+ origin_node.children.each do |child|
70
+ next unless child.is_a?(Parser::AST::Node)
71
+ next if scanned_node?(child)
72
+ process_node(child)
74
73
  end
74
+ end
75
75
 
76
- def scope_stack
77
- @scope_stack ||= []
76
+ def process_node(node)
77
+ catch(:skip_children) do
78
+ dispatch_node(node)
79
+ process_children(node)
78
80
  end
81
+ end
79
82
 
80
- def push_scope(scope_node)
81
- scope = Scope.new(scope_node)
82
- invoke_hook(:before_entering_scope, scope)
83
- scope_stack.push(scope)
84
- invoke_hook(:after_entering_scope, scope)
85
- scope
86
- end
83
+ def skip_children!
84
+ throw :skip_children
85
+ end
87
86
 
88
- def pop_scope
89
- scope = current_scope
90
- invoke_hook(:before_leaving_scope, scope)
91
- scope_stack.pop
92
- invoke_hook(:after_leaving_scope, scope)
93
- scope
87
+ def dispatch_node(node)
88
+ case node.type
89
+ when *DECLARATION_TYPES
90
+ process_variable_declaration(node)
91
+ when VARIABLE_ASSIGNMENT_TYPE
92
+ process_variable_assignment(node)
93
+ when REGEXP_NAMED_CAPTURE_TYPE
94
+ process_regexp_named_captures(node)
95
+ when *OPERATOR_ASSIGNMENT_TYPES
96
+ process_variable_operator_assignment(node)
97
+ when MULTIPLE_ASSIGNMENT_TYPE
98
+ process_variable_multiple_assignment(node)
99
+ when VARIABLE_REFERENCE_TYPE
100
+ process_variable_referencing(node)
101
+ when *LOOP_TYPES
102
+ process_loop(node)
103
+ when RESCUE_TYPE
104
+ process_rescue(node)
105
+ when ZERO_ARITY_SUPER_TYPE
106
+ process_zero_arity_super(node)
107
+ when *SCOPE_TYPES
108
+ process_scope(node)
94
109
  end
110
+ end
95
111
 
96
- def current_scope
97
- scope_stack.last
98
- end
112
+ def process_variable_declaration(node)
113
+ # restarg would have no name:
114
+ #
115
+ # def initialize(*)
116
+ # end
117
+ return if node.type == :restarg && node.children.empty?
99
118
 
100
- def current_scope_level
101
- scope_stack.count
102
- end
119
+ variable_name = node.children.first
120
+ variable_table.declare_variable(variable_name, node)
121
+ end
103
122
 
104
- def add_variable_entry(variable_declaration_node, name = nil)
105
- entry = VariableEntry.new(variable_declaration_node, name)
106
- invoke_hook(:before_declaring_variable, entry)
107
- current_scope.variable_entries[entry.name] = entry
108
- invoke_hook(:after_declaring_variable, entry)
109
- entry
110
- end
123
+ def process_variable_assignment(node)
124
+ name = node.children.first
111
125
 
112
- def find_variable_entry(variable_name)
113
- scope_stack.reverse_each do |scope|
114
- entry = scope.variable_entries[variable_name]
115
- return entry if entry
116
- # Only block scope allows referencing outer scope variables.
117
- return nil unless scope.node.type == :block
118
- end
119
- nil
126
+ unless variable_table.variable_exist?(name)
127
+ variable_table.declare_variable(name, node)
120
128
  end
129
+
130
+ # Need to scan rhs before assignment so that we can mark previous
131
+ # assignments as referenced if rhs has referencing to the variable
132
+ # itself like:
133
+ #
134
+ # foo = 1
135
+ # foo = foo + 1
136
+ process_children(node)
137
+
138
+ variable_table.assign_to_variable(name, node)
139
+
140
+ skip_children!
121
141
  end
122
142
 
123
- # This provides a way to scan all nodes only in current scope.
124
- class NodeScanner
125
- TWISTED_SCOPE_NODE_TYPES = [:block, :sclass, :defs].freeze
126
- POST_CONDITION_LOOP_NODE_TYPES = [:while_post, :until_post].freeze
143
+ def process_regexp_named_captures(node)
144
+ regexp_node, rhs_node = *node
145
+
146
+ regexp_string = regexp_node.children[0].children[0]
147
+ regexp = Regexp.new(regexp_string)
148
+ variable_names = regexp.named_captures.keys
127
149
 
128
- def self.scan_nodes_in_scope(origin_node, &block)
129
- instance = new(block)
130
- instance.scan_nodes_in_scope(origin_node)
150
+ variable_names.each do |name|
151
+ unless variable_table.variable_exist?(name)
152
+ variable_table.declare_variable(name, node)
153
+ end
131
154
  end
132
155
 
133
- def initialize(callback)
134
- @callback = callback
156
+ process_node(rhs_node)
157
+ process_node(regexp_node)
158
+
159
+ variable_names.each do |name|
160
+ variable_table.assign_to_variable(name, node)
135
161
  end
136
162
 
137
- def scan_nodes_in_scope(origin_node, yield_origin_node = false)
138
- @callback.call(origin_node) if yield_origin_node
163
+ skip_children!
164
+ end
139
165
 
140
- origin_node.children.each_with_index do |child, index|
141
- next unless child.is_a?(Parser::AST::Node)
142
- node = child
166
+ def process_variable_operator_assignment(node)
167
+ if LOGICAL_OPERATOR_ASSIGNMENT_TYPES.include?(node.type)
168
+ asgn_node, rhs_node = *node
169
+ else
170
+ asgn_node, _operator, rhs_node = *node
171
+ end
143
172
 
144
- if index == 0 &&
145
- TWISTED_SCOPE_NODE_TYPES.include?(origin_node.type)
146
- next
147
- end
173
+ return unless asgn_node.type == :lvasgn
148
174
 
149
- @callback.call(node)
175
+ name = asgn_node.children.first
150
176
 
151
- scan_children(node)
152
- end
177
+ unless variable_table.variable_exist?(name)
178
+ variable_table.declare_variable(name, asgn_node)
153
179
  end
154
180
 
155
- def scan_children(node)
156
- case node.type
157
- when *POST_CONDITION_LOOP_NODE_TYPES
158
- # Loop body nodes need to be scanned first.
159
- #
160
- # Ruby:
161
- # begin
162
- # foo = 1
163
- # end while foo > 10
164
- # puts foo
165
- #
166
- # AST:
167
- # (begin
168
- # (while-post
169
- # (send
170
- # (lvar :foo) :>
171
- # (int 10))
172
- # (kwbegin
173
- # (lvasgn :foo
174
- # (int 1))))
175
- # (send nil :puts
176
- # (lvar :foo)))
177
- scan_nodes_in_scope(node.children[1], true)
178
- scan_nodes_in_scope(node.children[0], true)
179
- when *TWISTED_SCOPE_NODE_TYPES
180
- # The variable foo belongs to the top level scope,
181
- # but in AST, it's under the block node.
182
- #
183
- # Ruby:
184
- # some_method(foo = 1) do
185
- # end
186
- # puts foo
187
- #
188
- # AST:
189
- # (begin
190
- # (block
191
- # (send nil :some_method
192
- # (lvasgn :foo
193
- # (int 1)))
194
- # (args) nil)
195
- # (send nil :puts
196
- # (lvar :foo)))
197
- #
198
- # So the the method argument nodes need to be processed
199
- # in current scope.
200
- #
201
- # Same thing.
202
- #
203
- # Ruby:
204
- # instance = Object.new
205
- # class << instance
206
- # foo = 1
207
- # end
208
- #
209
- # AST:
210
- # (begin
211
- # (lvasgn :instance
212
- # (send
213
- # (const nil :Object) :new))
214
- # (sclass
215
- # (lvar :instance)
216
- # (begin
217
- # (lvasgn :foo
218
- # (int 1))
219
- scan_nodes_in_scope(node.children.first, true)
220
- when *SCOPE_TYPES
221
- # Do not go into inner scope.
222
- else
223
- scan_nodes_in_scope(node)
224
- end
225
- end
181
+ # The following statements:
182
+ #
183
+ # foo = 1
184
+ # foo += foo = 2
185
+ # # => 3
186
+ #
187
+ # are equivalent to:
188
+ #
189
+ # foo = 1
190
+ # foo = foo + (foo = 2)
191
+ # # => 3
192
+ #
193
+ # So, at operator assignment node, we need to reference the variable
194
+ # before processing rhs nodes.
195
+
196
+ variable_table.reference_variable(name, node)
197
+ process_node(rhs_node)
198
+ variable_table.assign_to_variable(name, asgn_node)
199
+
200
+ skip_children!
226
201
  end
227
202
 
228
- def variable_table
229
- @variable_table ||= VariableTable.new(self)
203
+ def process_variable_multiple_assignment(node)
204
+ lhs_node, rhs_node = *node
205
+ process_node(rhs_node)
206
+ process_node(lhs_node)
207
+ skip_children!
230
208
  end
231
209
 
232
- # Starting point.
233
- def inspect_variables(root_node)
234
- return unless root_node
210
+ def process_variable_referencing(node)
211
+ name = node.children.first
212
+ variable_table.reference_variable(name, node)
213
+ end
235
214
 
236
- # Wrap with begin node if it's standalone node.
237
- unless root_node.type == :begin
238
- root_node = Parser::AST::Node.new(:begin, [root_node])
215
+ def process_loop(node)
216
+ if POST_CONDITION_LOOP_TYPES.include?(node.type)
217
+ # See the comment at the end of file for this behavior.
218
+ condition_node, body_node = *node
219
+ process_node(body_node)
220
+ process_node(condition_node)
221
+ else
222
+ process_children(node)
239
223
  end
240
224
 
241
- inspect_variables_in_scope(root_node)
225
+ mark_assignments_as_referenced_in_loop(node)
226
+
227
+ skip_children!
242
228
  end
243
229
 
244
- # This is called for each scope recursively.
245
- def inspect_variables_in_scope(scope_node)
246
- variable_table.push_scope(scope_node)
230
+ def process_rescue(node)
231
+ resbody_nodes = node.children.select do |child|
232
+ next false unless child.is_a?(Parser::AST::Node)
233
+ child.type == :resbody
234
+ end
247
235
 
248
- NodeScanner.scan_nodes_in_scope(scope_node) do |node|
249
- # puts "scope:#{variable_table.current_scope_level} node:#{node}"
250
- process_node(node)
236
+ contain_retry = resbody_nodes.any? do |resbody_node|
237
+ scan(resbody_node) do |node_in_resbody|
238
+ break true if node_in_resbody.type == :retry
239
+ end
251
240
  end
252
241
 
253
- variable_table.pop_scope
242
+ # Treat begin..rescue..end with retry as a loop.
243
+ process_loop(node) if contain_retry
254
244
  end
255
245
 
256
- def process_node(node)
257
- case node.type
258
- when *ARGUMENT_DECLARATION_TYPES
259
- variable_table.add_variable_entry(node)
260
- when :lvasgn
261
- variable_name = node.children.first
262
- process_variable_assignment(node, variable_name)
263
- when :match_with_lvasgn
264
- process_named_captures(node)
265
- when *VARIABLE_USE_TYPES
266
- variable_name = node.children.first
267
- variable_entry = variable_table.find_variable_entry(variable_name)
268
- unless variable_entry
269
- fail "Using undeclared local variable \"#{variable_name}\" " +
270
- "at #{node.loc.expression}, #{node.inspect}"
246
+ def process_zero_arity_super(node)
247
+ variable_table.accessible_variables.each do |variable|
248
+ next unless variable.method_argument?
249
+ variable.reference!(node)
250
+ end
251
+ end
252
+
253
+ def process_scope(node)
254
+ if TWISTED_SCOPE_TYPES.include?(node.type)
255
+ # See the comment at the end of file for this behavior.
256
+ twisted_nodes = [node.children[0]]
257
+ twisted_nodes << node.children[1] if node.type == :class
258
+ twisted_nodes.compact!
259
+
260
+ twisted_nodes.each do |twisted_node|
261
+ process_node(twisted_node)
262
+ scanned_nodes << twisted_node
271
263
  end
272
- variable_entry.used = true
273
- when *SCOPE_TYPES
274
- inspect_variables_in_scope(node)
275
264
  end
265
+
266
+ inspect_variables_in_scope(node)
267
+ skip_children!
276
268
  end
277
269
 
278
- def process_variable_assignment(node, name)
279
- entry = variable_table.find_variable_entry(name)
280
- if entry
281
- entry.used = true
282
- else
283
- variable_table.add_variable_entry(node, name)
270
+ # Mark all assignments which are referenced in the same loop
271
+ # as referenced by ignoring AST order since they would be referenced
272
+ # in next iteration.
273
+ def mark_assignments_as_referenced_in_loop(node)
274
+ referenced_variable_names_in_loop = []
275
+ assignment_nodes_in_loop = []
276
+
277
+ # #scan does not consider scope,
278
+ # but we don't need to care about it here.
279
+ scan(node) do |scanning_node|
280
+ case scanning_node.type
281
+ when :lvar
282
+ referenced_variable_names_in_loop << scanning_node.children.first
283
+ when :lvasgn
284
+ assignment_nodes_in_loop << scanning_node
285
+ end
286
+ end
287
+
288
+ referenced_variable_names_in_loop.each do |name|
289
+ variable = variable_table.find_variable(name)
290
+ # Non related references which are catched in the above scan
291
+ # would be skipped here.
292
+ next unless variable
293
+ variable.assignments.each do |assignment|
294
+ next if assignment_nodes_in_loop.none? do |assignment_node|
295
+ assignment_node.equal?(assignment.node)
296
+ end
297
+ assignment.reference!
298
+ end
284
299
  end
285
300
  end
286
301
 
287
- def process_named_captures(match_with_lvasgn_node)
288
- regexp_string = match_with_lvasgn_node.children[0]
289
- .children[0]
290
- .children[0]
291
- regexp = Regexp.new(regexp_string)
292
- variable_names = regexp.named_captures.keys
302
+ # Simple recursive scan
303
+ def scan(node, &block)
304
+ node.children.each do |child|
305
+ next unless child.is_a?(Parser::AST::Node)
306
+ yield child
307
+ scan(child, &block)
308
+ end
309
+ nil
310
+ end
293
311
 
294
- variable_names.each do |name|
295
- process_variable_assignment(match_with_lvasgn_node, name)
312
+ # Use Node#equal? for accurate check.
313
+ def scanned_node?(node)
314
+ scanned_nodes.any? do |scanned_node|
315
+ scanned_node.equal?(node)
296
316
  end
297
317
  end
298
318
 
319
+ def scanned_nodes
320
+ @scanned_nodes ||= []
321
+ end
322
+
299
323
  # Hooks
300
324
 
301
325
  def before_entering_scope(scope)
@@ -310,11 +334,75 @@ module Rubocop
310
334
  def after_leaving_scope(scope)
311
335
  end
312
336
 
313
- def before_declaring_variable(variable_entry)
337
+ def before_declaring_variable(variable_variable)
314
338
  end
315
339
 
316
- def after_declaring_variable(variable_entry)
340
+ def after_declaring_variable(variable_variable)
317
341
  end
342
+
343
+ # Post condition loops
344
+ #
345
+ # Loop body nodes need to be scanned first.
346
+ #
347
+ # Ruby:
348
+ # begin
349
+ # foo = 1
350
+ # end while foo > 10
351
+ # puts foo
352
+ #
353
+ # AST:
354
+ # (begin
355
+ # (while-post
356
+ # (send
357
+ # (lvar :foo) :>
358
+ # (int 10))
359
+ # (kwbegin
360
+ # (lvasgn :foo
361
+ # (int 1))))
362
+ # (send nil :puts
363
+ # (lvar :foo)))
364
+
365
+ # Twisted scope types
366
+ #
367
+ # The variable foo belongs to the top level scope,
368
+ # but in AST, it's under the block node.
369
+ #
370
+ # Ruby:
371
+ # some_method(foo = 1) do
372
+ # end
373
+ # puts foo
374
+ #
375
+ # AST:
376
+ # (begin
377
+ # (block
378
+ # (send nil :some_method
379
+ # (lvasgn :foo
380
+ # (int 1)))
381
+ # (args) nil)
382
+ # (send nil :puts
383
+ # (lvar :foo)))
384
+ #
385
+ # So the the method argument nodes need to be processed
386
+ # in current scope.
387
+ #
388
+ # Same thing.
389
+ #
390
+ # Ruby:
391
+ # instance = Object.new
392
+ # class << instance
393
+ # foo = 1
394
+ # end
395
+ #
396
+ # AST:
397
+ # (begin
398
+ # (lvasgn :instance
399
+ # (send
400
+ # (const nil :Object) :new))
401
+ # (sclass
402
+ # (lvar :instance)
403
+ # (begin
404
+ # (lvasgn :foo
405
+ # (int 1))
318
406
  end
319
407
  end
320
408
  end