rubocop 0.46.0 → 0.47.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 (214) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +77 -2
  4. data/config/default.yml +151 -74
  5. data/config/disabled.yml +9 -0
  6. data/config/enabled.yml +49 -9
  7. data/lib/rubocop.rb +36 -8
  8. data/lib/rubocop/ast/builder.rb +59 -0
  9. data/lib/rubocop/ast/node.rb +607 -0
  10. data/lib/rubocop/ast/node/array_node.rb +45 -0
  11. data/lib/rubocop/ast/node/case_node.rb +63 -0
  12. data/lib/rubocop/ast/node/for_node.rb +53 -0
  13. data/lib/rubocop/ast/node/hash_node.rb +102 -0
  14. data/lib/rubocop/ast/node/if_node.rb +136 -0
  15. data/lib/rubocop/ast/node/keyword_splat_node.rb +45 -0
  16. data/lib/rubocop/ast/node/mixin/conditional_node.rb +45 -0
  17. data/lib/rubocop/ast/node/mixin/hash_element_node.rb +125 -0
  18. data/lib/rubocop/ast/node/mixin/modifier_node.rb +17 -0
  19. data/lib/rubocop/ast/node/pair_node.rb +64 -0
  20. data/lib/rubocop/ast/node/until_node.rb +43 -0
  21. data/lib/rubocop/ast/node/when_node.rb +61 -0
  22. data/lib/rubocop/ast/node/while_node.rb +43 -0
  23. data/lib/rubocop/ast/sexp.rb +16 -0
  24. data/lib/rubocop/{ast_node → ast}/traversal.rb +1 -1
  25. data/lib/rubocop/cli.rb +18 -14
  26. data/lib/rubocop/comment_config.rb +1 -3
  27. data/lib/rubocop/config.rb +93 -35
  28. data/lib/rubocop/config_loader.rb +1 -1
  29. data/lib/rubocop/cop/badge.rb +73 -0
  30. data/lib/rubocop/cop/bundler/duplicated_gem.rb +2 -2
  31. data/lib/rubocop/cop/bundler/ordered_gems.rb +43 -3
  32. data/lib/rubocop/cop/commissioner.rb +17 -6
  33. data/lib/rubocop/cop/cop.rb +25 -112
  34. data/lib/rubocop/cop/lint/ambiguous_operator.rb +9 -4
  35. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +7 -0
  36. data/lib/rubocop/cop/lint/assignment_in_condition.rb +18 -4
  37. data/lib/rubocop/cop/lint/block_alignment.rb +40 -9
  38. data/lib/rubocop/cop/lint/circular_argument_reference.rb +14 -0
  39. data/lib/rubocop/cop/lint/condition_position.rb +14 -16
  40. data/lib/rubocop/cop/lint/debugger.rb +28 -0
  41. data/lib/rubocop/cop/lint/def_end_alignment.rb +21 -1
  42. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +13 -1
  43. data/lib/rubocop/cop/lint/duplicate_case_condition.rb +26 -22
  44. data/lib/rubocop/cop/lint/duplicate_methods.rb +15 -1
  45. data/lib/rubocop/cop/lint/duplicated_key.rb +16 -8
  46. data/lib/rubocop/cop/lint/each_with_object_argument.rb +9 -0
  47. data/lib/rubocop/cop/lint/else_layout.rb +26 -29
  48. data/lib/rubocop/cop/lint/empty_ensure.rb +38 -0
  49. data/lib/rubocop/cop/lint/empty_expression.rb +11 -1
  50. data/lib/rubocop/cop/lint/empty_interpolation.rb +8 -0
  51. data/lib/rubocop/cop/lint/empty_when.rb +14 -16
  52. data/lib/rubocop/cop/lint/end_alignment.rb +48 -28
  53. data/lib/rubocop/cop/lint/end_in_method.rb +23 -0
  54. data/lib/rubocop/cop/lint/ensure_return.rb +21 -0
  55. data/lib/rubocop/cop/lint/float_out_of_range.rb +5 -0
  56. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +29 -4
  57. data/lib/rubocop/cop/lint/handle_exceptions.rb +40 -0
  58. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +7 -2
  59. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +11 -2
  60. data/lib/rubocop/cop/lint/invalid_character_literal.rb +3 -0
  61. data/lib/rubocop/cop/lint/literal_in_condition.rb +34 -36
  62. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +8 -0
  63. data/lib/rubocop/cop/lint/loop.rb +36 -0
  64. data/lib/rubocop/cop/lint/multiple_compare.rb +46 -0
  65. data/lib/rubocop/cop/lint/nested_method_definition.rb +22 -0
  66. data/lib/rubocop/cop/lint/next_without_accumulator.rb +5 -0
  67. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +8 -0
  68. data/lib/rubocop/cop/lint/percent_string_array.rb +27 -13
  69. data/lib/rubocop/cop/lint/percent_symbol_array.rb +14 -4
  70. data/lib/rubocop/cop/lint/rand_one.rb +7 -3
  71. data/lib/rubocop/cop/lint/require_parentheses.rb +20 -19
  72. data/lib/rubocop/cop/lint/rescue_exception.rb +20 -0
  73. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +66 -0
  74. data/lib/rubocop/cop/lint/shadowed_exception.rb +6 -1
  75. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +24 -0
  76. data/lib/rubocop/cop/lint/string_conversion_in_interpolation.rb +8 -0
  77. data/lib/rubocop/cop/lint/underscore_prefixed_variable_name.rb +24 -0
  78. data/lib/rubocop/cop/lint/unified_integer.rb +5 -0
  79. data/lib/rubocop/cop/lint/unneeded_disable.rb +2 -2
  80. data/lib/rubocop/cop/lint/unneeded_splat_expansion.rb +5 -0
  81. data/lib/rubocop/cop/lint/unreachable_code.rb +17 -0
  82. data/lib/rubocop/cop/lint/unused_block_argument.rb +2 -0
  83. data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
  84. data/lib/rubocop/cop/lint/useless_access_modifier.rb +28 -1
  85. data/lib/rubocop/cop/lint/useless_assignment.rb +18 -0
  86. data/lib/rubocop/cop/lint/useless_comparison.rb +3 -1
  87. data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +16 -1
  88. data/lib/rubocop/cop/lint/useless_setter_call.rb +16 -4
  89. data/lib/rubocop/cop/lint/void.rb +52 -0
  90. data/lib/rubocop/cop/message_annotator.rb +102 -0
  91. data/lib/rubocop/cop/metrics/block_length.rb +6 -0
  92. data/lib/rubocop/cop/metrics/block_nesting.rb +17 -5
  93. data/lib/rubocop/cop/metrics/line_length.rb +11 -4
  94. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -2
  95. data/lib/rubocop/cop/mixin/array_syntax.rb +2 -11
  96. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +12 -5
  97. data/lib/rubocop/cop/mixin/configurable_formatting.rb +48 -0
  98. data/lib/rubocop/cop/mixin/configurable_max.rb +3 -3
  99. data/lib/rubocop/cop/mixin/configurable_naming.rb +5 -33
  100. data/lib/rubocop/cop/mixin/configurable_numbering.rb +6 -47
  101. data/lib/rubocop/cop/mixin/documentation_comment.rb +7 -1
  102. data/lib/rubocop/cop/mixin/duplication.rb +46 -0
  103. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +2 -2
  104. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +14 -11
  105. data/lib/rubocop/cop/mixin/hash_alignment.rb +114 -0
  106. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +3 -3
  107. data/lib/rubocop/cop/mixin/negative_conditional.rb +21 -7
  108. data/lib/rubocop/cop/mixin/on_method_def.rb +14 -0
  109. data/lib/rubocop/cop/mixin/on_normal_if_unless.rb +1 -24
  110. data/lib/rubocop/cop/mixin/statement_modifier.rb +8 -13
  111. data/lib/rubocop/cop/mixin/target_ruby_version.rb +16 -0
  112. data/lib/rubocop/cop/mixin/trailing_comma.rb +2 -3
  113. data/lib/rubocop/cop/offense.rb +1 -1
  114. data/lib/rubocop/cop/performance/case_when_splat.rb +56 -59
  115. data/lib/rubocop/cop/performance/detect.rb +2 -2
  116. data/lib/rubocop/cop/performance/flat_map.rb +3 -3
  117. data/lib/rubocop/cop/performance/redundant_merge.rb +3 -6
  118. data/lib/rubocop/cop/performance/regexp_match.rb +201 -0
  119. data/lib/rubocop/cop/rails/delegate.rb +2 -2
  120. data/lib/rubocop/cop/rails/delegate_allow_blank.rb +10 -19
  121. data/lib/rubocop/cop/rails/enum_uniqueness.rb +12 -40
  122. data/lib/rubocop/cop/rails/file_path.rb +80 -0
  123. data/lib/rubocop/cop/rails/find_each.rb +5 -14
  124. data/lib/rubocop/cop/rails/http_positional_arguments.rb +30 -24
  125. data/lib/rubocop/cop/rails/not_null_column.rb +23 -0
  126. data/lib/rubocop/cop/rails/reversible_migration.rb +217 -0
  127. data/lib/rubocop/cop/rails/safe_navigation.rb +4 -2
  128. data/lib/rubocop/cop/rails/skips_model_validations.rb +46 -0
  129. data/lib/rubocop/cop/rails/time_zone.rb +1 -1
  130. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +7 -5
  131. data/lib/rubocop/cop/registry.rb +170 -0
  132. data/lib/rubocop/cop/{lint → security}/eval.rb +7 -1
  133. data/lib/rubocop/cop/security/marshal_load.rb +33 -0
  134. data/lib/rubocop/cop/security/yaml_load.rb +37 -0
  135. data/lib/rubocop/cop/style/align_hash.rb +138 -169
  136. data/lib/rubocop/cop/style/and_or.rb +1 -1
  137. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +10 -15
  138. data/lib/rubocop/cop/style/case_indentation.rb +36 -27
  139. data/lib/rubocop/cop/style/conditional_assignment.rb +64 -47
  140. data/lib/rubocop/cop/style/each_with_object.rb +4 -1
  141. data/lib/rubocop/cop/style/else_alignment.rb +14 -20
  142. data/lib/rubocop/cop/style/empty_case_condition.rb +16 -25
  143. data/lib/rubocop/cop/style/empty_else.rb +20 -22
  144. data/lib/rubocop/cop/style/empty_literal.rb +4 -4
  145. data/lib/rubocop/cop/style/empty_method.rb +12 -6
  146. data/lib/rubocop/cop/style/encoding.rb +1 -1
  147. data/lib/rubocop/cop/style/file_name.rb +24 -4
  148. data/lib/rubocop/cop/style/first_method_argument_line_break.rb +1 -1
  149. data/lib/rubocop/cop/style/format_string.rb +17 -48
  150. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +40 -11
  151. data/lib/rubocop/cop/style/guard_clause.rb +11 -17
  152. data/lib/rubocop/cop/style/hash_syntax.rb +24 -42
  153. data/lib/rubocop/cop/style/identical_conditional_branches.rb +40 -28
  154. data/lib/rubocop/cop/style/if_inside_else.rb +6 -9
  155. data/lib/rubocop/cop/style/if_unless_modifier.rb +16 -25
  156. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +3 -9
  157. data/lib/rubocop/cop/style/indent_array.rb +1 -1
  158. data/lib/rubocop/cop/style/indentation_width.rb +29 -60
  159. data/lib/rubocop/cop/style/infinite_loop.rb +21 -22
  160. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +86 -0
  161. data/lib/rubocop/cop/style/{method_call_parentheses.rb → method_call_without_args_parentheses.rb} +8 -1
  162. data/lib/rubocop/cop/style/missing_else.rb +40 -14
  163. data/lib/rubocop/cop/style/multiline_if_modifier.rb +5 -15
  164. data/lib/rubocop/cop/style/multiline_if_then.rb +14 -8
  165. data/lib/rubocop/cop/style/multiline_method_call_indentation.rb +3 -3
  166. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -5
  167. data/lib/rubocop/cop/style/mutable_constant.rb +3 -2
  168. data/lib/rubocop/cop/style/negated_if.rb +3 -19
  169. data/lib/rubocop/cop/style/negated_while.rb +2 -17
  170. data/lib/rubocop/cop/style/nested_modifier.rb +16 -43
  171. data/lib/rubocop/cop/style/nested_ternary_operator.rb +3 -5
  172. data/lib/rubocop/cop/style/next.rb +23 -21
  173. data/lib/rubocop/cop/style/non_nil_check.rb +2 -3
  174. data/lib/rubocop/cop/style/not.rb +1 -3
  175. data/lib/rubocop/cop/style/numeric_literals.rb +2 -2
  176. data/lib/rubocop/cop/style/one_line_conditional.rb +12 -22
  177. data/lib/rubocop/cop/style/option_hash.rb +4 -15
  178. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -3
  179. data/lib/rubocop/cop/style/parentheses_around_condition.rb +8 -12
  180. data/lib/rubocop/cop/style/percent_q_literals.rb +15 -12
  181. data/lib/rubocop/cop/style/redundant_freeze.rb +3 -2
  182. data/lib/rubocop/cop/style/redundant_parentheses.rb +27 -4
  183. data/lib/rubocop/cop/style/redundant_return.rb +4 -8
  184. data/lib/rubocop/cop/style/safe_navigation.rb +13 -6
  185. data/lib/rubocop/cop/style/space_after_colon.rb +2 -4
  186. data/lib/rubocop/cop/style/space_around_block_parameters.rb +1 -1
  187. data/lib/rubocop/cop/style/space_around_operators.rb +15 -13
  188. data/lib/rubocop/cop/style/string_methods.rb +1 -3
  189. data/lib/rubocop/cop/style/symbol_array.rb +1 -5
  190. data/lib/rubocop/cop/style/ternary_parentheses.rb +5 -6
  191. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +2 -5
  192. data/lib/rubocop/cop/style/trailing_comma_in_literal.rb +1 -1
  193. data/lib/rubocop/cop/style/unless_else.rb +1 -5
  194. data/lib/rubocop/cop/style/when_then.rb +4 -2
  195. data/lib/rubocop/cop/style/while_until_do.rb +9 -13
  196. data/lib/rubocop/cop/style/while_until_modifier.rb +12 -11
  197. data/lib/rubocop/cop/style/word_array.rb +5 -9
  198. data/lib/rubocop/cop/team.rb +16 -15
  199. data/lib/rubocop/cop/util.rb +13 -3
  200. data/lib/rubocop/formatter/clang_style_formatter.rb +2 -2
  201. data/lib/rubocop/formatter/disabled_config_formatter.rb +2 -1
  202. data/lib/rubocop/magic_comment.rb +196 -0
  203. data/lib/rubocop/options.rb +5 -4
  204. data/lib/rubocop/processed_source.rb +1 -1
  205. data/lib/rubocop/rspec/cop_helper.rb +9 -0
  206. data/lib/rubocop/rspec/shared_examples.rb +1 -1
  207. data/lib/rubocop/runner.rb +7 -2
  208. data/lib/rubocop/version.rb +1 -1
  209. metadata +41 -14
  210. data/lib/rubocop/ast_node.rb +0 -624
  211. data/lib/rubocop/ast_node/builder.rb +0 -30
  212. data/lib/rubocop/ast_node/sexp.rb +0 -13
  213. data/lib/rubocop/cop/mixin/hash_node.rb +0 -14
  214. data/lib/rubocop/cop/mixin/if_node.rb +0 -42
