rubocop 0.48.1 → 0.49.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (184) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -3
  3. data/config/default.yml +397 -357
  4. data/config/disabled.yml +29 -29
  5. data/config/enabled.yml +366 -326
  6. data/lib/rubocop.rb +85 -70
  7. data/lib/rubocop/ast/builder.rb +4 -1
  8. data/lib/rubocop/ast/node.rb +2 -2
  9. data/lib/rubocop/ast/node/and_node.rb +1 -1
  10. data/lib/rubocop/ast/node/args_node.rb +24 -0
  11. data/lib/rubocop/ast/node/block_node.rb +107 -0
  12. data/lib/rubocop/ast/node/case_node.rb +1 -1
  13. data/lib/rubocop/ast/node/ensure_node.rb +1 -1
  14. data/lib/rubocop/ast/node/for_node.rb +1 -1
  15. data/lib/rubocop/ast/node/if_node.rb +1 -1
  16. data/lib/rubocop/ast/node/mixin/parameterized_node.rb +74 -0
  17. data/lib/rubocop/ast/node/or_node.rb +1 -1
  18. data/lib/rubocop/ast/node/pair_node.rb +1 -1
  19. data/lib/rubocop/ast/node/resbody_node.rb +1 -1
  20. data/lib/rubocop/ast/node/send_node.rb +36 -57
  21. data/lib/rubocop/ast/node/super_node.rb +42 -0
  22. data/lib/rubocop/ast/node/until_node.rb +1 -1
  23. data/lib/rubocop/ast/node/when_node.rb +1 -1
  24. data/lib/rubocop/ast/node/while_node.rb +1 -1
  25. data/lib/rubocop/cli.rb +10 -0
  26. data/lib/rubocop/config.rb +23 -7
  27. data/lib/rubocop/config_loader.rb +19 -3
  28. data/lib/rubocop/cop/badge.rb +1 -1
  29. data/lib/rubocop/cop/bundler/duplicated_gem.rb +2 -2
  30. data/lib/rubocop/cop/commissioner.rb +1 -1
  31. data/lib/rubocop/cop/cop.rb +10 -0
  32. data/lib/rubocop/cop/{style → layout}/access_modifier_indentation.rb +33 -3
  33. data/lib/rubocop/cop/{style → layout}/align_array.rb +16 -1
  34. data/lib/rubocop/cop/{style → layout}/align_hash.rb +1 -1
  35. data/lib/rubocop/cop/{style → layout}/align_parameters.rb +29 -1
  36. data/lib/rubocop/cop/{style → layout}/block_end_newline.rb +10 -5
  37. data/lib/rubocop/cop/{style → layout}/case_indentation.rb +64 -1
  38. data/lib/rubocop/cop/{style → layout}/closing_parenthesis_indentation.rb +2 -2
  39. data/lib/rubocop/cop/{style → layout}/comment_indentation.rb +1 -1
  40. data/lib/rubocop/cop/{style → layout}/dot_position.rb +1 -1
  41. data/lib/rubocop/cop/{style → layout}/else_alignment.rb +1 -1
  42. data/lib/rubocop/cop/{style → layout}/empty_line_after_magic_comment.rb +1 -1
  43. data/lib/rubocop/cop/{style → layout}/empty_line_between_defs.rb +1 -1
  44. data/lib/rubocop/cop/{style → layout}/empty_lines.rb +1 -1
  45. data/lib/rubocop/cop/{style → layout}/empty_lines_around_access_modifier.rb +2 -7
  46. data/lib/rubocop/cop/{style → layout}/empty_lines_around_begin_body.rb +1 -1
  47. data/lib/rubocop/cop/{style → layout}/empty_lines_around_block_body.rb +2 -4
  48. data/lib/rubocop/cop/{style → layout}/empty_lines_around_class_body.rb +1 -1
  49. data/lib/rubocop/cop/{style → layout}/empty_lines_around_exception_handling_keywords.rb +1 -1
  50. data/lib/rubocop/cop/{style → layout}/empty_lines_around_method_body.rb +1 -1
  51. data/lib/rubocop/cop/{style → layout}/empty_lines_around_module_body.rb +1 -1
  52. data/lib/rubocop/cop/{style → layout}/end_of_line.rb +1 -1
  53. data/lib/rubocop/cop/{style → layout}/extra_spacing.rb +1 -1
  54. data/lib/rubocop/cop/{style → layout}/first_array_element_line_break.rb +1 -1
  55. data/lib/rubocop/cop/{style → layout}/first_hash_element_line_break.rb +1 -1
  56. data/lib/rubocop/cop/{style → layout}/first_method_argument_line_break.rb +1 -1
  57. data/lib/rubocop/cop/{style → layout}/first_method_parameter_line_break.rb +1 -1
  58. data/lib/rubocop/cop/{style → layout}/first_parameter_indentation.rb +1 -1
  59. data/lib/rubocop/cop/{style → layout}/indent_array.rb +1 -1
  60. data/lib/rubocop/cop/{style → layout}/indent_assignment.rb +1 -1
  61. data/lib/rubocop/cop/{style → layout}/indent_hash.rb +2 -2
  62. data/lib/rubocop/cop/{style → layout}/indent_heredoc.rb +3 -3
  63. data/lib/rubocop/cop/{style → layout}/indentation_consistency.rb +1 -1
  64. data/lib/rubocop/cop/{style → layout}/indentation_width.rb +10 -12
  65. data/lib/rubocop/cop/{style → layout}/initial_indentation.rb +1 -1
  66. data/lib/rubocop/cop/{style → layout}/leading_comment_space.rb +1 -1
  67. data/lib/rubocop/cop/{style → layout}/multiline_array_brace_layout.rb +1 -1
  68. data/lib/rubocop/cop/{style → layout}/multiline_assignment_layout.rb +1 -1
  69. data/lib/rubocop/cop/{style → layout}/multiline_block_layout.rb +21 -36
  70. data/lib/rubocop/cop/{style → layout}/multiline_hash_brace_layout.rb +5 -1
  71. data/lib/rubocop/cop/{style → layout}/multiline_method_call_brace_layout.rb +1 -1
  72. data/lib/rubocop/cop/{style → layout}/multiline_method_call_indentation.rb +3 -3
  73. data/lib/rubocop/cop/{style → layout}/multiline_method_definition_brace_layout.rb +1 -1
  74. data/lib/rubocop/cop/{style → layout}/multiline_operation_indentation.rb +6 -5
  75. data/lib/rubocop/cop/{style → layout}/rescue_ensure_alignment.rb +1 -1
  76. data/lib/rubocop/cop/{style → layout}/space_after_colon.rb +2 -2
  77. data/lib/rubocop/cop/{style → layout}/space_after_comma.rb +2 -2
  78. data/lib/rubocop/cop/{style → layout}/space_after_method_name.rb +1 -1
  79. data/lib/rubocop/cop/{style → layout}/space_after_not.rb +1 -1
  80. data/lib/rubocop/cop/{style → layout}/space_after_semicolon.rb +2 -2
  81. data/lib/rubocop/cop/{style → layout}/space_around_block_parameters.rb +7 -5
  82. data/lib/rubocop/cop/{style → layout}/space_around_equals_in_parameter_default.rb +1 -1
  83. data/lib/rubocop/cop/{style → layout}/space_around_keyword.rb +1 -1
  84. data/lib/rubocop/cop/{style → layout}/space_around_operators.rb +6 -2
  85. data/lib/rubocop/cop/{style → layout}/space_before_block_braces.rb +6 -2
  86. data/lib/rubocop/cop/{style → layout}/space_before_comma.rb +1 -1
  87. data/lib/rubocop/cop/{style → layout}/space_before_comment.rb +1 -1
  88. data/lib/rubocop/cop/{style → layout}/space_before_first_arg.rb +4 -2
  89. data/lib/rubocop/cop/{style → layout}/space_before_semicolon.rb +1 -1
  90. data/lib/rubocop/cop/{style → layout}/space_in_lambda_literal.rb +1 -1
  91. data/lib/rubocop/cop/{style → layout}/space_inside_array_percent_literal.rb +1 -1
  92. data/lib/rubocop/cop/{style → layout}/space_inside_block_braces.rb +3 -4
  93. data/lib/rubocop/cop/{style → layout}/space_inside_brackets.rb +1 -1
  94. data/lib/rubocop/cop/{style → layout}/space_inside_hash_literal_braces.rb +1 -1
  95. data/lib/rubocop/cop/{style → layout}/space_inside_parens.rb +1 -1
  96. data/lib/rubocop/cop/{style → layout}/space_inside_percent_literal_delimiters.rb +8 -7
  97. data/lib/rubocop/cop/{style → layout}/space_inside_range_literal.rb +1 -1
  98. data/lib/rubocop/cop/{style → layout}/space_inside_string_interpolation.rb +1 -1
  99. data/lib/rubocop/cop/{style → layout}/tab.rb +1 -1
  100. data/lib/rubocop/cop/{style → layout}/trailing_blank_lines.rb +1 -1
  101. data/lib/rubocop/cop/{style → layout}/trailing_whitespace.rb +2 -2
  102. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  103. data/lib/rubocop/cop/lint/ambiguous_operator.rb +4 -4
  104. data/lib/rubocop/cop/lint/debugger.rb +0 -15
  105. data/lib/rubocop/cop/lint/duplicate_methods.rb +2 -1
  106. data/lib/rubocop/cop/lint/rescue_type.rb +81 -0
  107. data/lib/rubocop/cop/lint/script_permission.rb +42 -0
  108. data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -1
  109. data/lib/rubocop/cop/message_annotator.rb +23 -13
  110. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  111. data/lib/rubocop/cop/mixin/array_min_size.rb +59 -0
  112. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +10 -11
  113. data/lib/rubocop/cop/mixin/def_node.rb +1 -1
  114. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +1 -1
  115. data/lib/rubocop/cop/mixin/enforce_superclass.rb +36 -0
  116. data/lib/rubocop/cop/mixin/hash_alignment.rb +1 -1
  117. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +7 -3
  118. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  119. data/lib/rubocop/cop/performance/caller.rb +41 -0
  120. data/lib/rubocop/cop/performance/compare_with_block.rb +60 -14
  121. data/lib/rubocop/cop/performance/double_start_end_with.rb +2 -2
  122. data/lib/rubocop/cop/performance/redundant_merge.rb +2 -0
  123. data/lib/rubocop/cop/rails/action_filter.rb +1 -3
  124. data/lib/rubocop/cop/rails/application_job.rb +32 -0
  125. data/lib/rubocop/cop/rails/application_record.rb +32 -0
  126. data/lib/rubocop/cop/rails/blank.rb +9 -3
  127. data/lib/rubocop/cop/rails/output_safety.rb +59 -15
  128. data/lib/rubocop/cop/rails/present.rb +9 -3
  129. data/lib/rubocop/cop/rails/relative_date_constant.rb +35 -4
  130. data/lib/rubocop/cop/rails/reversible_migration.rb +82 -18
  131. data/lib/rubocop/cop/rails/save_bang.rb +7 -2
  132. data/lib/rubocop/cop/rails/skips_model_validations.rb +7 -0
  133. data/lib/rubocop/cop/registry.rb +4 -3
  134. data/lib/rubocop/cop/security/eval.rb +9 -3
  135. data/lib/rubocop/cop/style/and_or.rb +1 -1
  136. data/lib/rubocop/cop/style/block_delimiters.rb +11 -17
  137. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +1 -1
  138. data/lib/rubocop/cop/style/collection_methods.rb +1 -3
  139. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
  140. data/lib/rubocop/cop/style/copyright.rb +2 -2
  141. data/lib/rubocop/cop/style/documentation_method.rb +1 -1
  142. data/lib/rubocop/cop/style/each_for_simple_loop.rb +2 -1
  143. data/lib/rubocop/cop/style/each_with_object.rb +10 -6
  144. data/lib/rubocop/cop/style/empty_case_condition.rb +2 -2
  145. data/lib/rubocop/cop/style/for.rb +4 -5
  146. data/lib/rubocop/cop/style/format_string.rb +49 -0
  147. data/lib/rubocop/cop/style/format_string_token.rb +141 -0
  148. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  149. data/lib/rubocop/cop/style/identical_conditional_branches.rb +2 -2
  150. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +1 -1
  151. data/lib/rubocop/cop/style/inverse_methods.rb +10 -1
  152. data/lib/rubocop/cop/style/lambda.rb +9 -9
  153. data/lib/rubocop/cop/style/line_end_concatenation.rb +4 -0
  154. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +3 -3
  155. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +1 -2
  156. data/lib/rubocop/cop/style/method_name.rb +8 -2
  157. data/lib/rubocop/cop/style/mixin_grouping.rb +41 -3
  158. data/lib/rubocop/cop/style/multiline_block_chain.rb +7 -11
  159. data/lib/rubocop/cop/style/multiple_comparison.rb +77 -0
  160. data/lib/rubocop/cop/style/next.rb +11 -22
  161. data/lib/rubocop/cop/style/parallel_assignment.rb +10 -19
  162. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -2
  163. data/lib/rubocop/cop/style/self_assignment.rb +4 -0
  164. data/lib/rubocop/cop/style/single_line_block_params.rb +23 -17
  165. data/lib/rubocop/cop/style/symbol_array.rb +24 -13
  166. data/lib/rubocop/cop/style/symbol_proc.rb +4 -0
  167. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  168. data/lib/rubocop/cop/style/unneeded_interpolation.rb +4 -0
  169. data/lib/rubocop/cop/style/word_array.rb +33 -53
  170. data/lib/rubocop/cop/style/yoda_condition.rb +78 -0
  171. data/lib/rubocop/cop/team.rb +1 -14
  172. data/lib/rubocop/cop/util.rb +16 -0
  173. data/lib/rubocop/formatter/simple_text_formatter.rb +0 -11
  174. data/lib/rubocop/node_pattern.rb +52 -52
  175. data/lib/rubocop/options.rb +25 -0
  176. data/lib/rubocop/path_util.rb +17 -1
  177. data/lib/rubocop/result_cache.rb +8 -7
  178. data/lib/rubocop/rspec/expect_offense.rb +167 -0
  179. data/lib/rubocop/rspec/shared_examples.rb +0 -8
  180. data/lib/rubocop/rspec/support.rb +1 -0
  181. data/lib/rubocop/runner.rb +12 -2
  182. data/lib/rubocop/target_finder.rb +5 -0
  183. data/lib/rubocop/version.rb +1 -1
  184. metadata +101 -72
