rubocop 0.48.1 → 0.49.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -3
- data/config/default.yml +397 -357
- data/config/disabled.yml +29 -29
- data/config/enabled.yml +366 -326
- data/lib/rubocop.rb +85 -70
- data/lib/rubocop/ast/builder.rb +4 -1
- data/lib/rubocop/ast/node.rb +2 -2
- data/lib/rubocop/ast/node/and_node.rb +1 -1
- data/lib/rubocop/ast/node/args_node.rb +24 -0
- data/lib/rubocop/ast/node/block_node.rb +107 -0
- data/lib/rubocop/ast/node/case_node.rb +1 -1
- data/lib/rubocop/ast/node/ensure_node.rb +1 -1
- data/lib/rubocop/ast/node/for_node.rb +1 -1
- data/lib/rubocop/ast/node/if_node.rb +1 -1
- data/lib/rubocop/ast/node/mixin/parameterized_node.rb +74 -0
- data/lib/rubocop/ast/node/or_node.rb +1 -1
- data/lib/rubocop/ast/node/pair_node.rb +1 -1
- data/lib/rubocop/ast/node/resbody_node.rb +1 -1
- data/lib/rubocop/ast/node/send_node.rb +36 -57
- data/lib/rubocop/ast/node/super_node.rb +42 -0
- data/lib/rubocop/ast/node/until_node.rb +1 -1
- data/lib/rubocop/ast/node/when_node.rb +1 -1
- data/lib/rubocop/ast/node/while_node.rb +1 -1
- data/lib/rubocop/cli.rb +10 -0
- data/lib/rubocop/config.rb +23 -7
- data/lib/rubocop/config_loader.rb +19 -3
- data/lib/rubocop/cop/badge.rb +1 -1
- data/lib/rubocop/cop/bundler/duplicated_gem.rb +2 -2
- data/lib/rubocop/cop/commissioner.rb +1 -1
- data/lib/rubocop/cop/cop.rb +10 -0
- data/lib/rubocop/cop/{style → layout}/access_modifier_indentation.rb +33 -3
- data/lib/rubocop/cop/{style → layout}/align_array.rb +16 -1
- data/lib/rubocop/cop/{style → layout}/align_hash.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/align_parameters.rb +29 -1
- data/lib/rubocop/cop/{style → layout}/block_end_newline.rb +10 -5
- data/lib/rubocop/cop/{style → layout}/case_indentation.rb +64 -1
- data/lib/rubocop/cop/{style → layout}/closing_parenthesis_indentation.rb +2 -2
- data/lib/rubocop/cop/{style → layout}/comment_indentation.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/dot_position.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/else_alignment.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/empty_line_after_magic_comment.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/empty_line_between_defs.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/empty_lines.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/empty_lines_around_access_modifier.rb +2 -7
- data/lib/rubocop/cop/{style → layout}/empty_lines_around_begin_body.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/empty_lines_around_block_body.rb +2 -4
- data/lib/rubocop/cop/{style → layout}/empty_lines_around_class_body.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/empty_lines_around_exception_handling_keywords.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/empty_lines_around_method_body.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/empty_lines_around_module_body.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/end_of_line.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/extra_spacing.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/first_array_element_line_break.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/first_hash_element_line_break.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/first_method_argument_line_break.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/first_method_parameter_line_break.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/first_parameter_indentation.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/indent_array.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/indent_assignment.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/indent_hash.rb +2 -2
- data/lib/rubocop/cop/{style → layout}/indent_heredoc.rb +3 -3
- data/lib/rubocop/cop/{style → layout}/indentation_consistency.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/indentation_width.rb +10 -12
- data/lib/rubocop/cop/{style → layout}/initial_indentation.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/leading_comment_space.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/multiline_array_brace_layout.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/multiline_assignment_layout.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/multiline_block_layout.rb +21 -36
- data/lib/rubocop/cop/{style → layout}/multiline_hash_brace_layout.rb +5 -1
- data/lib/rubocop/cop/{style → layout}/multiline_method_call_brace_layout.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/multiline_method_call_indentation.rb +3 -3
- data/lib/rubocop/cop/{style → layout}/multiline_method_definition_brace_layout.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/multiline_operation_indentation.rb +6 -5
- data/lib/rubocop/cop/{style → layout}/rescue_ensure_alignment.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_after_colon.rb +2 -2
- data/lib/rubocop/cop/{style → layout}/space_after_comma.rb +2 -2
- data/lib/rubocop/cop/{style → layout}/space_after_method_name.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_after_not.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_after_semicolon.rb +2 -2
- data/lib/rubocop/cop/{style → layout}/space_around_block_parameters.rb +7 -5
- data/lib/rubocop/cop/{style → layout}/space_around_equals_in_parameter_default.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_around_keyword.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_around_operators.rb +6 -2
- data/lib/rubocop/cop/{style → layout}/space_before_block_braces.rb +6 -2
- data/lib/rubocop/cop/{style → layout}/space_before_comma.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_before_comment.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_before_first_arg.rb +4 -2
- data/lib/rubocop/cop/{style → layout}/space_before_semicolon.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_in_lambda_literal.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_inside_array_percent_literal.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_inside_block_braces.rb +3 -4
- data/lib/rubocop/cop/{style → layout}/space_inside_brackets.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_inside_hash_literal_braces.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_inside_parens.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_inside_percent_literal_delimiters.rb +8 -7
- data/lib/rubocop/cop/{style → layout}/space_inside_range_literal.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_inside_string_interpolation.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/tab.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/trailing_blank_lines.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/trailing_whitespace.rb +2 -2
- data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
- data/lib/rubocop/cop/lint/ambiguous_operator.rb +4 -4
- data/lib/rubocop/cop/lint/debugger.rb +0 -15
- data/lib/rubocop/cop/lint/duplicate_methods.rb +2 -1
- data/lib/rubocop/cop/lint/rescue_type.rb +81 -0
- data/lib/rubocop/cop/lint/script_permission.rb +42 -0
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -1
- data/lib/rubocop/cop/message_annotator.rb +23 -13
- data/lib/rubocop/cop/metrics/block_length.rb +1 -1
- data/lib/rubocop/cop/mixin/array_min_size.rb +59 -0
- data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +10 -11
- data/lib/rubocop/cop/mixin/def_node.rb +1 -1
- data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +1 -1
- data/lib/rubocop/cop/mixin/enforce_superclass.rb +36 -0
- data/lib/rubocop/cop/mixin/hash_alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +7 -3
- data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
- data/lib/rubocop/cop/performance/caller.rb +41 -0
- data/lib/rubocop/cop/performance/compare_with_block.rb +60 -14
- data/lib/rubocop/cop/performance/double_start_end_with.rb +2 -2
- data/lib/rubocop/cop/performance/redundant_merge.rb +2 -0
- data/lib/rubocop/cop/rails/action_filter.rb +1 -3
- data/lib/rubocop/cop/rails/application_job.rb +32 -0
- data/lib/rubocop/cop/rails/application_record.rb +32 -0
- data/lib/rubocop/cop/rails/blank.rb +9 -3
- data/lib/rubocop/cop/rails/output_safety.rb +59 -15
- data/lib/rubocop/cop/rails/present.rb +9 -3
- data/lib/rubocop/cop/rails/relative_date_constant.rb +35 -4
- data/lib/rubocop/cop/rails/reversible_migration.rb +82 -18
- data/lib/rubocop/cop/rails/save_bang.rb +7 -2
- data/lib/rubocop/cop/rails/skips_model_validations.rb +7 -0
- data/lib/rubocop/cop/registry.rb +4 -3
- data/lib/rubocop/cop/security/eval.rb +9 -3
- data/lib/rubocop/cop/style/and_or.rb +1 -1
- data/lib/rubocop/cop/style/block_delimiters.rb +11 -17
- data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +1 -1
- data/lib/rubocop/cop/style/collection_methods.rb +1 -3
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
- data/lib/rubocop/cop/style/copyright.rb +2 -2
- data/lib/rubocop/cop/style/documentation_method.rb +1 -1
- data/lib/rubocop/cop/style/each_for_simple_loop.rb +2 -1
- data/lib/rubocop/cop/style/each_with_object.rb +10 -6
- data/lib/rubocop/cop/style/empty_case_condition.rb +2 -2
- data/lib/rubocop/cop/style/for.rb +4 -5
- data/lib/rubocop/cop/style/format_string.rb +49 -0
- data/lib/rubocop/cop/style/format_string_token.rb +141 -0
- data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +2 -2
- data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +1 -1
- data/lib/rubocop/cop/style/inverse_methods.rb +10 -1
- data/lib/rubocop/cop/style/lambda.rb +9 -9
- data/lib/rubocop/cop/style/line_end_concatenation.rb +4 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +3 -3
- data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +1 -2
- data/lib/rubocop/cop/style/method_name.rb +8 -2
- data/lib/rubocop/cop/style/mixin_grouping.rb +41 -3
- data/lib/rubocop/cop/style/multiline_block_chain.rb +7 -11
- data/lib/rubocop/cop/style/multiple_comparison.rb +77 -0
- data/lib/rubocop/cop/style/next.rb +11 -22
- data/lib/rubocop/cop/style/parallel_assignment.rb +10 -19
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -2
- data/lib/rubocop/cop/style/self_assignment.rb +4 -0
- data/lib/rubocop/cop/style/single_line_block_params.rb +23 -17
- data/lib/rubocop/cop/style/symbol_array.rb +24 -13
- data/lib/rubocop/cop/style/symbol_proc.rb +4 -0
- data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/unneeded_interpolation.rb +4 -0
- data/lib/rubocop/cop/style/word_array.rb +33 -53
- data/lib/rubocop/cop/style/yoda_condition.rb +78 -0
- data/lib/rubocop/cop/team.rb +1 -14
- data/lib/rubocop/cop/util.rb +16 -0
- data/lib/rubocop/formatter/simple_text_formatter.rb +0 -11
- data/lib/rubocop/node_pattern.rb +52 -52
- data/lib/rubocop/options.rb +25 -0
- data/lib/rubocop/path_util.rb +17 -1
- data/lib/rubocop/result_cache.rb +8 -7
- data/lib/rubocop/rspec/expect_offense.rb +167 -0
- data/lib/rubocop/rspec/shared_examples.rb +0 -8
- data/lib/rubocop/rspec/support.rb +1 -0
- data/lib/rubocop/runner.rb +12 -2
- data/lib/rubocop/target_finder.rb +5 -0
- data/lib/rubocop/version.rb +1 -1
- 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
|
26
|
-
'`%s { |%s, %s| %s
|
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
|
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,
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
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
|
-
|
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
|
27
|
-
'instead of
|
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)
|
@@ -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
|
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,
|
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,
|
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
|
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>#{
|
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,
|
21
|
+
# content_tag(:p, user_content)
|
22
|
+
# => ActiveSupport::SafeBuffer
|
23
|
+
# "<p><b>hi</b></p>"
|
15
24
|
#
|
16
25
|
# # bad
|
17
26
|
# out = ""
|
18
|
-
# out <<
|
19
|
-
# out <<
|
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,
|
25
|
-
# out << content_tag(:li,
|
35
|
+
# out << content_tag(:li, user_content)
|
36
|
+
# out << content_tag(:li, user_content)
|
26
37
|
# safe_join(out)
|
38
|
+
# => ActiveSupport::SafeBuffer
|
39
|
+
# "<li><b>hi</b></li><li><b>hi</b></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><b>hi</b>"
|
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<b>hi</b>
|
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
|
+
# "<b>hi</b> <span><b>hi</b></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
|
-
|
34
|
-
|
35
|
-
|
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
|
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,
|
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,
|
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
|
-
|
30
|
+
# rhs would be nil in a or_asgn node
|
31
|
+
return unless rhs
|
31
32
|
|
32
|
-
|
33
|
-
|
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
|
-
|
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.
|
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 ${:
|
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
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
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
|
-
|
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
|
-
|
194
|
-
|
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)
|