@@ -69,6 +69,11 @@ Style/InlineComment:
69
69
  Description: 'Avoid trailing inline comments.'
70
70
  Enabled: false
71
71
 
72
+ Style/MethodCallWithArgsParentheses:
73
+ Description: 'Use parentheses for method calls with arguments.'
74
+ StyleGuide: '#method-invocation-parens'
75
+ Enabled: false
76
+
72
77
  Style/MethodCalledOnDoEndBlock:
73
78
  Description: 'Avoid chaining a method call on a do...end block.'
74
79
  StyleGuide: '#single-line-blocks'
@@ -113,3 +118,7 @@ Style/SymbolArray:
113
118
  Description: 'Use %i or %I for arrays of symbols.'
114
119
  StyleGuide: '#percent-i'
115
120
  Enabled: false
121
+
122
+ Style/SingleLineBlockParams:
123
+ Description: 'Enforces the names of some block params.'
124
+ Enabled: false
@@ -404,7 +404,7 @@ Style/LineEndConcatenation:
404
404
  line end.
405
405
  Enabled: true
406
406
 
407
- Style/MethodCallParentheses:
407
+ Style/MethodCallWithoutArgsParentheses:
408
408
  Description: 'Do not use parentheses for method calls with no arguments.'
409
409
  StyleGuide: '#method-invocation-parens'
