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
@@ -40,15 +40,17 @@ module RuboCop
40
40
  # foo&.bar(baz)
41
41
  # foo&.bar { |e| e.baz }
42
42
  class SafeNavigation < Cop
43
+ extend TargetRubyVersion
44
+
43
45
  MSG = 'Use safe navigation (`&.`) instead of `%s`.'.freeze
44
46
 
45
47
  def_node_matcher :try_call, <<-PATTERN
46
48
  (send !nil ${:try :try!} $_ ...)
47
49
  PATTERN
48
50
 
49
- def on_send(node)
50
- return if target_ruby_version < 2.3
51
+ minimum_target_ruby_version 2.3
51
52
 
53
+ def on_send(node)
52
54
  try_call(node) do |try_method, method_to_try|
53
55
  return if try_method == :try && !cop_config['ConvertTry']
54
56
  return unless method_to_try.sym_type?
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks for the use of methods which skip
7
+ # validations which are listed in
8
+ # http://guides.rubyonrails.org/active_record_validations.html#skipping-validations
9
+ #
10
+ # @example
11
+ # # bad
12
+ # Article.first.decrement!(:view_count)
13
+ # DiscussionBoard.decrement_counter(:post_count, 5)
14
+ # Article.first.increment!(:view_count)
15
+ # DiscussionBoard.increment_counter(:post_count, 5)
16
+ # person.toggle :active
17
+ # product.touch
18
+ # Billing.update_all("category = 'authorized', author = 'David'")
19
+ # user.update_attribute(website: 'example.com')
20
+ # user.update_columns(last_request_at: Time.current)
21
+ # Post.update_counters 5, comment_count: -1, action_count: 1
22
+ #
23
+ # # good
24
+ # user.update_attributes(website: 'example.com')
25
+ class SkipsModelValidations < Cop
26
+ MSG = 'Avoid using `%s` because it skips validations.'.freeze
27
+
28
+ def on_send(node)
29
+ _receiver, method_name = *node
30
+
31
+ return unless blacklist.include?(method_name.to_s)
32
+
33
+ add_offense(node,
34
+ node.loc.selector,
35
+ format(MSG, method_name))
36
+ end
37
+
38
+ private
39
+
40
+ def blacklist
41
+ cop_config['Blacklist']
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -109,7 +109,7 @@ module RuboCop
109
109
  # called is part of the time class.
110
110
  def method_from_time_class?(node)
111
111
  receiver, method_name, *_args = *node
112
- if (receiver.is_a? RuboCop::Node) && !receiver.cbase_type?
112
+ if (receiver.is_a? RuboCop::AST::Node) && !receiver.cbase_type?
113
113
  method_from_time_class?(receiver)
114
114
  else
115
115
  TIMECLASS.include? method_name
@@ -15,11 +15,11 @@ module RuboCop
15
15
  # # good
16
16
  # Model.uniq.pluck(:id)
17
17
  #
18
- # This cop has two different enforcement modes. When the EnforcedMode
18
+ # This cop has two different enforcement modes. When the EnforcedStyle
19
19
  # is conservative (the default) then only calls to pluck on a constant
20
20
  # (i.e. a model class) before uniq are added as offenses.
21
21
  #
22
- # When the EnforcedMode is aggressive then all calls to pluck before
22
+ # When the EnforcedStyle is aggressive then all calls to pluck before
23
23
  # uniq are added as offenses. This may lead to false positives as the cop
24
24
  # cannot distinguish between calls to pluck on an ActiveRecord::Relation
25
25
  # vs a call to pluck on an ActiveRecord::Associations::CollectionProxy.
@@ -35,6 +35,8 @@ module RuboCop
35
35
  # false positives.
36
36
  #
37
37
  class UniqBeforePluck < RuboCop::Cop::Cop
38
+ include ConfigurableEnforcedStyle
39
+
38
40
  MSG = 'Use `%s` before `pluck`.'.freeze
39
41
  NEWLINE = "\n".freeze
40
42
  PATTERN = '[!^block (send (send %s :pluck ...) ${:uniq :distinct} ...)]'