@@ -12,6 +12,7 @@ module RuboCop
12
12
  # array.sort { |a, b| a.foo <=> b.foo }
13
13
  # array.max { |a, b| a.foo <=> b.foo }
14
14
  # array.min { |a, b| a.foo <=> b.foo }
15
+ # array.sort { |a, b| a[:foo] <=> b[:foo] }
15
16
  #
16
17
  # @good
17
18
  # array.sort_by(&:foo)
@@ -21,37 +22,82 @@ module RuboCop
21
22
  # end
22
23
  # array.max_by(&:foo)
23
24
  # array.min_by(&:foo)
25
+ # array.sort_by { |a| a[:foo] }
24
26
  class CompareWithBlock < Cop
25
- MSG = 'Use `%s_by(&:%s)` instead of ' \
26
- '`%s { |%s, %s| %s.%s <=> %s.%s }`.'.freeze
27
+ MSG = 'Use `%s_by%s` instead of ' \
28
+ '`%s { |%s, %s| %s <=> %s }`.'.freeze
27
29
 
28
30
  def_node_matcher :compare?, <<-END
29
- (block $(send _ {:sort :min :max}) (args (arg $_a) (arg $_b)) (send (send (lvar _a) $_m) :<=> (send (lvar _b) $_m)))
31
+ (block
32
+ $(send _ {:sort :min :max})
33
+ (args (arg $_a) (arg $_b))
34
+ $send)
35
+ END
36
+
37
+ def_node_matcher :replaceable_body?, <<-END
38
+ (send
39
+ (send (lvar %1) $_method $...)
40
+ :<=>
41
+ (send (lvar %2) _method $...))
30
42
  END
