rubocop 0.58.2 → 0.59.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 +1 -1
- data/config/default.yml +22 -7
- data/config/disabled.yml +33 -4
- data/config/enabled.yml +4 -11
- data/lib/rubocop.rb +5 -0
- data/lib/rubocop/ast/builder.rb +1 -0
- data/lib/rubocop/ast/node.rb +11 -33
- data/lib/rubocop/ast/node/block_node.rb +8 -1
- data/lib/rubocop/ast/node/defined_node.rb +13 -0
- data/lib/rubocop/ast/node/mixin/method_dispatch_node.rb +16 -5
- data/lib/rubocop/ast/node/mixin/method_identifier_predicates.rb +21 -0
- data/lib/rubocop/ast/node/send_node.rb +3 -12
- data/lib/rubocop/ast/traversal.rb +10 -0
- data/lib/rubocop/cli.rb +4 -1
- data/lib/rubocop/config.rb +21 -5
- data/lib/rubocop/config_loader.rb +2 -0
- data/lib/rubocop/config_loader_resolver.rb +3 -1
- data/lib/rubocop/cop/autocorrect_logic.rb +1 -0
- data/lib/rubocop/cop/bundler/gem_comment.rb +64 -0
- data/lib/rubocop/cop/bundler/ordered_gems.rb +2 -0
- data/lib/rubocop/cop/commissioner.rb +2 -0
- data/lib/rubocop/cop/cop.rb +3 -0
- data/lib/rubocop/cop/corrector.rb +2 -0
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -0
- data/lib/rubocop/cop/correctors/line_break_corrector.rb +2 -0
- data/lib/rubocop/cop/correctors/space_corrector.rb +2 -0
- data/lib/rubocop/cop/force.rb +1 -0
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -0
- data/lib/rubocop/cop/generator.rb +1 -0
- data/lib/rubocop/cop/layout/access_modifier_indentation.rb +1 -0
- data/lib/rubocop/cop/layout/class_structure.rb +4 -0
- data/lib/rubocop/cop/layout/closing_heredoc_indentation.rb +5 -4
- data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +35 -0
- data/lib/rubocop/cop/layout/else_alignment.rb +1 -0
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +2 -0
- data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +1 -0
- data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +5 -2
- data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +1 -0
- data/lib/rubocop/cop/layout/end_of_line.rb +1 -0
- data/lib/rubocop/cop/layout/extra_spacing.rb +1 -0
- data/lib/rubocop/cop/layout/indent_array.rb +1 -0
- data/lib/rubocop/cop/layout/indent_heredoc.rb +3 -0
- data/lib/rubocop/cop/layout/indentation_width.rb +2 -0
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +1 -0
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -1
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +34 -11
- data/lib/rubocop/cop/layout/space_after_method_name.rb +1 -0
- data/lib/rubocop/cop/layout/space_after_not.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_keyword.rb +3 -1
- data/lib/rubocop/cop/layout/space_around_operators.rb +1 -0
- data/lib/rubocop/cop/layout/space_before_comment.rb +1 -0
- data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +16 -8
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +2 -0
- data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +1 -1
- data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +2 -0
- data/lib/rubocop/cop/layout/tab.rb +1 -0
- data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -0
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +1 -0
- data/lib/rubocop/cop/lint/duplicate_methods.rb +9 -1
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +1 -0
- data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +1 -0
- data/lib/rubocop/cop/lint/interpolation_check.rb +2 -0
- data/lib/rubocop/cop/lint/literal_as_condition.rb +3 -6
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -0
- data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -0
- data/lib/rubocop/cop/lint/rescue_exception.rb +1 -0
- data/lib/rubocop/cop/lint/rescue_type.rb +1 -0
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +2 -2
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +2 -0
- data/lib/rubocop/cop/lint/script_permission.rb +1 -0
- data/lib/rubocop/cop/lint/shadowed_argument.rb +3 -0
- data/lib/rubocop/cop/lint/shadowed_exception.rb +2 -0
- data/lib/rubocop/cop/lint/unneeded_cop_disable_directive.rb +1 -0
- data/lib/rubocop/cop/lint/unneeded_cop_enable_directive.rb +1 -0
- data/lib/rubocop/cop/lint/unneeded_require_statement.rb +1 -0
- data/lib/rubocop/cop/lint/unneeded_splat_expansion.rb +1 -1
- data/lib/rubocop/cop/lint/unreachable_code.rb +2 -0
- data/lib/rubocop/cop/lint/useless_assignment.rb +1 -0
- data/lib/rubocop/cop/lint/useless_setter_call.rb +3 -0
- data/lib/rubocop/cop/lint/void.rb +1 -0
- data/lib/rubocop/cop/message_annotator.rb +1 -0
- data/lib/rubocop/cop/metrics/block_length.rb +1 -0
- data/lib/rubocop/cop/metrics/block_nesting.rb +1 -0
- data/lib/rubocop/cop/metrics/line_length.rb +6 -1
- data/lib/rubocop/cop/metrics/method_length.rb +1 -0
- data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -0
- data/lib/rubocop/cop/mixin/classish_length.rb +1 -0
- data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +1 -0
- data/lib/rubocop/cop/mixin/configurable_formatting.rb +1 -0
- data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +12 -6
- data/lib/rubocop/cop/mixin/empty_parameter.rb +1 -0
- data/lib/rubocop/cop/mixin/ignored_methods.rb +19 -0
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +25 -1
- data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +5 -3
- data/lib/rubocop/cop/mixin/percent_literal.rb +2 -0
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +2 -0
- data/lib/rubocop/cop/mixin/safe_assignment.rb +2 -1
- data/lib/rubocop/cop/mixin/statement_modifier.rb +6 -1
- data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -0
- data/lib/rubocop/cop/mixin/surrounding_space.rb +4 -0
- data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -0
- data/lib/rubocop/cop/mixin/uncommunicative_name.rb +2 -0
- data/lib/rubocop/cop/naming/ascii_identifiers.rb +1 -0
- data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +1 -0
- data/lib/rubocop/cop/naming/file_name.rb +4 -1
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -0
- data/lib/rubocop/cop/naming/predicate_name.rb +1 -0
- data/lib/rubocop/cop/naming/uncommunicative_block_param_name.rb +1 -0
- data/lib/rubocop/cop/naming/uncommunicative_method_param_name.rb +1 -0
- data/lib/rubocop/cop/naming/variable_name.rb +1 -0
- data/lib/rubocop/cop/performance/case_when_splat.rb +11 -7
- data/lib/rubocop/cop/performance/casecmp.rb +33 -42
- data/lib/rubocop/cop/performance/chain_array_allocation.rb +77 -0
- data/lib/rubocop/cop/performance/compare_with_block.rb +3 -0
- data/lib/rubocop/cop/performance/regexp_match.rb +1 -0
- data/lib/rubocop/cop/performance/sample.rb +2 -0
- data/lib/rubocop/cop/performance/size.rb +8 -2
- data/lib/rubocop/cop/performance/string_replacement.rb +1 -0
- data/lib/rubocop/cop/rails/active_support_aliases.rb +1 -0
- data/lib/rubocop/cop/rails/bulk_change_table.rb +9 -2
- data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +1 -0
- data/lib/rubocop/cop/rails/delegate.rb +7 -2
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +1 -0
- data/lib/rubocop/cop/rails/find_each.rb +7 -2
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +1 -1
- data/lib/rubocop/cop/rails/http_status.rb +2 -0
- data/lib/rubocop/cop/rails/inverse_of.rb +4 -0
- data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +1 -0
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +1 -0
- data/lib/rubocop/cop/rails/reversible_migration.rb +1 -0
- data/lib/rubocop/cop/rails/save_bang.rb +189 -38
- data/lib/rubocop/cop/rails/time_zone.rb +1 -0
- data/lib/rubocop/cop/security/eval.rb +1 -0
- data/lib/rubocop/cop/security/json_load.rb +2 -2
- data/lib/rubocop/cop/security/open.rb +6 -3
- data/lib/rubocop/cop/severity.rb +1 -0
- data/lib/rubocop/cop/style/and_or.rb +3 -3
- data/lib/rubocop/cop/style/ascii_comments.rb +1 -0
- data/lib/rubocop/cop/style/block_delimiters.rb +2 -4
- data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +2 -3
- data/lib/rubocop/cop/style/class_and_module_children.rb +3 -0
- data/lib/rubocop/cop/style/class_vars.rb +1 -1
- data/lib/rubocop/cop/style/colon_method_definition.rb +1 -0
- data/lib/rubocop/cop/style/commented_keyword.rb +2 -0
- data/lib/rubocop/cop/style/conditional_assignment.rb +2 -0
- data/lib/rubocop/cop/style/copyright.rb +7 -2
- data/lib/rubocop/cop/style/date_time.rb +40 -7
- data/lib/rubocop/cop/style/double_negation.rb +1 -1
- data/lib/rubocop/cop/style/empty_case_condition.rb +8 -0
- data/lib/rubocop/cop/style/empty_else.rb +2 -0
- data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -0
- data/lib/rubocop/cop/style/eval_with_location.rb +2 -0
- data/lib/rubocop/cop/style/for.rb +56 -10
- data/lib/rubocop/cop/style/format_string_token.rb +1 -1
- data/lib/rubocop/cop/style/if_with_semicolon.rb +1 -0
- data/lib/rubocop/cop/style/inverse_methods.rb +1 -0
- data/lib/rubocop/cop/style/lambda.rb +1 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +3 -5
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +3 -5
- data/lib/rubocop/cop/style/method_def_parentheses.rb +2 -2
- data/lib/rubocop/cop/style/missing_else.rb +1 -0
- data/lib/rubocop/cop/style/multiline_memoization.rb +1 -0
- data/lib/rubocop/cop/style/multiline_method_signature.rb +65 -0
- data/lib/rubocop/cop/style/multiple_comparison.rb +1 -0
- data/lib/rubocop/cop/style/nil_comparison.rb +45 -5
- data/lib/rubocop/cop/style/not.rb +1 -1
- data/lib/rubocop/cop/style/numeric_predicate.rb +5 -0
- data/lib/rubocop/cop/style/one_line_conditional.rb +1 -1
- data/lib/rubocop/cop/style/or_assignment.rb +2 -0
- data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
- data/lib/rubocop/cop/style/random_with_offset.rb +1 -0
- data/lib/rubocop/cop/style/redundant_begin.rb +13 -0
- data/lib/rubocop/cop/style/redundant_conditional.rb +1 -0
- data/lib/rubocop/cop/style/redundant_parentheses.rb +6 -1
- data/lib/rubocop/cop/style/redundant_return.rb +1 -0
- data/lib/rubocop/cop/style/rescue_modifier.rb +1 -0
- data/lib/rubocop/cop/style/rescue_standard_error.rb +1 -0
- data/lib/rubocop/cop/style/safe_navigation.rb +4 -0
- data/lib/rubocop/cop/style/semicolon.rb +4 -0
- data/lib/rubocop/cop/style/signal_exception.rb +1 -0
- data/lib/rubocop/cop/style/string_hash_keys.rb +1 -0
- data/lib/rubocop/cop/style/symbol_proc.rb +1 -8
- data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +1 -0
- data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -0
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +1 -0
- data/lib/rubocop/cop/style/unneeded_condition.rb +13 -2
- data/lib/rubocop/cop/style/unneeded_percent_q.rb +2 -0
- data/lib/rubocop/cop/style/word_array.rb +13 -1
- data/lib/rubocop/cop/team.rb +1 -0
- data/lib/rubocop/cop/variable_force.rb +5 -0
- data/lib/rubocop/cop/variable_force/assignment.rb +4 -0
- data/lib/rubocop/cop/variable_force/branch.rb +4 -0
- data/lib/rubocop/cop/variable_force/branchable.rb +2 -0
- data/lib/rubocop/cop/variable_force/scope.rb +6 -0
- data/lib/rubocop/cop/variable_force/variable_table.rb +1 -0
- data/lib/rubocop/file_finder.rb +2 -0
- data/lib/rubocop/formatter/disabled_config_formatter.rb +4 -4
- data/lib/rubocop/formatter/file_list_formatter.rb +1 -0
- data/lib/rubocop/formatter/simple_text_formatter.rb +1 -0
- data/lib/rubocop/options.rb +16 -0
- data/lib/rubocop/path_util.rb +16 -1
- data/lib/rubocop/processed_source.rb +4 -0
- data/lib/rubocop/remote_config.rb +6 -1
- data/lib/rubocop/result_cache.rb +1 -0
- data/lib/rubocop/rspec/cop_helper.rb +3 -5
- data/lib/rubocop/rspec/shared_examples.rb +1 -9
- data/lib/rubocop/runner.rb +4 -0
- data/lib/rubocop/target_finder.rb +2 -0
- data/lib/rubocop/version.rb +1 -1
- metadata +7 -2
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# This cop is used to identify usages of
|
7
|
+
# @example
|
8
|
+
# # bad
|
9
|
+
# array = ["a", "b", "c"]
|
10
|
+
# array.compact.flatten.map { |x| x.downcase }
|
11
|
+
#
|
12
|
+
# Each of these methods (`compact`, `flatten`, `map`) will generate a
|
13
|
+
# new intermediate array that is promptly thrown away. Instead it is
|
14
|
+
# faster to mutate when we know it's safe.
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
# # good.
|
18
|
+
# array = ["a", "b", "c"]
|
19
|
+
# array.compact!
|
20
|
+
# array.flatten!
|
21
|
+
# array.map! { |x| x.downcase }
|
22
|
+
# array
|
23
|
+
class ChainArrayAllocation < Cop
|
24
|
+
include RangeHelp
|
25
|
+
|
26
|
+
# These methods return a new array but only sometimes. They must be
|
27
|
+
# called with an argument. For example:
|
28
|
+
#
|
29
|
+
# [1,2].first # => 1
|
30
|
+
# [1,2].first(1) # => [1]
|
31
|
+
#
|
32
|
+
RETURN_NEW_ARRAY_WHEN_ARGS = ':first :last :pop :sample :shift '.freeze
|
33
|
+
|
34
|
+
# These methods return a new array only when called without a block.
|
35
|
+
RETURNS_NEW_ARRAY_WHEN_NO_BLOCK = ':zip :product '.freeze
|
36
|
+
|
37
|
+
# These methods ALWAYS return a new array
|
38
|
+
# after they're called it's safe to mutate the the resulting array
|
39
|
+
ALWAYS_RETURNS_NEW_ARRAY = ':* :+ :- :collect :compact :drop '\
|
40
|
+
':drop_while :flatten :map :reject ' \
|
41
|
+
':reverse :rotate :select :shuffle :sort ' \
|
42
|
+
':take :take_while :transpose :uniq ' \
|
43
|
+
':values_at :| '.freeze
|
44
|
+
|
45
|
+
# These methods have a mutation alternative. For example :collect
|
46
|
+
# can be called as :collect!
|
47
|
+
HAS_MUTATION_ALTERNATIVE = ':collect :compact :flatten :map :reject '\
|
48
|
+
':reverse :rotate :select :shuffle :sort '\
|
49
|
+
':uniq '.freeze
|
50
|
+
MSG = 'Use `%<method>s...%<second_method>s!` instead of `%<method>s' \
|
51
|
+
'...%<second_method>s`.'.freeze
|
52
|
+
|
53
|
+
def_node_matcher :flat_map_candidate?, <<-PATTERN
|
54
|
+
{
|
55
|
+
(send (send _ ${#{RETURN_NEW_ARRAY_WHEN_ARGS}} {int lvar ivar cvar gvar}) ${#{HAS_MUTATION_ALTERNATIVE}} $...)
|
56
|
+
(send (block (send _ ${#{ALWAYS_RETURNS_NEW_ARRAY} }) ...) ${#{HAS_MUTATION_ALTERNATIVE}} $...)
|
57
|
+
(send (send _ ${#{ALWAYS_RETURNS_NEW_ARRAY + RETURNS_NEW_ARRAY_WHEN_NO_BLOCK}} ...) ${#{HAS_MUTATION_ALTERNATIVE}} $...)
|
58
|
+
}
|
59
|
+
PATTERN
|
60
|
+
|
61
|
+
def on_send(node)
|
62
|
+
flat_map_candidate?(node) do |fm, sm, _|
|
63
|
+
range = range_between(
|
64
|
+
node.loc.dot.begin_pos,
|
65
|
+
node.source_range.end_pos
|
66
|
+
)
|
67
|
+
add_offense(
|
68
|
+
node,
|
69
|
+
location: range,
|
70
|
+
message: format(MSG, method: fm, second_method: sm)
|
71
|
+
)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -48,6 +48,7 @@ module RuboCop
|
|
48
48
|
compare?(node) do |send, var_a, var_b, body|
|
49
49
|
replaceable_body?(body, var_a, var_b) do |method, args_a, args_b|
|
50
50
|
return unless slow_compare?(method, args_a, args_b)
|
51
|
+
|
51
52
|
range = compare_range(send, node)
|
52
53
|
|
53
54
|
add_offense(
|
@@ -78,8 +79,10 @@ module RuboCop
|
|
78
79
|
|
79
80
|
def slow_compare?(method, args_a, args_b)
|
80
81
|
return false unless args_a == args_b
|
82
|
+
|
81
83
|
if method == :[]
|
82
84
|
return false unless args_a.size == 1
|
85
|
+
|
83
86
|
key = args_a.first
|
84
87
|
return false unless %i[sym str int].include?(key.type)
|
85
88
|
else
|
@@ -88,12 +88,14 @@ module RuboCop
|
|
88
88
|
|
89
89
|
def sample_size_for_two_args(first, second)
|
90
90
|
return :unknown unless first.int_type? && first.to_a.first.zero?
|
91
|
+
|
91
92
|
second.int_type? ? second.to_a.first : :unknown
|
92
93
|
end
|
93
94
|
|
94
95
|
def range_size(range_node)
|
95
96
|
vals = range_node.to_a
|
96
97
|
return :unknown unless vals.all?(&:int_type?)
|
98
|
+
|
97
99
|
low, high = vals.map { |val| val.children[0] }
|
98
100
|
return :unknown unless low.zero? && high >= 0
|
99
101
|
|
@@ -55,15 +55,21 @@ module RuboCop
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def array?(node)
|
58
|
+
return true if node.array_type?
|
59
|
+
return false unless node.send_type?
|
60
|
+
|
58
61
|
_, constant = *node.receiver
|
59
62
|
|
60
|
-
|
63
|
+
constant == :Array || node.method_name == :to_a
|
61
64
|
end
|
62
65
|
|
63
66
|
def hash?(node)
|
67
|
+
return true if node.hash_type?
|
68
|
+
return false unless node.send_type?
|
69
|
+
|
64
70
|
_, constant = *node.receiver
|
65
71
|
|
66
|
-
|
72
|
+
constant == :Hash || node.method_name == :to_h
|
67
73
|
end
|
68
74
|
end
|
69
75
|
end
|
@@ -87,6 +87,7 @@ module RuboCop
|
|
87
87
|
unless first_param.str_type?
|
88
88
|
return true if options
|
89
89
|
return true unless first_source =~ DETERMINISTIC_REGEX
|
90
|
+
|
90
91
|
# This must be done after checking DETERMINISTIC_REGEX
|
91
92
|
# Otherwise things like \s will trip us up
|
92
93
|
first_source = interpret_string_escapes(first_source)
|
@@ -6,8 +6,8 @@ module RuboCop
|
|
6
6
|
# This Cop checks whether alter queries are combinable.
|
7
7
|
# If combinable queries are detected, it suggests to you
|
8
8
|
# to use `change_table` with `bulk: true` instead.
|
9
|
-
#
|
10
|
-
#
|
9
|
+
# This option causes the migration to generate a single
|
10
|
+
# ALTER TABLE statement combining multiple column alterations.
|
11
11
|
#
|
12
12
|
# The `bulk` option is only supported on the MySQL and
|
13
13
|
# the PostgreSQL (5.2 later) adapter; thus it will
|
@@ -155,6 +155,7 @@ module RuboCop
|
|
155
155
|
return unless node.command?(:change_table)
|
156
156
|
return if include_bulk_options?(node)
|
157
157
|
return unless node.block_node
|
158
|
+
|
158
159
|
send_nodes = node.block_node.body.each_child_node(:send).to_a
|
159
160
|
|
160
161
|
transformations = send_nodes.select do |send_node|
|
@@ -171,6 +172,7 @@ module RuboCop
|
|
171
172
|
# arguments: [{(sym :table)(str "table")} (hash (pair (sym :bulk) _))]
|
172
173
|
options = node.arguments[1]
|
173
174
|
return false unless options
|
175
|
+
|
174
176
|
options.hash_type? &&
|
175
177
|
options.keys.any? { |key| key.sym_type? && key.value == :bulk }
|
176
178
|
end
|
@@ -181,6 +183,7 @@ module RuboCop
|
|
181
183
|
|
182
184
|
def database_from_yaml
|
183
185
|
return nil unless database_yaml
|
186
|
+
|
184
187
|
case database_yaml['adapter']
|
185
188
|
when 'mysql2'
|
186
189
|
MYSQL
|
@@ -191,10 +194,13 @@ module RuboCop
|
|
191
194
|
|
192
195
|
def database_yaml
|
193
196
|
return nil unless File.exist?('config/database.yml')
|
197
|
+
|
194
198
|
yaml = YAML.load_file('config/database.yml')
|
195
199
|
return nil unless yaml.is_a? Hash
|
200
|
+
|
196
201
|
config = yaml['development']
|
197
202
|
return nil unless config.is_a?(Hash)
|
203
|
+
|
198
204
|
config
|
199
205
|
rescue Psych::SyntaxError
|
200
206
|
nil
|
@@ -236,6 +242,7 @@ module RuboCop
|
|
236
242
|
# arguments: [{(sym :table)(str "table")} ...]
|
237
243
|
table_node = node.arguments[0]
|
238
244
|
return unless table_node.is_a? RuboCop::AST::BasicLiteralNode
|
245
|
+
|
239
246
|
message = format(MSG_FOR_ALTER_METHODS, table: table_node.value)
|
240
247
|
add_offense(node, message: message)
|
241
248
|
end
|
@@ -94,8 +94,13 @@ module RuboCop
|
|
94
94
|
def arguments_match?(arg_array, body)
|
95
95
|
argument_array = body.arguments
|
96
96
|
|
97
|
-
arg_array
|
98
|
-
|
97
|
+
return false if arg_array.size != argument_array.size
|
98
|
+
|
99
|
+
arg_array.zip(argument_array).all? do |arg, argument|
|
100
|
+
arg.arg_type? &&
|
101
|
+
argument.lvar_type? &&
|
102
|
+
arg.children == argument.children
|
103
|
+
end
|
99
104
|
end
|
100
105
|
|
101
106
|
def method_name_matches?(method_name, body)
|
@@ -15,11 +15,16 @@ module RuboCop
|
|
15
15
|
class FindEach < Cop
|
16
16
|
MSG = 'Use `find_each` instead of `each`.'.freeze
|
17
17
|
|
18
|
-
SCOPE_METHODS = %i[
|
18
|
+
SCOPE_METHODS = %i[
|
19
|
+
all eager_load includes joins left_joins left_outer_joins not preload
|
20
|
+
references unscoped where
|
21
|
+
].freeze
|
19
22
|
IGNORED_METHODS = %i[order limit select].freeze
|
20
23
|
|
21
24
|
def on_send(node)
|
22
|
-
return unless node.receiver &&
|
25
|
+
return unless node.receiver &&
|
26
|
+
node.receiver.send_type? &&
|
27
|
+
node.method?(:each)
|
23
28
|
|
24
29
|
return unless SCOPE_METHODS.include?(node.receiver.method_name)
|
25
30
|
return if method_chain(node).any? { |m| ignored_by_find_each?(m) }
|
@@ -5,7 +5,7 @@ module RuboCop
|
|
5
5
|
module Rails
|
6
6
|
# This cop is used to identify usages of http methods like `get`, `post`,
|
7
7
|
# `put`, `patch` without the usage of keyword arguments in your tests and
|
8
|
-
# change them to use keyword args.
|
8
|
+
# change them to use keyword args. This cop only applies to Rails >= 5.
|
9
9
|
# If you are running Rails < 5 you should disable the
|
10
10
|
# Rails/HttpPositionalArguments cop or set your TargetRailsVersion in your
|
11
11
|
# .rubocop.yml file to 4.0, etc.
|
@@ -56,8 +56,10 @@ module RuboCop
|
|
56
56
|
http_status(node) do |hash_node|
|
57
57
|
status = status_code(hash_node)
|
58
58
|
return unless status
|
59
|
+
|
59
60
|
checker = checker_class.new(status)
|
60
61
|
return unless checker.offensive?
|
62
|
+
|
61
63
|
add_offense(checker.node, message: checker.message)
|
62
64
|
end
|
63
65
|
end
|
@@ -178,6 +178,7 @@ module RuboCop
|
|
178
178
|
def on_send(node)
|
179
179
|
recv, arguments = association_recv_arguments(node)
|
180
180
|
return unless arguments
|
181
|
+
|
181
182
|
with_options = with_options_arguments(recv, node)
|
182
183
|
|
183
184
|
options = arguments.concat(with_options).flat_map do |arg|
|
@@ -189,6 +190,7 @@ module RuboCop
|
|
189
190
|
options_requiring_inverse_of?(options)
|
190
191
|
|
191
192
|
return if options_contain_inverse_of?(options)
|
193
|
+
|
192
194
|
add_offense(node, message: message(options), location: :selector)
|
193
195
|
end
|
194
196
|
|
@@ -203,6 +205,7 @@ module RuboCop
|
|
203
205
|
end
|
204
206
|
|
205
207
|
return required if target_rails_version >= 5.2
|
208
|
+
|
206
209
|
required || options.any? { |opt| as_option?(opt) }
|
207
210
|
end
|
208
211
|
|
@@ -226,6 +229,7 @@ module RuboCop
|
|
226
229
|
|
227
230
|
def same_context_in_with_options?(arg, recv)
|
228
231
|
return true if arg.nil? && recv.nil?
|
232
|
+
|
229
233
|
arg && recv && arg.children[0] == recv.children[0]
|
230
234
|
end
|
231
235
|
|
@@ -7,12 +7,21 @@ module RuboCop
|
|
7
7
|
# should be used instead of save because the model might have failed to
|
8
8
|
# save and an exception is better than unhandled failure.
|
9
9
|
#
|
10
|
-
# This will
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
10
|
+
# This will allow:
|
11
|
+
# - update or save calls, assigned to a variable,
|
12
|
+
# or used as a condition in an if/unless/case statement.
|
13
|
+
# - create calls, assigned to a variable that then has a
|
14
|
+
# call to `persisted?`.
|
15
|
+
# - calls if the result is explicitly returned from methods and blocks,
|
16
|
+
# or provided as arguments.
|
17
|
+
# - calls whose signature doesn't look like an ActiveRecord
|
18
|
+
# persistence method.
|
19
|
+
#
|
20
|
+
# By default it will also allow implicit returns from methods and blocks.
|
21
|
+
# that behavior can be turned off with `AllowImplicitReturn: false`.
|
22
|
+
#
|
23
|
+
# You can permit receivers that are giving false positives with
|
24
|
+
# `AllowedReceivers: []`
|
16
25
|
#
|
17
26
|
# @example
|
18
27
|
#
|
@@ -35,6 +44,58 @@ module RuboCop
|
|
35
44
|
# unless user.persisted?
|
36
45
|
# # ...
|
37
46
|
# end
|
47
|
+
#
|
48
|
+
# def save_user
|
49
|
+
# return user.save
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# @example AllowImplicitReturn: true (default)
|
53
|
+
#
|
54
|
+
# # good
|
55
|
+
# users.each { |u| u.save }
|
56
|
+
#
|
57
|
+
# def save_user
|
58
|
+
# user.save
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# @example AllowImplicitReturn: false
|
62
|
+
#
|
63
|
+
# # bad
|
64
|
+
# users.each { |u| u.save }
|
65
|
+
# def save_user
|
66
|
+
# user.save
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
# # good
|
70
|
+
# users.each { |u| u.save! }
|
71
|
+
#
|
72
|
+
# def save_user
|
73
|
+
# user.save!
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
# def save_user
|
77
|
+
# return user.save
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
# @example AllowedReceivers: ['merchant.customers', 'Service::Mailer']
|
81
|
+
#
|
82
|
+
# # bad
|
83
|
+
# merchant.create
|
84
|
+
# customers.builder.save
|
85
|
+
# Mailer.create
|
86
|
+
#
|
87
|
+
# module Service::Mailer
|
88
|
+
# self.create
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# # good
|
92
|
+
# merchant.customers.create
|
93
|
+
# MerchantService.merchant.customers.destroy
|
94
|
+
# Service::Mailer.update(message: 'Message')
|
95
|
+
# ::Service::Mailer.update
|
96
|
+
# Services::Service::Mailer.update(message: 'Message')
|
97
|
+
# Service::Mailer::update
|
98
|
+
#
|
38
99
|
class SaveBang < Cop
|
39
100
|
include NegativeConditional
|
40
101
|
|
@@ -43,7 +104,7 @@ module RuboCop
|
|
43
104
|
CREATE_MSG = (MSG +
|
44
105
|
' Or check `persisted?` on model returned from ' \
|
45
106
|
'`%<current>s`.').freeze
|
46
|
-
CREATE_CONDITIONAL_MSG = '`%<
|
107
|
+
CREATE_CONDITIONAL_MSG = '`%<current>s` returns a model which is ' \
|
47
108
|
'always truthy.'.freeze
|
48
109
|
|
49
110
|
CREATE_PERSIST_METHODS = %i[create
|
@@ -67,28 +128,23 @@ module RuboCop
|
|
67
128
|
|
68
129
|
def check_assignment(assignment)
|
69
130
|
node = right_assignment_node(assignment)
|
70
|
-
|
71
|
-
return unless
|
72
|
-
return unless
|
131
|
+
|
132
|
+
return unless node && node.send_type?
|
133
|
+
return unless persist_method?(node, CREATE_PERSIST_METHODS)
|
73
134
|
return if persisted_referenced?(assignment)
|
74
135
|
|
75
|
-
|
76
|
-
message: format(CREATE_MSG,
|
77
|
-
prefer: "#{node.method_name}!",
|
78
|
-
current: node.method_name.to_s))
|
136
|
+
add_offense_for_node(node, CREATE_MSG)
|
79
137
|
end
|
80
138
|
|
81
|
-
def on_send(node)
|
82
|
-
return unless
|
83
|
-
return unless expected_signature?(node)
|
139
|
+
def on_send(node) # rubocop:disable Metrics/CyclomaticComplexity
|
140
|
+
return unless persist_method?(node)
|
84
141
|
return if return_value_assigned?(node)
|
85
142
|
return if check_used_in_conditional(node)
|
86
|
-
return if
|
143
|
+
return if argument?(node)
|
144
|
+
return if implicit_return?(node)
|
145
|
+
return if explicit_return?(node)
|
87
146
|
|
88
|
-
|
89
|
-
message: format(MSG,
|
90
|
-
prefer: "#{node.method_name}!",
|
91
|
-
current: node.method_name.to_s))
|
147
|
+
add_offense_for_node(node)
|
92
148
|
end
|
93
149
|
|
94
150
|
def autocorrect(node)
|
@@ -100,10 +156,19 @@ module RuboCop
|
|
100
156
|
|
101
157
|
private
|
102
158
|
|
159
|
+
def add_offense_for_node(node, msg = MSG)
|
160
|
+
name = node.method_name
|
161
|
+
full_message = format(msg, prefer: "#{name}!", current: name.to_s)
|
162
|
+
|
163
|
+
add_offense(node, location: :selector, message: full_message)
|
164
|
+
end
|
165
|
+
|
103
166
|
def right_assignment_node(assignment)
|
104
167
|
node = assignment.node.child_nodes.first
|
168
|
+
|
105
169
|
return node unless node && node.block_type?
|
106
|
-
|
170
|
+
|
171
|
+
node.send_node
|
107
172
|
end
|
108
173
|
|
109
174
|
def persisted_referenced?(assignment)
|
@@ -118,36 +183,122 @@ module RuboCop
|
|
118
183
|
node.send_type? && node.method?(:persisted?)
|
119
184
|
end
|
120
185
|
|
186
|
+
def assignable_node(node)
|
187
|
+
assignable = node.block_node || node
|
188
|
+
while node
|
189
|
+
node = hash_parent(node) || array_parent(node)
|
190
|
+
assignable = node if node
|
191
|
+
end
|
192
|
+
assignable
|
193
|
+
end
|
194
|
+
|
195
|
+
def hash_parent(node)
|
196
|
+
pair = node.parent
|
197
|
+
return unless pair && pair.pair_type?
|
198
|
+
|
199
|
+
hash = pair.parent
|
200
|
+
return unless hash && hash.hash_type?
|
201
|
+
|
202
|
+
hash
|
203
|
+
end
|
204
|
+
|
205
|
+
def array_parent(node)
|
206
|
+
array = node.parent
|
207
|
+
return unless array && array.array_type?
|
208
|
+
|
209
|
+
array
|
210
|
+
end
|
211
|
+
|
121
212
|
def check_used_in_conditional(node)
|
122
213
|
return false unless conditional?(node)
|
123
214
|
|
124
215
|
unless MODIFY_PERSIST_METHODS.include?(node.method_name)
|
125
|
-
|
126
|
-
message: format(CREATE_CONDITIONAL_MSG,
|
127
|
-
method: node.method_name.to_s))
|
216
|
+
add_offense_for_node(node, CREATE_CONDITIONAL_MSG)
|
128
217
|
end
|
129
218
|
|
130
219
|
true
|
131
220
|
end
|
132
221
|
|
133
|
-
def conditional?(node)
|
134
|
-
node.
|
135
|
-
|
136
|
-
|
137
|
-
|
222
|
+
def conditional?(node) # rubocop:disable Metrics/CyclomaticComplexity
|
223
|
+
node = node.block_node || node
|
224
|
+
|
225
|
+
condition = node.parent
|
226
|
+
return false unless condition
|
227
|
+
|
228
|
+
condition.if_type? || condition.case_type? ||
|
229
|
+
condition.or_type? || condition.and_type? ||
|
230
|
+
single_negative?(condition)
|
231
|
+
end
|
232
|
+
|
233
|
+
def allowed_receiver?(node)
|
234
|
+
return false unless node.receiver
|
235
|
+
return false unless cop_config['AllowedReceivers']
|
236
|
+
|
237
|
+
cop_config['AllowedReceivers'].any? do |allowed_receiver|
|
238
|
+
receiver_chain_matches?(node, allowed_receiver)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def receiver_chain_matches?(node, allowed_receiver)
|
243
|
+
allowed_receiver.split('.').reverse.all? do |receiver_part|
|
244
|
+
node = node.receiver
|
245
|
+
return false unless node
|
246
|
+
|
247
|
+
if node.variable?
|
248
|
+
node.node_parts.first == receiver_part.to_sym
|
249
|
+
elsif node.send_type?
|
250
|
+
node.method_name == receiver_part.to_sym
|
251
|
+
elsif node.const_type?
|
252
|
+
const_matches?(node.const_name, receiver_part)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
# Const == Const
|
258
|
+
# ::Const == ::Const
|
259
|
+
# ::Const == Const
|
260
|
+
# Const == ::Const
|
261
|
+
# NameSpace::Const == Const
|
262
|
+
# NameSpace::Const == NameSpace::Const
|
263
|
+
# NameSpace::Const != ::Const
|
264
|
+
# Const != NameSpace::Const
|
265
|
+
def const_matches?(const, allowed_const)
|
266
|
+
parts = allowed_const.split('::').reverse.zip(
|
267
|
+
const.split('::').reverse
|
138
268
|
)
|
269
|
+
parts.all? do |(allowed_part, const_part)|
|
270
|
+
allowed_part == const_part.to_s
|
271
|
+
end
|
139
272
|
end
|
140
273
|
|
141
|
-
def
|
142
|
-
|
274
|
+
def implicit_return?(node)
|
275
|
+
return false unless cop_config['AllowImplicitReturn']
|
276
|
+
|
277
|
+
node = assignable_node(node)
|
278
|
+
method = node.parent
|
279
|
+
return unless method && (method.def_type? || method.block_type?)
|
280
|
+
|
281
|
+
method.children.size == node.sibling_index + 1
|
282
|
+
end
|
283
|
+
|
284
|
+
def argument?(node)
|
285
|
+
assignable_node(node).argument?
|
286
|
+
end
|
287
|
+
|
288
|
+
def explicit_return?(node)
|
289
|
+
ret = assignable_node(node).parent
|
290
|
+
ret && (ret.return_type? || ret.next_type?)
|
143
291
|
end
|
144
292
|
|
145
|
-
# Ignore simple assignment or if condition
|
146
293
|
def return_value_assigned?(node)
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
294
|
+
assignment = assignable_node(node).parent
|
295
|
+
assignment && assignment.lvasgn_type?
|
296
|
+
end
|
297
|
+
|
298
|
+
def persist_method?(node, methods = PERSIST_METHODS)
|
299
|
+
methods.include?(node.method_name) &&
|
300
|
+
expected_signature?(node) &&
|
301
|
+
!allowed_receiver?(node)
|
151
302
|
end
|
152
303
|
|
153
304
|
# Check argument signature as no arguments or one hash
|