410
410
  Enabled: true
@@ -710,10 +710,6 @@ Style/SignalException:
710
710
  StyleGuide: '#prefer-raise-over-fail'
711
711
  Enabled: true
712
712
 
713
- Style/SingleLineBlockParams:
714
- Description: 'Enforces the names of some block params.'
715
- Enabled: true
716
-
717
713
  Style/SingleLineMethods:
718
714
  Description: 'Avoid single-line methods.'
719
715
  StyleGuide: '#no-single-line-methods'
@@ -1120,10 +1116,6 @@ Lint/EnsureReturn:
1120
1116
  StyleGuide: '#no-return-ensure'
1121
1117
  Enabled: true
1122
1118
 
1123
- Lint/Eval:
1124
- Description: 'The use of eval represents a serious security risk.'
1125
- Enabled: true
1126
-
1127
1119
  Lint/FloatOutOfRange:
1128
1120
  Description: >-
1129
1121
  Catches floating-point literals too large or small for Ruby to
@@ -1176,6 +1168,10 @@ Lint/Loop:
1176
1168
  StyleGuide: '#loop-with-break'
1177
1169
  Enabled: true
1178
1170
 
1171
+ Lint/MultipleCompare:
1172
+ Description: "Use `&&` operator to compare multiple value."
1173
+ Enabled: true
1174
+
1179
1175
  Lint/NestedMethodDefinition:
1180
1176
  Description: 'Do not use nested method definitions.'
1181
1177
  StyleGuide: '#no-nested-methods'
@@ -1225,6 +1221,10 @@ Lint/RescueException:
1225
1221
  StyleGuide: '#no-blind-rescues'
1226
1222
  Enabled: true
1227
1223
 
1224
+ Lint/SafeNavigationChain:
1225
+ Description: 'Do not chain ordinary method call after safe navigation operator.'
1226
+ Enabled: true
1227
+
1228
1228
  Lint/ShadowedException:
1229
1229
  Description: >-
1230
1230
  Avoid rescuing a higher level exception
@@ -1279,6 +1279,7 @@ Lint/UselessAccessModifier:
1279
1279
  Description: 'Checks for useless access modifiers.'
1280
1280
  Enabled: true
1281
1281
  ContextCreatingMethods: []
1282
+ MethodCreatingMethods: []
1282
1283
 
1283
1284
  Lint/UselessAssignment:
1284
1285
  Description: 'Checks for useless assignment to a local variable.'
@@ -1408,6 +1409,10 @@ Performance/RedundantSortBy:
1408
1409
  Description: 'Use `sort` instead of `sort_by { |x| x }`.'
1409
1410
  Enabled: true
1410
1411
 
1412
+ Performance/RegexpMatch:
1413
+ Description: 'Use `match?` instead of `Regexp#match`, `String#match`, `Symbol#match`, `Regexp#===` or `=~` when `MatchData` is not used.'
1414
+ Enabled: true
1415
+
1411
1416
  Performance/ReverseEach:
1412
1417
  Description: 'Use `reverse_each` instead of `reverse.each`.'
1413
1418
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablereverseeach-vs-enumerablereverse_each-code'
@@ -1488,6 +1493,10 @@ Rails/Exit:
1488
1493
  exits during unit testing or running in production.
1489
1494
  Enabled: true
1490
1495
 
1496
+ Rails/FilePath:
1497
+ Description: 'Use `Rails.root.join` for file path joining.'
1498
+ Enabled: true
1499
+
1491
1500
  Rails/FindBy:
1492
1501
  Description: 'Prefer find_by over where.first.'
1493
1502
  StyleGuide: 'https://github.com/bbatsov/rails-style-guide#find_by'
@@ -1537,6 +1546,12 @@ Rails/RequestReferer:
1537
1546
  Description: 'Use consistent syntax for request.referer.'
1538
1547
  Enabled: true
1539
1548
 
1549
+ Rails/ReversibleMigration:
1550
+ Description: 'Checks whether the change method of the migration file is reversible.'
1551
+ StyleGuide: 'https://github.com/bbatsov/rails-style-guide#reversible-migration'
1552
+ Reference: 'http://api.rubyonrails.org/classes/ActiveRecord/Migration/CommandRecorder.html'
1553
+ Enabled: true
1554
+
1540
1555
  Rails/SafeNavigation:
1541
1556
  Description: "Use Ruby's safe navigation operator (`&.`) instead of `try!`"
1542
1557
  Enabled: true
@@ -1555,10 +1570,21 @@ Rails/UniqBeforePluck:
1555
1570
  Description: 'Prefer the use of uniq or distinct before pluck.'
1556
1571
  Enabled: true
1557
1572
 
1573
+ Rails/SkipsModelValidations:
1574
+ Description: >-
1575
+ Use methods that skips model validations with caution.
1576
+ See reference for more information.
1577
+ Reference: 'http://guides.rubyonrails.org/active_record_validations.html#skipping-validations'
1578
+ Enabled: true
1579
+
1558
1580
  Rails/Validation:
1559
1581
  Description: 'Use validates :attribute, hash of validations.'
1560
1582
  Enabled: true
1561
1583
 
1584
+ Security/Eval:
1585
+ Description: 'The use of eval represents a serious security risk.'
1586
+ Enabled: true
1587
+
1562
1588
  Security/JSONLoad:
1563
1589
  Description: >-
1564
1590
  Prefer usage of `JSON.parse` over `JSON.load` due to potential
@@ -1569,6 +1595,20 @@ Security/JSONLoad:
1569
1595
  # on the value of the argument.
1570
1596
  AutoCorrect: false
1571
1597
 
1598
+ Security/MarshalLoad:
1599
+ Description: >-
1600
+ Avoid using of `Marshal.load` or `Marshal.restore` due to potential
1601
+ security issues. See reference for more information.
1602
+ Reference: 'http://ruby-doc.org/core-2.3.3/Marshal.html#module-Marshal-label-Security+considerations'
1603
+ Enabled: true
1604
+
1605
+ Security/YAMLLoad:
1606
+ Description: >-
1607
+ Prefer usage of `YAML.safe_load` over `YAML.load` due to potential
1608
+ security issues. See reference for more information.
1609
+ Reference: 'https://ruby-doc.org/stdlib-2.3.3/libdoc/yaml/rdoc/YAML.html#module-YAML-label-Security'
1610
+ Enabled: true
1611
+
1572
1612
  ##################### Bundler #############################
1573
1613
 
1574
1614
  Bundler/DuplicatedGem:
@@ -19,17 +19,33 @@ require 'rubocop/string_util'
19
19
  require 'rubocop/name_similarity'
20
20
  require 'rubocop/node_pattern'
21
21
  require 'rubocop/string_interpreter'
22
- require 'rubocop/ast_node/sexp'
23
- require 'rubocop/ast_node'
24
- require 'rubocop/ast_node/builder'
25
- require 'rubocop/ast_node/traversal'
22
+ require 'rubocop/ast/sexp'
23
+ require 'rubocop/ast/node'
24
+ require 'rubocop/ast/node/mixin/conditional_node'
25
+ require 'rubocop/ast/node/mixin/hash_element_node'
26
+ require 'rubocop/ast/node/mixin/modifier_node'
27
+ require 'rubocop/ast/node/array_node'
28
+ require 'rubocop/ast/node/case_node'
29
+ require 'rubocop/ast/node/for_node'
30
+ require 'rubocop/ast/node/hash_node'
31
+ require 'rubocop/ast/node/if_node'
32
+ require 'rubocop/ast/node/keyword_splat_node'
33
+ require 'rubocop/ast/node/pair_node'
34
+ require 'rubocop/ast/node/until_node'
35
+ require 'rubocop/ast/node/when_node'
36
+ require 'rubocop/ast/node/while_node'
37
+ require 'rubocop/ast/builder'
38
+ require 'rubocop/ast/traversal'
26
39
  require 'rubocop/error'
27
40
  require 'rubocop/warning'
28
41
 
29
42
  require 'rubocop/cop/util'
30
43
  require 'rubocop/cop/offense'
44
+ require 'rubocop/cop/message_annotator'
31
45
  require 'rubocop/cop/ignored_node'
32
46
  require 'rubocop/cop/autocorrect_logic'
47
+ require 'rubocop/cop/badge'
48
+ require 'rubocop/cop/registry'
33
49
  require 'rubocop/cop/cop'
34
50
  require 'rubocop/cop/commissioner'
35
51
  require 'rubocop/cop/corrector'
@@ -54,16 +70,17 @@ require 'rubocop/cop/mixin/configurable_max'
54
70
  require 'rubocop/cop/mixin/code_length' # relies on configurable_max
55
71
  require 'rubocop/cop/mixin/classish_length' # relies on code_length
56
72
  require 'rubocop/cop/mixin/configurable_enforced_style'
73
+ require 'rubocop/cop/mixin/configurable_formatting'
57
74
  require 'rubocop/cop/mixin/configurable_naming'
58
75
  require 'rubocop/cop/mixin/configurable_numbering'
59
76
  require 'rubocop/cop/mixin/def_node'
60
77
  require 'rubocop/cop/mixin/documentation_comment'
78
+ require 'rubocop/cop/mixin/duplication'
61
79
  require 'rubocop/cop/mixin/empty_lines_around_body'
62
80
  require 'rubocop/cop/mixin/end_keyword_alignment'
63
81
  require 'rubocop/cop/mixin/first_element_line_break'
64
82
  require 'rubocop/cop/mixin/frozen_string_literal'
65
- require 'rubocop/cop/mixin/hash_node'
66
- require 'rubocop/cop/mixin/if_node'
83
+ require 'rubocop/cop/mixin/hash_alignment'
67
84
  require 'rubocop/cop/mixin/integer_node'
68
85
  require 'rubocop/cop/mixin/on_method_def'
69
86
  require 'rubocop/cop/mixin/match_range'
@@ -87,6 +104,7 @@ require 'rubocop/cop/mixin/space_inside' # relies on surrounding_space
87
104
  require 'rubocop/cop/mixin/statement_modifier'
88
105
  require 'rubocop/cop/mixin/string_help'
89
106
  require 'rubocop/cop/mixin/string_literals_help'
107
+ require 'rubocop/cop/mixin/target_ruby_version'
90
108
  require 'rubocop/cop/mixin/too_many_lines'
91
109
  require 'rubocop/cop/mixin/trailing_comma'
92
110
  require 'rubocop/cop/mixin/unused_argument'
@@ -115,7 +133,6 @@ require 'rubocop/cop/lint/empty_when'
115
133
  require 'rubocop/cop/lint/end_alignment'
116
134
  require 'rubocop/cop/lint/end_in_method'
117
135
  require 'rubocop/cop/lint/ensure_return'
118
- require 'rubocop/cop/lint/eval'
119
136
  require 'rubocop/cop/lint/float_out_of_range'
120
137
  require 'rubocop/cop/lint/format_parameter_mismatch'
121
138
  require 'rubocop/cop/lint/handle_exceptions'
@@ -126,6 +143,7 @@ require 'rubocop/cop/lint/invalid_character_literal'
126
143
  require 'rubocop/cop/lint/literal_in_condition'