31
43
 
32
44
  def on_block(node)
33
- compare?(node) do |send, var_a, var_b, method|
34
- range = compare_range(send, node)
35
- compare_method = send.method_name
36
- add_offense(node, range,
37
- format(MSG, compare_method, method,
38
- compare_method, var_a, var_b,
39
- var_a, method, var_b, method))
45
+ compare?(node) do |send, var_a, var_b, body|
46
+ replaceable_body?(body, var_a, var_b) do |method, args_a, args_b|
47
+ return unless slow_compare?(method, args_a, args_b)
48
+ range = compare_range(send, node)
49
+ add_offense(node, range,
50
+ message(send, method, var_a, var_b, args_a))
51
+ end
40
52
  end
41
53
  end
42
54
 
43
55
  def autocorrect(node)
44
- send, = *node
45
-
46
56
  lambda do |corrector|
47
- method = node.children.last.children.last.children.last
57
+ send, var_a, var_b, body = compare?(node)
58
+ method, arg, = replaceable_body?(body, var_a, var_b)
59
+ replacement =
60
+ if method == :[]
61
+ "#{send.method_name}_by { |a| a[#{arg.first.source}] }"
62
+ else
63
+ "#{send.method_name}_by(&:#{method})"
64
+ end
48
65
  corrector.replace(compare_range(send, node),
49
- "#{send.method_name}_by(&:#{method})")
66
+ replacement)
50
67
  end
