rubocop 0.58.2 → 0.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|