127
144
  require 'rubocop/cop/lint/literal_in_interpolation'
128
145
  require 'rubocop/cop/lint/loop'
146
+ require 'rubocop/cop/lint/multiple_compare'
129
147
  require 'rubocop/cop/lint/nested_method_definition'
130
148
  require 'rubocop/cop/lint/next_without_accumulator'
131
149
  require 'rubocop/cop/lint/non_local_exit_from_iterator'
@@ -135,6 +153,7 @@ require 'rubocop/cop/lint/percent_symbol_array'
135
153
  require 'rubocop/cop/lint/rand_one'
136
154
  require 'rubocop/cop/lint/require_parentheses'
137
155
  require 'rubocop/cop/lint/rescue_exception'
156
+ require 'rubocop/cop/lint/safe_navigation_chain'
138
157
  require 'rubocop/cop/lint/shadowed_exception'
139
158
  require 'rubocop/cop/lint/shadowing_outer_local_variable'
140
159
  require 'rubocop/cop/lint/string_conversion_in_interpolation'
@@ -179,6 +198,7 @@ require 'rubocop/cop/performance/redundant_block_call'
179
198
  require 'rubocop/cop/performance/redundant_match'
180
199
  require 'rubocop/cop/performance/redundant_merge'
181
200
  require 'rubocop/cop/performance/redundant_sort_by'
201
+ require 'rubocop/cop/performance/regexp_match'
182
202
  require 'rubocop/cop/performance/reverse_each'
183
203
  require 'rubocop/cop/performance/sample'
184
204
  require 'rubocop/cop/performance/size'
@@ -278,7 +298,8 @@ require 'rubocop/cop/style/lambda'
278
298
  require 'rubocop/cop/style/lambda_call'
279
299
  require 'rubocop/cop/style/leading_comment_space'
280
300
  require 'rubocop/cop/style/line_end_concatenation'
281
- require 'rubocop/cop/style/method_call_parentheses'
301
+ require 'rubocop/cop/style/method_call_without_args_parentheses'
302
+ require 'rubocop/cop/style/method_call_with_args_parentheses'
282
303
  require 'rubocop/cop/style/method_called_on_do_end_block'
283
304
  require 'rubocop/cop/style/method_def_parentheses'
284
305
  require 'rubocop/cop/style/method_name'
@@ -399,6 +420,7 @@ require 'rubocop/cop/rails/delegate'
399
420
  require 'rubocop/cop/rails/delegate_allow_blank'
400
421
  require 'rubocop/cop/rails/enum_uniqueness'
401
422
  require 'rubocop/cop/rails/exit'
423
+ require 'rubocop/cop/rails/file_path'
402
424
  require 'rubocop/cop/rails/find_by'
403
425
  require 'rubocop/cop/rails/find_each'
404
426
  require 'rubocop/cop/rails/has_and_belongs_to_many'
@@ -409,14 +431,19 @@ require 'rubocop/cop/rails/output'
409
431
  require 'rubocop/cop/rails/pluralization_grammar'
410
432
  require 'rubocop/cop/rails/read_write_attribute'
411
433
  require 'rubocop/cop/rails/request_referer'
434
+ require 'rubocop/cop/rails/reversible_migration'
412
435
  require 'rubocop/cop/rails/safe_navigation'
413
436
  require 'rubocop/cop/rails/save_bang'
414
437
  require 'rubocop/cop/rails/scope_args'
438
+ require 'rubocop/cop/rails/skips_model_validations'
415
439
  require 'rubocop/cop/rails/time_zone'
416
440
  require 'rubocop/cop/rails/uniq_before_pluck'
417
441
  require 'rubocop/cop/rails/validation'
418
442
 
443
+ require 'rubocop/cop/security/eval'
419
444
  require 'rubocop/cop/security/json_load'
445
+ require 'rubocop/cop/security/marshal_load'
446
+ require 'rubocop/cop/security/yaml_load'
420
447
 
421
448
  require 'rubocop/cop/team'
422
449
 
@@ -444,6 +471,7 @@ require 'rubocop/config_store'
444
471
  require 'rubocop/target_finder'
445
472
  require 'rubocop/token'
446
473
  require 'rubocop/comment_config'
474
+ require 'rubocop/magic_comment'
447
475
  require 'rubocop/processed_source'
448
476
  require 'rubocop/result_cache'