51
68
  end
52
69
 
53
70
  private
54
71
 
72
+ def slow_compare?(method, args_a, args_b)
73
+ return false unless args_a == args_b
74
+ if method == :[]
75
+ return false unless args_a.size == 1
76
+ key = args_a.first
77
+ return false unless %i[sym str int].include?(key.type)
78
+ else
79
+ return false unless args_a.empty?
80
+ end
81
+ true
82
+ end
83
+
84
+ def message(send, method, var_a, var_b, args)
85
+ compare_method = send.method_name
86
+ if method == :[]
87
+ key = args.first
88
+ instead = " { |a| a[#{key.source}] }"
89
+ str_a = "#{var_a}[#{key.source}]"
90
+ str_b = "#{var_b}[#{key.source}]"
91
+ else
92
+ instead = "(&:#{method})"
93
+ str_a = "#{var_a}.#{method}"
94
+ str_b = "#{var_b}.#{method}"
95
+ end
96
+ format(MSG, compare_method, instead,
97
+ compare_method, var_a, var_b,
98
+ str_a, str_b)
99
+ end
100
+
55
101
  def compare_range(send, node)
56
102
  range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
57
103
  end
@@ -23,8 +23,8 @@ module RuboCop
23
23
  # var2 = ...
24
24
  # str.end_with?(var1, var2)