@@ -47,7 +49,7 @@ module RuboCop
47
49
  format(PATTERN, '_')
48
50
 
49
51
  def on_send(node)
50
- method = if mode == :conservative
52
+ method = if style == :conservative
51
53
  conservative_node_match(node)
52
54
  else
53
55
  aggressive_node_match(node)
@@ -66,8 +68,8 @@ module RuboCop
66
68
 
67
69
  private
68
70
 
69
- def mode
70
- @mode ||= cop_config['EnforcedMode'].to_sym
71
+ def style_parameter_name
72
+ 'EnforcedStyle'
71
73
  end
72
74
 
73
75
  def dot_method_with_whitespace(method, node)
@@ -0,0 +1,170 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Error raised when an unqualified cop name is used that could
6
+ # refer to two or more cops under different departments
7
+ class AmbiguousCopName < RuboCop::Error
8
+ MSG = 'Ambiguous cop name `%<name>s` used in %<origin>s needs ' \
9
+ 'department qualifier. Did you mean %<options>s?'.freeze
10
+
11
+ def initialize(name, origin, badges)
12
+ super(
13
+ format(
14
+ MSG,
15
+ name: name,
16
+ origin: origin,
17
+ options: badges.to_a.join(' or ')
18
+ )
19
+ )
20
+ end
21
+ end
22
+
23
+ # Registry that tracks all cops by their badge and department.
24
+ class Registry
25
+ def initialize(cops = [])
26
+ @registry = {}
27
+ @departments = {}
28
+
29
+ cops.each { |cop| enlist(cop) }
30
+ end
31
+
32
+ def enlist(cop)
33
+ @registry[cop.badge] = cop
34
+ @departments[cop.department] ||= []
35
+ @departments[cop.department] << cop
36
+ end
37
+
38
+ # @return [Array<Symbol>] list of departments for current cops.
39
+ def departments
40
+ @departments.keys
41
+ end
42
+
43
+ # @return [Registry] Cops for that specific department.
44
+ def with_department(department)
45
+ with(@departments.fetch(department, []))
46
+ end
47
+
48
+ # @return [Registry] Cops not for a specific department.
49
+ def without_department(department)
50
+ without_department = @departments.dup
51
+ without_department.delete(department)
52
+
53
+ with(without_department.values.flatten)
54
+ end
55
+
56
+ def contains_cop_matching?(names)
57
+ cops.any? { |cop| cop.match?(names) }
58
+ end
59
+
60
+ # Convert a user provided cop name into a properly namespaced name
61
+ #
62
+ # @example gives back a correctly qualified cop name
63
+ #
64
+ # cops = RuboCop::Cop::Cop.all
65
+ # cops.qualified_cop_name('Style/IndentArray') # => 'Style/IndentArray'
66
+ #
67
+ # @example fixes incorrect namespaces
68
+ #
69
+ # cops = RuboCop::Cop::Cop.all
70
+ # cops.qualified_cop_name('Lint/IndentArray') # => 'Style/IndentArray'
71
+ #
72
+ # @example namespaces bare cop identifiers
73
+ #
74
+ # cops = RuboCop::Cop::Cop.all
75
+ # cops.qualified_cop_name('IndentArray') # => 'IndentArray'
76
+ #
77
+ # @example passes back unrecognized cop names
78
+ #
79
+ # cops = RuboCop::Cop::Cop.all
80
+ # cops.qualified_cop_name('NotACop') # => 'NotACop'
81
+ #
82
+ # @param name [String] Cop name extracted from config
83
+ # @param path [String] Path of file that `name` was extracted from
84
+ #
85
+ # @raise [AmbiguousCopName]
86
+ # if a bare identifier with two possible namespaces is provided
87
+ #
88
+ # @note Emits a warning if the provided name has an incorrect namespace
89
+ #
90
+ # @return [String] Qualified cop name
91
+ def qualified_cop_name(name, path)
92
+ badge = Badge.parse(name)
93
+ return name if registered?(badge)
94
+
95
+ potential_badges = qualify_badge(badge)
96
+
97
+ case potential_badges.size
98
+ when 0 then name # No namespace found. Deal with it later in caller.
99
+ when 1 then resolve_badge(badge, potential_badges.first, path)
100
+ else raise AmbiguousCopName.new(badge, path, potential_badges)
101
+ end
102
+ end
103
+
104
+ def to_h
105
+ cops.group_by(&:cop_name)
106
+ end
107
+
108
+ def cops
109
+ @registry.values
110
+ end
111
+
112
+ def length
113
+ @registry.size
114
+ end
115
+
116
+ def enabled(config, only)
117
+ select do |cop|
118
+ config.cop_enabled?(cop) || only.include?(cop.cop_name)
119
+ end
120
+ end
121
+
122
+ def names
123
+ cops.map(&:cop_name)
124
+ end
125
+
126
+ def ==(other)
127
+ cops == other.cops
128
+ end
129
+
130
+ def sort!
131
+ @registry = Hash[@registry.sort_by { |badge, _| badge.cop_name }]
132
+
133
+ self
134
+ end
135
+
136
+ def select(&block)
137
+ cops.select(&block)
138
+ end
139
+
140
+ def each(&block)
141
+ cops.each(&block)
142
+ end
143
+
144
+ private
145
+
146
+ def with(cops)
147
+ self.class.new(cops)
148
+ end
149
+
150
+ def qualify_badge(badge)
151
+ @departments
152
+ .map { |department, _| badge.with_department(department) }
153
+ .select { |potential_badge| registered?(potential_badge) }
154
+ end
155
+
156
+ def resolve_badge(given_badge, real_badge, source_path)
157
+ unless given_badge.match?(real_badge)
158
+ warn "#{source_path}: #{given_badge} has the wrong namespace - " \
159
+ "should be #{real_badge.department}"
160
+ end
161
+
162
+ real_badge.to_s
163
+ end
164
+
165
+ def registered?(badge)
166
+ @registry.key?(badge)
167
+ end
168
+ end
169
+ end
170
+ end
@@ -2,8 +2,14 @@
2
2
 