449
477
  require 'rubocop/runner'
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module AST
5
+ # `RuboCop::Builder` is an AST builder that is utilized to let `Parser`
6
+ # generate ASTs with {RuboCop::AST::Node}.
7
+ #
8
+ # @example
9
+ # buffer = Parser::Source::Buffer.new('(string)')
10
+ # buffer.source = 'puts :foo'
11
+ #
12
+ # builder = RuboCop::Builder.new
13
+ # parser = Parser::CurrentRuby.new(builder)
14
+ # root_node = parser.parse(buffer)
15
+ class Builder < Parser::Builders::Default
16
+ NODE_MAP = {
17
+ ArrayNode => [:array],
18
+ CaseNode => [:case],
19
+ ForNode => [:for],
20
+ HashNode => [:hash],
21
+ IfNode => [:if],
22
+ KeywordSplatNode => [:kwsplat],
23
+ PairNode => [:pair],
24
+ UntilNode => [:until, :until_post],
25
+ WhenNode => [:when],
26
+ WhileNode => [:while, :while_post]
27
+ }.freeze
28
+
29
+ # Generates {Node} from the given information.
30
+ #
31
+ # @return [Node] the generated node
32
+ def n(type, children, source_map)
33
+ node_klass(type).new(type, children, location: source_map)
34
+ end
35
+
36
+ # TODO: Figure out what to do about literal encoding handling...
37
+ # More details here https://github.com/whitequark/parser/issues/283
38
+ def string_value(token)
39
+ value(token)
40
+ end
41
+
42
+ private
43
+
44
+ def node_klass(type)
45
+ node_map[type] || Node
46
+ end
47
+
48
+ # Take the human readable constant and generate a hash map where each
49
+ # (mapped) node type is a key with its constant as the value.
50
+ def node_map
51
+ @node_map ||= begin
52
+ NODE_MAP.each_pair.each_with_object({}) do |(klass, types), map|
53
+ types.each { |type| map[type] = klass }
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,607 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module AST
5
+ # `RuboCop::AST::Node` is a subclass of `Parser::AST::Node`. It provides
6
+ # access to parent nodes and an object-oriented way to traverse an AST with
7
+ # the power of `Enumerable`.
8
+ #
9
+ # It has predicate methods for every node type, like this:
10
+ #
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::AST::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, :regopt].freeze
29
+ FALSEY_LITERALS = [:false, :nil].freeze
30
+ LITERALS = (TRUTHY_LITERALS + FALSEY_LITERALS).freeze
31
+ COMPOSITE_LITERALS = [:dstr, :xstr, :dsym, :array, :hash, :irange,
32
+ :erange, :regexp].freeze
33
+ BASIC_LITERALS = (LITERALS - COMPOSITE_LITERALS).freeze
34
+ MUTABLE_LITERALS = [:str, :dstr, :xstr, :array, :hash].freeze
35
+ IMMUTABLE_LITERALS = (LITERALS - MUTABLE_LITERALS).freeze
36
+
37
+ VARIABLES = [:ivar, :gvar, :cvar, :lvar].freeze
38
+ REFERENCES = [:nth_ref, :back_ref].freeze
39
+ KEYWORDS = [:alias, :and, :break, :case, :class, :def, :defs, :defined?,
40
+ :kwbegin, :do, :else, :ensure, :for, :if, :module, :next,
41
+ :not, :or, :postexe, :redo, :rescue, :retry, :return, :self,
42
+ :super, :zsuper, :then, :undef, :until, :when, :while,
43
+ :yield].freeze
44
+ OPERATOR_KEYWORDS = [:and, :or].freeze
45
+ SPECIAL_KEYWORDS = %w(__FILE__ __LINE__ __ENCODING__).freeze
46
+
47
+ # def_matcher can be used to define a pattern-matching method on Node
48
+ class << self
49
+ def def_matcher(method_name, pattern_str)
50
+ compiler = RuboCop::NodePattern::Compiler.new(pattern_str, 'self')
51
+ src = "def #{method_name}(" \
52
+ "#{compiler.emit_param_list});" \
53
+ "#{compiler.emit_method_code};end"
54
+
55
+ file, lineno = *caller.first.split(':')
56
+ class_eval(src, file, lineno.to_i)
57
+ end
58
+ end
59
+
60
+ # @see http://rubydoc.info/gems/ast/AST/Node:initialize
61
+ def initialize(type, children = [], properties = {})
62
+ @mutable_attributes = {}
63
+
64
+ # ::AST::Node#initialize freezes itself.
65
+ super
66
+
67
+ # #parent= may be invoked multiple times for a node because there are
68
+ # pending nodes while constructing AST and they are replaced later.
69
+ # For example, `lvar` and `send` type nodes are initially created as an
70
+ # `ident` type node and fixed to the appropriate type later.
71
+ # So, the #parent attribute needs to be mutable.
72
+ each_child_node do |child_node|
73
+ child_node.parent = self unless child_node.complete?
74
+ end
75
+ end
76
+
77
+ Parser::Meta::NODE_TYPES.each do |node_type|
78
+ method_name = "#{node_type.to_s.gsub(/\W/, '')}_type?"
79
+ define_method(method_name) do
80
+ type == node_type
81
+ end
82
+ end
83
+
84
+ # Returns the parent node, or `nil` if the receiver is a root node.
85
+ #
86
+ # @return [Node, nil] the parent node or `nil`
87
+ def parent
88
+ @mutable_attributes[:parent]
89
+ end
90
+
91
+ def parent=(node)
92
+ @mutable_attributes[:parent] = node
93
+ end
94
+
95
+ def complete!
96
+ @mutable_attributes.freeze
97
+ each_child_node(&:complete!)
98
+ end
99
+
100
+ def complete?
101
+ @mutable_attributes.frozen?
102
+ end
103
+
104
+ protected :parent=
105
+
106
+ # Override `AST::Node#updated` so that `AST::Processor` does not try to
107
+ # mutate our ASTs. Since we keep references from children to parents and
108
+ # not just the other way around, we cannot update an AST and share
109
+ # identical subtrees. Rather, the entire AST must be copied any time any
110
+ # part of it is changed.
111
+ def updated(type = nil, children = nil, properties = {})
112
+ properties[:location] ||= @location
113
+ Node.new(type || @type, children || @children, properties)
114
+ end
115
+
116
+ # Returns the index of the receiver node in its siblings. (Sibling index
117
+ # uses zero based numbering.)
118
+ #
119
+ # @return [Integer] the index of the receiver node in its siblings
120
+ def sibling_index
121
+ parent.children.index { |sibling| sibling.equal?(self) }
122
+ end
123
+
124
+ # Calls the given block for each ancestor node from parent to root.
125
+ # If no block is given, an `Enumerator` is returned.
126
+ #
127
+ # @overload each_ancestor
128
+ # Yield all nodes.
129
+ # @overload each_ancestor(type)
130
+ # Yield only nodes matching the type.
131
+ # @param [Symbol] type a node type
132
+ # @overload each_ancestor(type_a, type_b, ...)
133
+ # Yield only nodes matching any of the types.
134
+ # @param [Symbol] type_a a node type
135
+ # @param [Symbol] type_b a node type
136
+ # @overload each_ancestor(types)
137
+ # Yield only nodes matching any of types in the array.
138
+ # @param [Array<Symbol>] types an array containing node types
139
+ # @yieldparam [Node] node each ancestor node
140
+ # @return [self] if a block is given
141
+ # @return [Enumerator] if no block is given
142
+ def each_ancestor(*types, &block)
143
+ return to_enum(__method__, *types) unless block_given?
144
+
145
+ visit_ancestors(types, &block)
146
+
147
+ self
148
+ end
149
+
150
+ # Returns an array of ancestor nodes.
151
+ # This is a shorthand for `node.each_ancestor.to_a`.
152
+ #
153
+ # @return [Array<Node>] an array of ancestor nodes
154
+ def ancestors
155
+ each_ancestor.to_a
156
+ end
157
+
158
+ # Calls the given block for each child node.
159
+ # If no block is given, an `Enumerator` is returned.
160
+ #
161
+ # Note that this is different from `node.children.each { |child| ... }`
162
+ # which yields all children including non-node elements.
163
+ #
164
+ # @overload each_child_node
165
+ # Yield all nodes.
166
+ # @overload each_child_node(type)
167
+ # Yield only nodes matching the type.
168
+ # @param [Symbol] type a node type
169
+ # @overload each_child_node(type_a, type_b, ...)
170
+ # Yield only nodes matching any of the types.
171
+ # @param [Symbol] type_a a node type
172
+ # @param [Symbol] type_b a node type
173
+ # @overload each_child_node(types)
174
+ # Yield only nodes matching any of types in the array.
175
+ # @param [Array<Symbol>] types an array containing node types
176
+ # @yieldparam [Node] node each child node
177
+ # @return [self] if a block is given
178
+ # @return [Enumerator] if no block is given
179
+ def each_child_node(*types)
180
+ return to_enum(__method__, *types) unless block_given?
181
+
182
+ children.each do |child|
183
+ next unless child.is_a?(Node)
184
+ yield child if types.empty? || types.include?(child.type)
185
+ end
186
+
187
+ self
188
+ end
189
+
190
+ # Returns an array of child nodes.
191
+ # This is a shorthand for `node.each_child_node.to_a`.
192
+ #
193
+ # @return [Array<Node>] an array of child nodes
194
+ def child_nodes
195
+ each_child_node.to_a
196
+ end
197
+
198
+ # Calls the given block for each descendant node with depth first order.
199
+ # If no block is given, an `Enumerator` is returned.
200
+ #
201
+ # @overload each_descendant
202
+ # Yield all nodes.
203
+ # @overload each_descendant(type)
204
+ # Yield only nodes matching the type.
205
+ # @param [Symbol] type a node type
206
+ # @overload each_descendant(type_a, type_b, ...)
207
+ # Yield only nodes matching any of the types.
208
+ # @param [Symbol] type_a a node type
209
+ # @param [Symbol] type_b a node type
210
+ # @overload each_descendant(types)
211
+ # Yield only nodes matching any of types in the array.
212
+ # @param [Array<Symbol>] types an array containing node types
213
+ # @yieldparam [Node] node each descendant node
214
+ # @return [self] if a block is given
215
+ # @return [Enumerator] if no block is given
216
+ def each_descendant(*types, &block)
217
+ return to_enum(__method__, *types) unless block_given?
218
+
219
+ visit_descendants(types, &block)
220
+
221
+ self
222
+ end
223
+
224
+ # Returns an array of descendant nodes.
225
+ # This is a shorthand for `node.each_descendant.to_a`.
226
+ #
227
+ # @return [Array<Node>] an array of descendant nodes
228
+ def descendants
229
+ each_descendant.to_a
230
+ end
231
+
232
+ # Calls the given block for the receiver and each descendant node in
233
+ # depth-first order.
234
+ # If no block is given, an `Enumerator` is returned.
235
+ #
236
+ # This method would be useful when you treat the receiver node as the root
237
+ # of a tree and want to iterate over all nodes in the tree.
238
+ #
239
+ # @overload each_node
240
+ # Yield all nodes.
241
+ # @overload each_node(type)
242
+ # Yield only nodes matching the type.
243
+ # @param [Symbol] type a node type
244
+ # @overload each_node(type_a, type_b, ...)
245
+ # Yield only nodes matching any of the types.
246
+ # @param [Symbol] type_a a node type
247
+ # @param [Symbol] type_b a node type
248
+ # @overload each_node(types)
249
+ # Yield only nodes matching any of types in the array.
250
+ # @param [Array<Symbol>] types an array containing node types
251
+ # @yieldparam [Node] node each node
252
+ # @return [self] if a block is given
253
+ # @return [Enumerator] if no block is given
254
+ def each_node(*types, &block)
255
+ return to_enum(__method__, *types) unless block_given?
256
+
257
+ yield self if types.empty? || types.include?(type)
258
+
259
+ visit_descendants(types, &block)
260
+
261
+ self
262
+ end
263
+
264
+ def source
265
+ loc.expression.source
266
+ end
267
+
268
+ def source_range
269
+ loc.expression
270
+ end
271
+
272
+ ## Destructuring
273
+
274
+ def_matcher :receiver, '{(send $_ ...) (block (send $_ ...) ...)}'
275
+ def_matcher :method_name, '{(send _ $_ ...) (block (send _ $_ ...) ...)}'
276
+ def_matcher :method_args, '{(send _ _ $...) (block (send _ _ $...) ...)}'
277
+ # Note: for masgn, #asgn_rhs will be an array node
278
+ def_matcher :asgn_rhs, '[assignment? (... $_)]'
279
+ def_matcher :str_content, '(str $_)'
280
+
281
+ def const_name
282
+ return unless const_type?
283
+ namespace, name = *self
284
+ if namespace && !namespace.cbase_type?
285
+ "#{namespace.const_name}::#{name}"
286
+ else
287
+ name.to_s
288
+ end
289
+ end
290
+
291
+ def_matcher :defined_module0, <<-PATTERN
292
+ {(class (const $_ $_) ...)
293
+ (module (const $_ $_) ...)
294
+ (casgn $_ $_ (send (const nil {:Class :Module}) :new ...))
295
+ (casgn $_ $_ (block (send (const nil {:Class :Module}) :new ...) ...))}
296
+ PATTERN
297
+ private :defined_module0
298
+
299
+ def defined_module
300
+ namespace, name = *defined_module0
301
+ s(:const, namespace, name) if name
302
+ end
303
+
304
+ def defined_module_name
305
+ (const = defined_module) && const.const_name
306
+ end
307
+
308
+ ## Searching the AST
309
+
310
+ def parent_module_name
311
+ # what class or module is this method/constant/etc definition in?
312
+ # returns nil if answer cannot be determined
313
+ ancestors = each_ancestor(:class, :module, :sclass, :casgn, :block)
314
+ result = ancestors.map do |ancestor|
315
+ parent_module_name_part(ancestor) { |full_name| return full_name }
316
+ end.compact.reverse.join('::')
317
+ result.empty? ? 'Object' : result
318
+ end
319
+
320
+ ## Predicates
321
+
322
+ def multiline?
323
+ source_range && (source_range.first_line != source_range.last_line)
324
+ end
325
+
326
+ def single_line?
327
+ !multiline?
328
+ end
329
+
330
+ def asgn_method_call?
331
+ !COMPARISON_OPERATORS.include?(method_name) &&
332
+ method_name.to_s.end_with?('='.freeze)
333
+ end
334
+
335
+ def_matcher :equals_asgn?, '{lvasgn ivasgn cvasgn gvasgn casgn masgn}'
336
+ def_matcher :shorthand_asgn?, '{op_asgn or_asgn and_asgn}'
337
+ def_matcher :assignment?,
338
+ '{equals_asgn? shorthand_asgn? asgn_method_call?}'
339
+
340
+ def literal?
341
+ LITERALS.include?(type)
342
+ end
343
+
344
+ def basic_literal?
345
+ BASIC_LITERALS.include?(type)
346
+ end
347
+
348
+ def truthy_literal?
349
+ TRUTHY_LITERALS.include?(type)
350
+ end
351
+
352
+ def falsey_literal?
353
+ FALSEY_LITERALS.include?(type)
354
+ end
355
+
356
+ def mutable_literal?
357
+ MUTABLE_LITERALS.include?(type)
358
+ end
359
+
360
+ def immutable_literal?
361
+ IMMUTABLE_LITERALS.include?(type)
362
+ end
363
+
364
+ [:literal, :basic_literal].each do |kind|
365
+ recursive_kind = :"recursive_#{kind}?"
366
+ kind_filter = :"#{kind}?"
367
+ define_method(recursive_kind) do
368
+ case type
369
+ when :send
370
+ receiver, method_name, *args = *self
371
+ COMPARISON_OPERATORS.include?(method_name) &&
372
+ receiver.send(recursive_kind) &&
373
+ args.all?(&recursive_kind)
374
+ when :begin, :pair, *OPERATOR_KEYWORDS, *COMPOSITE_LITERALS
375
+ children.all?(&recursive_kind)
376
+ else
377
+ send(kind_filter)
378
+ end
379
+ end
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 keyword_bang?
407
+ _receiver, method_name, *args = *self
408
+ args.empty? && method_name == :! && loc.selector.is?('!'.freeze)
409
+ end
410
+
411
+ def unary_operation?
412
+ return false unless loc.respond_to?(:selector) && loc.selector
413
+ Cop::Util.operator?(loc.selector.source.to_sym) &&
414
+ source_range.begin_pos == loc.selector.begin_pos
415
+ end
416
+
417
+ def binary_operation?
418
+ return false unless loc.respond_to?(:selector) && loc.selector
419
+ Cop::Util.operator?(method_name) &&
420
+ source_range.begin_pos != loc.selector.begin_pos
421
+ end
422
+
423
+ def chained?
424
+ return false unless argument?
425
+
426
+ receiver, _method_name, *_args = *parent
427
+ equal?(receiver)
428
+ end
429
+
430
+ def argument?
431
+ parent && parent.send_type?
432
+ end
433
+
434
+ def numeric_type?
435
+ int_type? || float_type?
436
+ end
437
+
438
+ def_matcher :guard_clause?, <<-PATTERN
439
+ [{(send nil {:raise :fail} ...) return break next} single_line?]
440
+ PATTERN
441
+
442
+ def_matcher :command?, '(send nil %1 ...)'
443
+ def_matcher :lambda?, '(block (send nil :lambda) ...)'
444
+ def_matcher :proc?, <<-PATTERN
445
+ {(block (send nil :proc) ...)
446
+ (block (send (const nil :Proc) :new) ...)
447
+ (send (const nil :Proc) :new)}
448
+ PATTERN
449
+ def_matcher :lambda_or_proc?, '{lambda? proc?}'
450
+
451
+ def_matcher :class_constructor?, <<-PATTERN
452
+ { (send (const nil {:Class :Module}) :new ...)
453
+ (block (send (const nil {:Class :Module}) :new ...) ...)}
454
+ PATTERN
455
+
456
+ def_matcher :module_definition?, <<-PATTERN
457
+ {class module (casgn _ _ class_constructor?)}
458
+ PATTERN
459
+
460
+ # Some expressions are evaluated for their value, some for their side
461
+ # effects, and some for both
462
+ # If we know that an expression is useful only for its side effects, that
463
+ # means we can transform it in ways which preserve the side effects, but
464
+ # change the return value
465
+ # So, does the return value of this node matter? If we changed it to
466
+ # `(...; nil)`, might that affect anything?
467
+ #
468
+ # rubocop:disable Metrics/MethodLength
469
+ def value_used?
470
+ # Be conservative and return true if we're not sure.
471
+ return false if parent.nil?
472
+
473
+ case parent.type
474
+ when :array, :defined?, :dstr, :dsym, :eflipflop, :erange, :float,
475
+ :hash, :iflipflop, :irange, :not, :pair, :regexp, :str, :sym,
476
+ :when, :xstr
477
+ parent.value_used?
478
+ when :begin, :kwbegin
479
+ begin_value_used?
480
+ when :for
481
+ for_value_used?
482
+ when :case, :if
483
+ case_if_value_used?
484
+ when :while, :until, :while_post, :until_post
485
+ while_until_value_used?
486
+ else
487
+ true
488
+ end
489
+ end
490
+ # rubocop:enable Metrics/MethodLength
491
+
492
+ # Some expressions are evaluated for their value, some for their side
493
+ # effects, and some for both.
494
+ # If we know that expressions are useful only for their return values,
495
+ # and have no side effects, that means we can reorder them, change the
496
+ # number of times they are evaluated, or replace them with other
497
+ # expressions which are equivalent in value.
498
+ # So, is evaluation of this node free of side effects?
499
+ #
500
+ def pure?
501
+ # Be conservative and return false if we're not sure
502
+ case type
503
+ when :__FILE__, :__LINE__, :const, :cvar, :defined?, :false, :float,
504
+ :gvar, :int, :ivar, :lvar, :nil, :str, :sym, :true, :regopt
505
+ true
506
+ when :and, :array, :begin, :case, :dstr, :dsym, :eflipflop, :ensure,
507
+ :erange, :for, :hash, :if, :iflipflop, :irange, :kwbegin, :not,
508
+ :or, :pair, :regexp, :until, :until_post, :when, :while,
509
+ :while_post
510
+ child_nodes.all?(&:pure?)
511
+ else
512
+ false
513
+ end
514
+ end
515
+
516
+ protected
517
+
518
+ def visit_descendants(types, &block)
519
+ each_child_node do |child|
520
+ yield child if types.empty? || types.include?(child.type)
521
+ child.visit_descendants(types, &block)
522
+ end
523
+ end
524
+
525
+ private
526
+
527
+ def visit_ancestors(types)
528
+ last_node = self
529
+
530
+ while (current_node = last_node.parent)
531
+ yield current_node if types.empty? ||
532
+ types.include?(current_node.type)
533
+ last_node = current_node
534
+ end
535
+ end
536
+
537
+ def begin_value_used?
538
+ # the last child node determines the value of the parent
539
+ sibling_index == parent.children.size - 1 ? parent.value_used? : false
540
+ end
541
+
542
+ def for_value_used?
543
+ # `for var in enum; body; end`
544
+ # (for <var> <enum> <body>)
545
+ sibling_index == 2 ? parent.value_used? : true
546
+ end
547
+
548
+ def case_if_value_used?
549
+ # (case <condition> <when...>)
550
+ # (if <condition> <truebranch> <falsebranch>)
551
+ sibling_index.zero? ? true : parent.value_used?
552
+ end
553
+
554
+ def while_until_value_used?
555
+ # (while <condition> <body>) -> always evaluates to `nil`
556
+ sibling_index.zero?
557
+ end
558
+
559
+ def parent_module_name_part(node)
560
+ case node.type
561
+ when :class, :module, :casgn
562
+ # TODO: if constant name has cbase (leading ::), then we don't need
563
+ # to keep traversing up through nested classes/modules
564
+ node.defined_module_name
565
+ when :sclass
566
+ yield parent_module_name_for_sclass(node)
567
+ else # block
568
+ parent_module_name_for_block(node) { yield nil }
569
+ end
570
+ end
571
+
572
+ def parent_module_name_for_sclass(sclass_node)
573
+ # TODO: look for constant definition and see if it is nested
574
+ # inside a class or module
575
+ subject = sclass_node.children[0]
576
+
577
+ if subject.const_type?
578
+ "#<Class:#{subject.const_name}>"
579
+ elsif subject.self_type?
580
+ "#<Class:#{sclass_node.parent_module_name}>"
581
+ end
582
+ end
583
+
584
+ def parent_module_name_for_block(ancestor)
585
+ if ancestor.method_name == :class_eval
586
+ # `class_eval` with no receiver applies to whatever module or class
587
+ # we are currently in
588
+ return unless (receiver = ancestor.receiver)
589
+ yield unless receiver.const_type?
590
+ receiver.const_name
591
+ elsif !new_class_or_module_block?(ancestor)
592
+ yield
593
+ end
594
+ end
595
+
596
+ def new_class_or_module_block?(block_node)
597
+ receiver = block_node.receiver
598
+
599
+ block_node.method_name == :new &&
600
+ receiver && receiver.const_type? &&
601
+ (receiver.const_name == 'Class' || receiver.const_name == 'Module') &&
602
+ block_node.parent &&
603
+ block_node.parent.casgn_type?
604
+ end
605
+ end
606
+ end
607
+ end