25
25
  class DoubleStartEndWith < Cop
26
- MSG = 'Use `%{receiver}.%{method}(%{combined_args})` ' \
27
- 'instead of `%{original_code}`.'.freeze
26
+ MSG = 'Use `%<receiver>s.%<method>s(%<combined_args>s)` ' \
27
+ 'instead of `%<original_code>s`.'.freeze
28
28
 
29
29
  def on_or(node)
30
30
  receiver,
@@ -21,6 +21,8 @@ module RuboCop
21
21
 
22
22
  def on_send(node)
23
23
  each_redundant_merge(node) do |receiver, pairs|
24
+ return if pairs.any?(&:kwsplat_type?)
25
+
24
26
  assignments = to_assignments(receiver, pairs).join('; ')
25
27
  message = format(MSG, assignments, node.source)
26
28
  add_offense(node, :expression, message)
@@ -51,9 +51,7 @@ module RuboCop
51
51
  minimum_target_rails_version 4.0
52
52
 
53
53
  def on_block(node)
54
- method, _args, _body = *node
55
-
56
- check_method_node(method)
54
+ check_method_node(node.send_node)
57
55
  end
58
56
 
59
57
  def on_send(node)
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks that jobs subclass ApplicationJob with Rails 5.0.
7
+ #
8
+ # @example
9
+ #
10
+ # # good
11
+ # class Rails5Job < ApplicationJob
12
+ # ...
13
+ # end
14
+ #
15
+ # # bad
16
+ # class Rails4Job < ActiveJob::Base
17
+ # ...
18
+ # end
19
+ class ApplicationJob < Cop
20
+ extend TargetRailsVersion
21
+
22
+ minimum_target_rails_version 5.0
23
+
24
+ MSG = 'Jobs should subclass `ApplicationJob`.'.freeze
25
+ SUPERCLASS = 'ApplicationJob'.freeze
26
+ BASE_PATTERN = '(const (const nil :ActiveJob) :Base)'.freeze
27
+
28
+ include RuboCop::Cop::EnforceSuperclass
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks that models subclass ApplicationRecord with Rails 5.0.
7
+ #
8
+ # @example
9
+ #
10
+ # # good
11
+ # class Rails5Model < ApplicationRecord
12
+ # ...
13
+ # end
14
+ #
15
+ # # bad
16
+ # class Rails4Model < ActiveRecord::Base
17
+ # ...
18
+ # end
19
+ class ApplicationRecord < Cop
20
+ extend TargetRailsVersion
21
+
22
+ minimum_target_rails_version 5.0
23
+
24
+ MSG = 'Models should subclass `ApplicationRecord`.'.freeze
25
+ SUPERCLASS = 'ApplicationRecord'.freeze
26
+ BASE_PATTERN = '(const (const nil :ActiveRecord) :Base)'.freeze
27
+
28
+ include RuboCop::Cop::EnforceSuperclass
29
+ end
30
+ end
31
+ end
32
+ end
@@ -39,7 +39,7 @@ module RuboCop
39
39
  # end
40
40
  class Blank < Cop
41
41
  MSG_NIL_OR_EMPTY = 'Use `%s.blank?` instead of `%s`.'.freeze
42
- MSG_NOT_PRESENT = 'Use `%s.blank?` instead of `%s`.'.freeze
42
+ MSG_NOT_PRESENT = 'Use `%s` instead of `%s`.'.freeze
43
43
  MSG_UNLESS_PRESENT = 'Use `if %s.blank?` instead of `%s`.'.freeze
44
44
 
45
45
  def_node_matcher :nil_or_empty?, <<-PATTERN
@@ -69,7 +69,9 @@ module RuboCop
69
69
  not_present?(node) do |receiver|