3
3
  module RuboCop
4
4
  module Cop
5
- module Lint
5
+ module Security
6
6
  # This cop checks for the use of *Kernel#eval*.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ #
12
+ # eval(something)
7
13
  class Eval < Cop
8
14
  MSG = 'The use of `eval` is a serious security risk.'.freeze
9
15
 
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Security
6
+ # This cop checks for the use of Marshal class methods which have
7
+ # potential security issues leading to remote code execution when
8
+ # loading from an untrusted source.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # Marshal.load("{}")
13
+ # Marshal.restore("{}")
14
+ #
15
+ # # good
16
+ # Marshal.dump("{}")
17
+ #
18
+ class MarshalLoad < Cop
19
+ MSG = 'Avoid using `Marshal.%s`.'.freeze
20
+
21
+ def_node_matcher :marshal_load, <<-END
22
+ (send (const nil :Marshal) ${:load :restore} ...)
23
+ END
24
+
25
+ def on_send(node)
26
+ marshal_load(node) do |method|
27
+ add_offense(node, :selector, format(MSG, method))
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Security
6
+ # This cop checks for the use of YAML class methods which have
7
+ # potential security issues leading to remote code execution when
8
+ # loading from an untrusted source.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # YAML.load("--- foo")
13
+ #
14
+ # # good
15
+ # YAML.safe_load("--- foo")
16
+ # YAML.dump("foo")
17
+ #
18
+ class YAMLLoad < Cop
19
+ MSG = 'Prefer using `YAML.safe_load` over `YAML.load`.'.freeze
20
+
21
+ def_node_matcher :yaml_load, <<-END
22
+ (send (const nil :YAML) :load ...)
23
+ END
24
+
25
+ def on_send(node)
26
+ yaml_load(node) do |method|
27
+ add_offense(node, :selector, format(MSG, method))
28
+ end
29
+ end
30
+
31
+ def autocorrect(node)
32
+ ->(corrector) { corrector.replace(node.loc.selector, 'safe_load') }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -3,217 +3,186 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Here we check if the keys, separators, and values of a multi-line hash
7
- # literal are aligned.
6
+ # Check that the keys, separators, and values of a multi-line hash
7
+ # literal are aligned according to configuration. The configuration
8
+ # options are:
9
+ #
10
+ # - key (left align keys)
11
+ # - separator (align hash rockets and colons, right align keys)
12
+ # - table (left align keys, hash rockets, and values)
13
+ #
14
+ # The treatment of hashes passed as the last argument to a method call
15
+ # can also be configured. The options are:
16
+ #
17
+ # - always_inspect
18
+ # - always_ignore
19
+ # - ignore_implicit (without curly braces)
20
+ # - ignore_explicit (with curly braces)
21
+ #
22
+ # @example
23
+ #
24
+ # # EnforcedHashRocketStyle: key (default)
25
+ # # EnforcedColonStyle: key (default)
26
+ #
27
+ # # good
28
+ # {
29
+ # foo: bar,
30
+ # ba: baz
31
+ # }
32
+ # {
33
+ # :foo => bar,
34
+ # :ba => baz
35
+ # }
36
+ #
37
+ # # bad
38
+ # {
39
+ # foo: bar,
40
+ # ba: baz
41
+ # }
42
+ # {
43
+ # :foo => bar,
44
+ # :ba => baz
45
+ # }
46
+ #
47
+ # @example
48
+ #
49
+ # # EnforcedHashRocketStyle: separator
50
+ # # EnforcedColonStyle: separator
51
+ #
52
+ # #good
53
+ # {
54
+ # foo: bar,
55
+ # ba: baz
56
+ # }
57
+ # {
58
+ # :foo => bar,
59
+ # :ba => baz
60
+ # }
61
+ #
62
+ # #bad
63
+ # {
64
+ # foo: bar,
65
+ # ba: baz
66
+ # }
67
+ # {
68
+ # :foo => bar,
69
+ # :ba => baz
70
+ # }
71
+ # {
72
+ # :foo => bar,
73
+ # :ba => baz
74
+ # }
75
+ #
76
+ # @example
77
+ #
78
+ # # EnforcedHashRocketStyle: table
79
+ # # EnforcedColonStyle: table
80
+ #
81
+ # #good
82
+ # {
83
+ # foo: bar,
84
+ # ba: baz
85
+ # }
86
+ # {
87
+ # :foo => bar,
88
+ # :ba => baz
89
+ # }
90
+ #
91
+ # #bad
92
+ # {
93
+ # foo: bar,
94
+ # ba: baz
95
+ # }
96
+ # {
97
+ # :foo => bar,
98
+ # :ba => baz
99
+ # }
8
100
  class AlignHash < Cop