70
70
  add_offense(node,
71
71
  :expression,
72
- format(MSG_NOT_PRESENT, receiver.source, node.source))
72
+ format(MSG_NOT_PRESENT,
73
+ replacement(receiver),
74
+ node.source))
73
75
  end
74
76
  end
75
77
 
@@ -113,7 +115,7 @@ module RuboCop
113
115
  range = node.loc.expression
114
116
  end
115
117
 
116
- corrector.replace(range, "#{variable1.source}.blank?")
118
+ corrector.replace(range, replacement(variable1))
117
119
  end
118
120
  end
119
121
 
@@ -126,6 +128,10 @@ module RuboCop
126
128
  node.loc.expression.begin.join(method_call.loc.expression)
127
129
  end
128
130
  end
131
+
132
+ def replacement(node)
133
+ node.respond_to?(:source) ? "#{node.source}.blank?" : 'blank?'
134
+ end
129
135
  end
130
136
  end
131
137
  end
@@ -3,38 +3,78 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop checks for the use of output safety calls like html_safe and
7
- # raw.
6
+ # This cop checks for the use of output safety calls like html_safe,
7
+ # raw, and safe_concat. These methods do not escape content. They
8
+ # simply return a SafeBuffer containing the content as is. Instead,
9
+ # use safe_join to join content and escape it and concat to
10
+ # concatenate content and escape it, ensuring its safety.
8
11
  #
9
12
  # @example
13
+ # user_content = "<b>hi</b>"
14
+ #
10
15
  # # bad
11
- # "<p>#{text}</p>".html_safe
16
+ # "<p>#{user_content}</p>".html_safe
17
+ # => ActiveSupport::SafeBuffer
18
+ # "<p><b>hi</b></p>"
12
19
  #
13
20
  # # good
14
- # content_tag(:p, text)
21
+ # content_tag(:p, user_content)
22
+ # => ActiveSupport::SafeBuffer
23
+ # "<p>&lt;b&gt;hi&lt;/b&gt;</p>"
15
24
  #
16
25
  # # bad
17
26
  # out = ""
18
- # out << content_tag(:li, "one")
19
- # out << content_tag(:li, "two")
27
+ # out << "<li>#{user_content}</li>"
28
+ # out << "<li>#{user_content}</li>"
20
29
  # out.html_safe
30
+ # => ActiveSupport::SafeBuffer
31
+ # "<li><b>hi</b></li><li><b>hi</b></li>"
21
32
  #
22
33
  # # good
23
34
  # out = []
24
- # out << content_tag(:li, "one")
25
- # out << content_tag(:li, "two")
35
+ # out << content_tag(:li, user_content)
36
+ # out << content_tag(:li, user_content)
26
37
  # safe_join(out)
38
+ # => ActiveSupport::SafeBuffer
39
+ # "<li>&lt;b&gt;hi&lt;/b&gt;</li><li>&lt;b&gt;hi&lt;/b&gt;</li>"
40
+ #
41
+ # # bad
42
+ # out = "<h1>trusted content</h1>".html_safe
43
+ # out.safe_concat(user_content)
44
+ # => ActiveSupport::SafeBuffer
45
+ # "<h1>trusted_content</h1><b>hi</b>"
46
+ #
47
+ # # good
48
+ # out = "<h1>trusted content</h1>".html_safe
49
+ # out.concat(user_content)
50
+ # => ActiveSupport::SafeBuffer
51
+ # "<h1>trusted_content</h1>&lt;b&gt;hi&lt;/b&gt;"
52
+ #
53
+ # # safe, though maybe not good style
54
+ # out = "trusted content"
55
+ # result = out.concat(user_content)
56
+ # => String "trusted content<b>hi</b>"
57
+ # # because when rendered in ERB the String will be escaped:
58
+ # <%= result %>
59
+ # => trusted content&lt;b&gt;hi&lt;/b&gt;
60
+ #
61
+ # # bad
62
+ # (user_content + " " + content_tag(:span, user_content)).html_safe
63
+ # => ActiveSupport::SafeBuffer
64
+ # "<b>hi</b> <span><b>hi</b></span>"
65
+ #
66
+ # # good
67
+ # safe_join([user_content, " ", content_tag(:span, user_content)])
68
+ # => ActiveSupport::SafeBuffer
69
+ # "&lt;b&gt;hi&lt;/b&gt; <span>&lt;b&gt;hi&lt;/b&gt;</span>"
27
70
  #
28
71
  class OutputSafety < Cop
29
- MSG = 'Tagging a string as html safe may be a security risk, ' \
30
- 'prefer `safe_join` or other Rails tag helpers instead.'.freeze
72
+ MSG = 'Tagging a string as html safe may be a security risk.'.freeze
31
73
 
32
74
  def on_send(node)
33
- ignore_node(node) if node.method?(:safe_join)
34
-
35
- return unless !part_of_ignored_node?(node) &&
36
- (looks_like_rails_html_safe?(node) ||
37
- looks_like_rails_raw?(node))
75
+ return unless looks_like_rails_html_safe?(node) ||
76
+ looks_like_rails_raw?(node) ||
77
+ looks_like_rails_safe_concat?(node)
38
78
 
39
79
  add_offense(node, :selector)
40
80
  end
@@ -48,6 +88,10 @@ module RuboCop
48
88
  def looks_like_rails_raw?(node)
49
89
  node.command?(:raw) && node.arguments.one?
50
90
  end
91
+
92
+ def looks_like_rails_safe_concat?(node)
93
+ node.method?(:safe_concat) && node.arguments.one?
94
+ end
51
95
  end
52
96
  end
53
97
  end
@@ -35,7 +35,7 @@ module RuboCop
35
35
  # # good
36
36
  # something if foo.present?
37
37
  class Present < Cop
38
- MSG_NOT_BLANK = 'Use `%s.present?` instead of `%s`.'.freeze
38
+ MSG_NOT_BLANK = 'Use `%s` instead of `%s`.'.freeze
39
39
  MSG_EXISTS_AND_NOT_EMPTY = 'Use `%s.present?` instead of `%s`.'.freeze
40
40
  MSG_UNLESS_BLANK = 'Use `if %s.present?` instead of `%s`.'.freeze
41
41
 
@@ -65,7 +65,9 @@ module RuboCop
65
65
  not_blank?(node) do |receiver|
66
66
  add_offense(node,
67
67
  :expression,
68
- format(MSG_NOT_BLANK, receiver.source, node.source))
68
+ format(MSG_NOT_BLANK,
69
+ replacement(receiver),
70
+ node.source))
69
71
  end
70
72
  end
71
73
 
@@ -118,7 +120,7 @@ module RuboCop
118
120
  range = node.loc.expression
119
121
  end
120
122
 
121
- corrector.replace(range, "#{variable1.source}.present?")
123
+ corrector.replace(range, replacement(variable1))
122
124
  end
123
125
  end
124
126
 
@@ -131,6 +133,10 @@ module RuboCop
131
133
  node.loc.expression.begin.join(method_call.loc.expression)
132
134
  end
133
135
  end
136
+
137
+ def replacement(node)
138
+ node.respond_to?(:source) ? "#{node.source}.present?" : 'present?'
139
+ end
134
140
  end
135
141
  end
136
142
  end
@@ -27,16 +27,47 @@ module RuboCop
27
27
  def on_casgn(node)
28
28
  _scope, _constant, rhs = *node
29
29
 
30
- return if rhs.lambda_or_proc?
30
+ # rhs would be nil in a or_asgn node
31
+ return unless rhs
31
32
 
32
- bad_node = node.descendants.find { |n| relative_date_method?(n) }
33
- return unless bad_node
33
+ check_node(rhs)
34
+ end
35
+
36
+ def on_masgn(node)
37
+ lhs, rhs = *node
38
+
39
+ return unless rhs && rhs.array_type?
34
40
 
35
- add_offense(node, :expression, format(MSG, bad_node.method_name))
41
+ lhs.children.zip(rhs.children).each do |(name, value)|
42
+ check_node(value) if name.casgn_type?
43
+ end
44
+ end
45
+
46
+ def on_or_asgn(node)
47
+ lhs, rhs = *node
48
+
49
+ return unless lhs.casgn_type?
50
+
51
+ check_node(rhs)
36
52
  end
37
53
 
38
54
  private
39
55
 
56
+ def check_node(node)
57
+ return unless node.irange_type? ||
58
+ node.erange_type? ||
59
+ node.send_type?
60
+
61
+ # for range nodes we need to check both their boundaries
62
+ nodes = node.send_type? ? [node] : node.children
63
+
64
+ nodes.each do |n|
65
+ if relative_date_method?(n)
66
+ add_offense(node.parent, :expression, format(MSG, n.method_name))
67
+ end
68
+ end
69
+ end
70
+
40
71
  def relative_date_method?(node)
41
72
  node.send_type? &&
42
73
  RELATIVE_DATE_METHODS.include?(node.method_name) &&
@@ -10,7 +10,7 @@ module RuboCop
10
10
  # # bad
11
11
  # def change
12
12
  # change_table :users do |t|