9
- # Handles calculation of deltas (deviations from correct alignment)
10
- # when the enforced style is 'key'.
11
- class KeyAlignment
12
- def checkable_layout(_node)
13
- true
14
- end
15
-
16
- def deltas_for_first_pair(*)
17
- {} # The first pair is always considered correct.
18
- end
19
-
20
- def deltas(first_pair, current_pair)
21
- if Util.begins_its_line?(current_pair.source_range)
22
- { key: first_pair.loc.column - current_pair.loc.column }
23
- else
24
- {}
25
- end
26
- end
27
- end
28
-
29
- # Common functionality for the styles where not only keys, but also
30
- # values are aligned.
31
- class AlignmentOfValues
32
- include HashNode # any_pairs_on_the_same_line?
33
-
34
- def checkable_layout(node)
35
- !any_pairs_on_the_same_line?(node) && all_have_same_separator?(node)
36
- end
37
-
38
- def deltas(first_pair, current_pair)
39
- key_delta = key_delta(first_pair, current_pair)
40
- current_separator = current_pair.loc.operator
41
- separator_delta = separator_delta(first_pair, current_separator,
42
- key_delta)
43
- value_delta = value_delta(first_pair, current_pair) -
44
- key_delta - separator_delta
45
-
46
- { key: key_delta, separator: separator_delta, value: value_delta }
47
- end
48
-
49
- private
50
-
51
- def separator_delta(first_pair, current_separator, key_delta)
52
- if current_separator.is?(':')
53
- 0 # Colon follows directly after key
54
- else
55
- hash_rocket_delta(first_pair, current_separator) - key_delta
56
- end
57
- end
58
-
59
- def all_have_same_separator?(node)
60
- first_separator = node.children.first.loc.operator.source
61
- node.children.butfirst.all? do |pair|
62
- pair.loc.operator.is?(first_separator)
63
- end
64
- end
65
- end
66
-
67
- # Handles calculation of deltas when the enforced style is 'table'.
68
- class TableAlignment < AlignmentOfValues
69
- # The table style is the only one where the first key-value pair can
70
- # be considered to have bad alignment.
71
- def deltas_for_first_pair(first_pair, node)
72
- key_widths = node.children.map do |pair|
73
- key, _value = *pair
74
- key.source.length
75
- end
76
- @max_key_width = key_widths.max
77
-
78
- separator_delta = separator_delta(first_pair,
79
- first_pair.loc.operator, 0)
80
- {
81
- separator: separator_delta,
82
- value: value_delta(first_pair, first_pair) - separator_delta
83
- }
84
- end
85
-
86
- private
87
-
88
- def key_delta(first_pair, current_pair)
89
- first_pair.loc.column - current_pair.loc.column
90
- end
91
-
92
- def hash_rocket_delta(first_pair, current_separator)
93
- first_pair.loc.column + @max_key_width + 1 -
94
- current_separator.column
95
- end
96
-
97
- def value_delta(first_pair, current_pair)
98
- first_key, = *first_pair
99
- _, current_value = *current_pair
100
- correct_value_column = first_key.loc.column +
101
- spaced_separator(current_pair).length +
102
- @max_key_width
103
- correct_value_column - current_value.loc.column
104
- end
105
-
106
- def spaced_separator(node)
107
- node.loc.operator.is?('=>') ? ' => ' : ': '
108
- end
109
- end
110
-
111
- # Handles calculation of deltas when the enforced style is 'separator'.
112
- class SeparatorAlignment < AlignmentOfValues
113
- def deltas_for_first_pair(*)
114
- {} # The first pair is always considered correct.
115
- end
116
-
117
- private
118
-
119
- def key_delta(first_pair, current_pair)
120
- key_end_column(first_pair) - key_end_column(current_pair)
121
- end
122
-
123
- def key_end_column(pair)
124
- key, _value = *pair
125
- key.loc.column + key.source.length
126
- end
127
-
128
- def hash_rocket_delta(first_pair, current_separator)
129
- first_pair.loc.operator.column - current_separator.column
130
- end
131
-
132
- def value_delta(first_pair, current_pair)
133
- _, first_value = *first_pair
134
- _, current_value = *current_pair
135
- first_value.loc.column - current_value.loc.column
136
- end
137
- end
101
+ include HashAlignment
138
102
 