13
- # t.column :name, :string
13
+ # t.remove :name
14
14
  # end
15
15
  # end
16
16
  #
@@ -90,12 +90,49 @@ module RuboCop
90
90
  # remove_foreign_key :accounts, :branches
91
91
  # end
92
92
  #
93
+ # @example
94
+ # # change_table
95
+ #
96
+ # # bad
97
+ # def change
98
+ # change_table :users do |t|
99
+ # t.remove :name
100
+ # t.change_default :authorized, 1
101
+ # t.change :price, :string
102
+ # end
103
+ # end
104
+ #
105
+ # # good
106
+ # def change
107
+ # change_table :users do |t|
108
+ # t.string :name
109
+ # end
110
+ # end
111
+ #
112
+ # # good
113
+ # def change
114
+ # reversible do |dir|
115
+ # change_table :users do |t|
116
+ # dir.up do
117
+ # t.change :price, :string
118
+ # end
119
+ #
120
+ # dir.down do
121
+ # t.change :price, :integer
122
+ # end
123
+ # end
124
+ # end
125
+ # end
126
+ #
93
127
  # @see http://api.rubyonrails.org/classes/ActiveRecord/Migration/CommandRecorder.html
94
128
  class ReversibleMigration < Cop
95
129
  MSG = '%s is not reversible.'.freeze
130
+ IRREVERSIBLE_CHANGE_TABLE_CALLS = %i[
131
+ change change_default remove
132
+ ].freeze
96
133
 
97
134
  def_node_matcher :irreversible_schema_statement_call, <<-END
98
- (send nil ${:change_table :change_table_comment :execute :remove_belongs_to} ...)
135
+ (send nil ${:change_table_comment :execute :remove_belongs_to} ...)
99
136
  END
100
137
 
101
138
  def_node_matcher :drop_table_call, <<-END
@@ -114,6 +151,10 @@ module RuboCop
114
151
  (send nil :remove_foreign_key _ $_)
115
152
  END
116
153
 
154
+ def_node_matcher :change_table_call, <<-END
155
+ (send nil :change_table $_ ...)
156
+ END
157
+
117
158
  def on_send(node)
118
159
  return unless within_change_method?(node)
119
160
  return if within_reversible_block?(node)
@@ -125,6 +166,13 @@ module RuboCop
125
166
  check_remove_foreign_key_node(node)
126
167
  end
127
168
 
169
+ def on_block(node)
170
+ return unless within_change_method?(node)
171
+ return if within_reversible_block?(node)
172
+
173
+ check_change_table_node(node.send_node, node.body)
174
+ end
175
+
128
176
  private
129
177
 
130
178
  def check_irreversible_schema_statement_node(node)
@@ -177,28 +225,44 @@ module RuboCop
177
225
  end
178
226
  end
179
227
 
180
- def within_change_method?(node)
181
- parent = node.parent
182
- while parent
183
- if parent.def_type?
184
- method_name, = *parent
185
- return true if method_name == :change
228
+ def check_change_table_node(node, block)
229
+ change_table_call(node) do |arg|
230
+ if target_rails_version < 4.0
231
+ add_offense(
232
+ node, :expression,
233
+ format(MSG, 'change_table')
234
+ )
235
+ elsif block.send_type?
236
+ check_change_table_offense(arg, block)
237
+ else
238
+ block.each_child_node do |child_node|
239
+ check_change_table_offense(arg, child_node)
240
+ end
186
241
  end
187
- parent = parent.parent
188
242
  end
189
- false
243
+ end
244
+
245
+ def check_change_table_offense(receiver, node)
246
+ method_name = node.method_name
247
+ return if receiver != node.receiver &&
248
+ !IRREVERSIBLE_CHANGE_TABLE_CALLS.include?(method_name)
249
+ add_offense(
250
+ node, :expression,
251
+ format(MSG, "change_table(with #{method_name})")
252
+ )
253
+ end
254
+
255
+ def within_change_method?(node)
256
+ node.each_ancestor(:def).any? do |ancestor|
257
+ method_name, = *ancestor
258
+ method_name == :change
259
+ end
190
260
  end
191
261
 
192
262
  def within_reversible_block?(node)
193
- parent = node.parent
194
- while parent
195
- if parent.block_type?
196
- _, block_name = *parent.to_a.first
197
- return true if block_name == :reversible
198
- end
199
- parent = parent.parent
263
+ node.each_ancestor(:block).any? do |ancestor|
264
+ ancestor.block_type? && ancestor.send_node.method?(:reversible)
200
265
  end
201
- false
202
266
  end
203
267
 
204
268
  def all_hash_key?(args, *keys)