139
103
  MSG = 'Align the elements of a hash literal if they span more than ' \
140
104
  'one line.'.freeze
141
105
 
142
106
  def on_send(node)
143
- return unless (last_child = node.children.last) &&
144
- hash?(last_child) &&
145
- ignore_last_argument_hash?(last_child)
107
+ return if double_splat?(node)
108
+
109
+ last_argument = node.children.last
110
+
111
+ return unless last_argument.hash_type? &&
112
+ ignore_hash_argument?(last_argument)
146
113
 
147
- ignore_node(last_child)
114
+ ignore_node(last_argument)
148
115
  end
149
116
 
150
117
  def on_hash(node)
151
118
  return if ignored_node?(node)
152
- return if node.children.empty?
153
- return unless node.multiline?
119
+ return if node.pairs.empty? || node.single_line?
154
120
 
155
- @alignment_for_hash_rockets ||=
156
- new_alignment('EnforcedHashRocketStyle')
157
- @alignment_for_colons ||= new_alignment('EnforcedColonStyle')
158
-
159
- unless @alignment_for_hash_rockets.checkable_layout(node) &&
160
- @alignment_for_colons.checkable_layout(node)
161
- return
162
- end
121
+ return unless alignment_for_hash_rockets.checkable_layout?(node) &&
122
+ alignment_for_colons.checkable_layout?(node)
163
123
 
164
124
  check_pairs(node)
165
125
  end
166
126
 
167
127
  private
168
128
 
129
+ attr_accessor :column_deltas
130
+
131
+ def double_splat?(node)
132
+ node.children.last.is_a?(Symbol)
133
+ end
134
+
169
135
  def check_pairs(node)
170
- first_pair = node.children.first
171
- @column_deltas = alignment_for(first_pair)
172
- .deltas_for_first_pair(first_pair, node)
136
+ first_pair = node.pairs.first
137
+ self.column_deltas = alignment_for(first_pair)
138
+ .deltas_for_first_pair(first_pair, node)
173
139
  add_offense(first_pair, :expression) unless good_alignment?
174
140
 
175
141
  node.children.each do |current|
176
- @column_deltas = alignment_for(current).deltas(first_pair, current)
142
+ self.column_deltas = alignment_for(current)
143
+ .deltas(first_pair, current)
177
144
  add_offense(current, :expression) unless good_alignment?
178
145
  end
179
146
  end
180
147
 
181
- def ignore_last_argument_hash?(node)
148
+ def ignore_hash_argument?(node)
182
149
  case cop_config['EnforcedLastArgumentHashStyle']
183
150
  when 'always_inspect' then false
184
151
  when 'always_ignore' then true
185
- when 'ignore_explicit' then explicit_hash?(node)
186
- when 'ignore_implicit' then !explicit_hash?(node)
152
+ when 'ignore_explicit' then node.braces?
153
+ when 'ignore_implicit' then !node.braces?
187
154
  end
188
155
  end
189
156
 
190
- def hash?(node)
191
- node.respond_to?(:type) && node.hash_type?
157
+ def alignment_for(pair)
158
+ if pair.hash_rocket?
159
+ alignment_for_hash_rockets
160
+ else
161
+ alignment_for_colons
162
+ end
192
163
  end
193
164
 
194
- def explicit_hash?(node)
195
- node.loc.begin
165
+ def alignment_for_hash_rockets
166
+ @alignment_for_hash_rockets ||=
167
+ new_alignment('EnforcedHashRocketStyle')
196
168
  end
197
169
 
198
- def alignment_for(pair)
199
- if pair.loc.operator.is?('=>')
200
- @alignment_for_hash_rockets
201
- else
202
- @alignment_for_colons
203
- end
170
+ def alignment_for_colons
171
+ @alignment_for_colons ||=
172
+ new_alignment('EnforcedColonStyle')
204
173
  end
205
174
 
206
175
  def autocorrect(node)
207
176
  # We can't use the instance variable inside the lambda. That would
208
177
  # just give each lambda the same reference and they would all get the
209
178
  # last value of each. A local variable fixes the problem.
210
- key_delta = @column_deltas[:key] || 0
211
- key, value = *node
179
+ key_delta = column_deltas[:key] || 0
212
180
 
213
- if value.nil?
181
+ if !node.value
214
182
  correct_no_value(key_delta, node.source_range)
215
183
  else
216
- correct_key_value(key_delta, key.source_range, value.source_range,
184
+ correct_key_value(key_delta, node.key.source_range,
185
+ node.value.source_range,
217
186
  node.loc.operator)
218
187
  end
219
188
  end
@@ -226,8 +195,8 @@ module RuboCop
226
195
  # We can't use the instance variable inside the lambda. That would
227
196
  # just give each lambda the same reference and they would all get the
228
197
  # last value of each. Some local variables fix the problem.
229
- separator_delta = @column_deltas[:separator] || 0
230
- value_delta = @column_deltas[:value] || 0
198
+ separator_delta = column_deltas[:separator] || 0
199
+ value_delta = column_deltas[:value] || 0
231
200
 
232
201
  key_column = key.column
233
202
  key_delta = -key_column if key_delta < -key_column
@@ -258,7 +227,7 @@ module RuboCop
258
227
  end
259
228
 
260
229
  def good_alignment?
261
- @column_deltas.values.compact.all?(&:zero?)
230
+ column_deltas.values.all?(&:zero?)
262
231
  end
263
232
  end
264
233
  end