rubocop 0.93.1 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +36 -16
- data/config/default.yml +207 -72
- data/exe/rubocop +1 -1
- data/lib/rubocop.rb +16 -2
- data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
- data/lib/rubocop/cli/command/version.rb +1 -1
- data/lib/rubocop/comment_config.rb +1 -1
- data/lib/rubocop/config.rb +4 -0
- data/lib/rubocop/config_loader.rb +33 -7
- data/lib/rubocop/config_loader_resolver.rb +7 -5
- data/lib/rubocop/config_validator.rb +7 -6
- data/lib/rubocop/cop/badge.rb +9 -24
- data/lib/rubocop/cop/base.rb +16 -1
- data/lib/rubocop/cop/bundler/duplicated_gem.rb +26 -6
- data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
- data/lib/rubocop/cop/commissioner.rb +37 -23
- data/lib/rubocop/cop/corrector.rb +3 -1
- data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +1 -1
- data/lib/rubocop/cop/force.rb +1 -1
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +3 -3
- data/lib/rubocop/cop/gemspec/required_ruby_version.rb +4 -5
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +1 -1
- data/lib/rubocop/cop/generator.rb +2 -9
- data/lib/rubocop/cop/generator/configuration_injector.rb +1 -1
- data/lib/rubocop/cop/layout/block_alignment.rb +3 -4
- data/lib/rubocop/cop/layout/class_structure.rb +22 -3
- data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/else_alignment.rb +15 -2
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -0
- data/lib/rubocop/cop/layout/end_alignment.rb +3 -3
- data/lib/rubocop/cop/layout/extra_spacing.rb +1 -2
- data/lib/rubocop/cop/layout/hash_alignment.rb +4 -4
- data/lib/rubocop/cop/layout/line_length.rb +8 -1
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +24 -18
- data/lib/rubocop/cop/layout/space_around_operators.rb +4 -1
- data/lib/rubocop/cop/layout/space_inside_parens.rb +35 -13
- data/lib/rubocop/cop/layout/trailing_whitespace.rb +37 -13
- data/lib/rubocop/cop/lint/constant_definition_in_block.rb +26 -2
- data/lib/rubocop/cop/lint/debugger.rb +17 -28
- data/lib/rubocop/cop/lint/duplicate_branch.rb +93 -0
- data/lib/rubocop/cop/lint/duplicate_case_condition.rb +2 -12
- data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +77 -0
- data/lib/rubocop/cop/lint/else_layout.rb +29 -3
- data/lib/rubocop/cop/lint/empty_block.rb +82 -0
- data/lib/rubocop/cop/lint/empty_class.rb +93 -0
- data/lib/rubocop/cop/lint/flip_flop.rb +8 -2
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +38 -6
- data/lib/rubocop/cop/lint/loop.rb +4 -4
- data/lib/rubocop/cop/lint/nested_percent_literal.rb +14 -0
- data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +58 -0
- data/lib/rubocop/cop/lint/number_conversion.rb +46 -13
- data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +27 -8
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +19 -16
- data/lib/rubocop/cop/lint/shadowed_exception.rb +4 -5
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +86 -0
- data/lib/rubocop/cop/lint/to_json.rb +1 -1
- data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +185 -0
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
- data/lib/rubocop/cop/lint/useless_method_definition.rb +2 -4
- data/lib/rubocop/cop/lint/useless_setter_call.rb +6 -1
- data/lib/rubocop/cop/metrics/parameter_lists.rb +4 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
- data/lib/rubocop/cop/mixin/configurable_numbering.rb +3 -3
- data/lib/rubocop/cop/mixin/line_length_help.rb +1 -1
- data/lib/rubocop/cop/mixin/statement_modifier.rb +9 -4
- data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +12 -2
- data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +11 -5
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +67 -18
- data/lib/rubocop/cop/naming/predicate_name.rb +2 -1
- data/lib/rubocop/cop/naming/variable_number.rb +98 -8
- data/lib/rubocop/cop/offense.rb +3 -3
- data/lib/rubocop/cop/security/open.rb +12 -10
- data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
- data/lib/rubocop/cop/style/and_or.rb +1 -3
- data/lib/rubocop/cop/style/arguments_forwarding.rb +142 -0
- data/lib/rubocop/cop/style/bisected_attr_accessor.rb +0 -4
- data/lib/rubocop/cop/style/case_like_if.rb +0 -4
- data/lib/rubocop/cop/style/collection_compact.rb +91 -0
- data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +169 -0
- data/lib/rubocop/cop/style/double_negation.rb +6 -1
- data/lib/rubocop/cop/style/format_string_token.rb +47 -2
- data/lib/rubocop/cop/style/hash_syntax.rb +3 -3
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +7 -2
- data/lib/rubocop/cop/style/if_inside_else.rb +37 -1
- data/lib/rubocop/cop/style/if_unless_modifier.rb +7 -3
- data/lib/rubocop/cop/style/infinite_loop.rb +4 -0
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +10 -13
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -11
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +7 -11
- data/lib/rubocop/cop/style/mixin_grouping.rb +0 -4
- data/lib/rubocop/cop/style/multiple_comparison.rb +55 -7
- data/lib/rubocop/cop/style/negated_if_else_condition.rb +106 -0
- data/lib/rubocop/cop/style/nil_lambda.rb +52 -0
- data/lib/rubocop/cop/style/raise_args.rb +21 -6
- data/lib/rubocop/cop/style/redundant_parentheses.rb +4 -0
- data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +7 -1
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
- data/lib/rubocop/cop/style/redundant_self.rb +3 -0
- data/lib/rubocop/cop/style/safe_navigation.rb +16 -4
- data/lib/rubocop/cop/style/semicolon.rb +3 -0
- data/lib/rubocop/cop/style/static_class.rb +97 -0
- data/lib/rubocop/cop/style/string_concatenation.rb +13 -1
- data/lib/rubocop/cop/style/swap_values.rb +108 -0
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +3 -1
- data/lib/rubocop/cop/style/while_until_modifier.rb +9 -0
- data/lib/rubocop/cop/team.rb +6 -1
- data/lib/rubocop/cop/util.rb +5 -1
- data/lib/rubocop/ext/regexp_node.rb +17 -9
- data/lib/rubocop/ext/regexp_parser.rb +84 -0
- data/lib/rubocop/formatter/formatter_set.rb +2 -1
- data/lib/rubocop/formatter/git_hub_actions_formatter.rb +47 -0
- data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
- data/lib/rubocop/magic_comment.rb +2 -2
- data/lib/rubocop/options.rb +6 -1
- data/lib/rubocop/rspec/shared_contexts.rb +4 -0
- data/lib/rubocop/target_finder.rb +1 -1
- data/lib/rubocop/target_ruby.rb +65 -1
- data/lib/rubocop/version.rb +56 -6
- metadata +22 -6
@@ -3,10 +3,14 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Lint
|
6
|
-
# This cop checks for odd else block layout - like
|
7
|
-
# having an expression on the same line as the else keyword,
|
6
|
+
# This cop checks for odd `else` block layout - like
|
7
|
+
# having an expression on the same line as the `else` keyword,
|
8
8
|
# which is usually a mistake.
|
9
9
|
#
|
10
|
+
# Its auto-correction tweaks layout to keep the syntax. So, this auto-correction
|
11
|
+
# is compatible correction for bad case syntax, but if your code makes a mistake
|
12
|
+
# with `elsif` and `else`, you will have to correct it manually.
|
13
|
+
#
|
10
14
|
# @example
|
11
15
|
#
|
12
16
|
# # bad
|
@@ -21,13 +25,25 @@ module RuboCop
|
|
21
25
|
#
|
22
26
|
# # good
|
23
27
|
#
|
28
|
+
# # This code is compatible with the bad case. It will be auto-corrected like this.
|
24
29
|
# if something
|
25
30
|
# # ...
|
26
31
|
# else
|
27
32
|
# do_this
|
28
33
|
# do_that
|
29
34
|
# end
|
35
|
+
#
|
36
|
+
# # This code is incompatible with the bad case.
|
37
|
+
# # If `do_this` is a condition, `elsif` should be used instead of `else`.
|
38
|
+
# if something
|
39
|
+
# # ...
|
40
|
+
# elsif do_this
|
41
|
+
# do_that
|
42
|
+
# end
|
30
43
|
class ElseLayout < Base
|
44
|
+
include RangeHelp
|
45
|
+
extend AutoCorrector
|
46
|
+
|
31
47
|
MSG = 'Odd `else` layout detected. Did you mean to use `elsif`?'
|
32
48
|
|
33
49
|
def on_if(node)
|
@@ -58,7 +74,17 @@ module RuboCop
|
|
58
74
|
return unless first_else
|
59
75
|
return unless first_else.source_range.line == node.loc.else.line
|
60
76
|
|
61
|
-
add_offense(first_else)
|
77
|
+
add_offense(first_else) do |corrector|
|
78
|
+
autocorrect(corrector, node, first_else)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def autocorrect(corrector, node, first_else)
|
83
|
+
corrector.insert_after(node.loc.else, "\n")
|
84
|
+
|
85
|
+
blank_range = range_between(node.loc.else.end_pos, first_else.loc.expression.begin_pos)
|
86
|
+
indentation = indent(node.else_branch.children[1])
|
87
|
+
corrector.replace(blank_range, indentation)
|
62
88
|
end
|
63
89
|
end
|
64
90
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# This cop checks for blocks without a body.
|
7
|
+
# Such empty blocks are typically an oversight or we should provide a comment
|
8
|
+
# be clearer what we're aiming for.
|
9
|
+
#
|
10
|
+
# Empty lambdas are ignored by default.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# # bad
|
14
|
+
# items.each { |item| }
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# items.each { |item| puts item }
|
18
|
+
#
|
19
|
+
# @example AllowComments: true (default)
|
20
|
+
# # good
|
21
|
+
# items.each do |item|
|
22
|
+
# # TODO: implement later (inner comment)
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# items.each { |item| } # TODO: implement later (inline comment)
|
26
|
+
#
|
27
|
+
# @example AllowComments: false
|
28
|
+
# # bad
|
29
|
+
# items.each do |item|
|
30
|
+
# # TODO: implement later (inner comment)
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# items.each { |item| } # TODO: implement later (inline comment)
|
34
|
+
#
|
35
|
+
# @example AllowEmptyLambdas: true (default)
|
36
|
+
# # good
|
37
|
+
# allow(subject).to receive(:callable).and_return(-> {})
|
38
|
+
#
|
39
|
+
# placeholder = lambda do
|
40
|
+
# end
|
41
|
+
# (callable || placeholder).call
|
42
|
+
#
|
43
|
+
# @example AllowEmptyLambdas: false
|
44
|
+
# # bad
|
45
|
+
# allow(subject).to receive(:callable).and_return(-> {})
|
46
|
+
#
|
47
|
+
# placeholder = lambda do
|
48
|
+
# end
|
49
|
+
# (callable || placeholder).call
|
50
|
+
#
|
51
|
+
class EmptyBlock < Base
|
52
|
+
MSG = 'Empty block detected.'
|
53
|
+
|
54
|
+
def on_block(node)
|
55
|
+
return if node.body
|
56
|
+
return if allow_empty_lambdas? && node.lambda?
|
57
|
+
return if cop_config['AllowComments'] && allow_comment?(node)
|
58
|
+
|
59
|
+
add_offense(node)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def allow_comment?(node)
|
65
|
+
return false unless processed_source.contains_comment?(node.source_range)
|
66
|
+
|
67
|
+
line_comment = processed_source.comment_at_line(node.source_range.line)
|
68
|
+
!line_comment || !comment_disables_cop?(line_comment.loc.expression.source)
|
69
|
+
end
|
70
|
+
|
71
|
+
def allow_empty_lambdas?
|
72
|
+
cop_config['AllowEmptyLambdas']
|
73
|
+
end
|
74
|
+
|
75
|
+
def comment_disables_cop?(comment)
|
76
|
+
regexp_pattern = "# rubocop : (disable|todo) ([^,],)* (all|#{cop_name})"
|
77
|
+
Regexp.new(regexp_pattern.gsub(' ', '\s*')).match?(comment)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# This cop checks for classes and metaclasses without a body.
|
7
|
+
# Such empty classes and metaclasses are typically an oversight or we should provide a comment
|
8
|
+
# to be clearer what we're aiming for.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# # bad
|
12
|
+
# class Foo
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# class Bar
|
16
|
+
# class << self
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# class << obj
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# class Foo
|
25
|
+
# def do_something
|
26
|
+
# # ... code
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# class Bar
|
31
|
+
# class << self
|
32
|
+
# attr_reader :bar
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# class << obj
|
37
|
+
# attr_reader :bar
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# @example AllowComments: false (default)
|
41
|
+
# # bad
|
42
|
+
# class Foo
|
43
|
+
# # TODO: implement later
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# class Bar
|
47
|
+
# class << self
|
48
|
+
# # TODO: implement later
|
49
|
+
# end
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# class << obj
|
53
|
+
# # TODO: implement later
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# @example AllowComments: true
|
57
|
+
# # good
|
58
|
+
# class Foo
|
59
|
+
# # TODO: implement later
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# class Bar
|
63
|
+
# class << self
|
64
|
+
# # TODO: implement later
|
65
|
+
# end
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# class << obj
|
69
|
+
# # TODO: implement later
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
class EmptyClass < Base
|
73
|
+
CLASS_MSG = 'Empty class detected.'
|
74
|
+
METACLASS_MSG = 'Empty metaclass detected.'
|
75
|
+
|
76
|
+
def on_class(node)
|
77
|
+
add_offense(node, message: CLASS_MSG) unless body_or_allowed_comment_lines?(node) ||
|
78
|
+
node.parent_class
|
79
|
+
end
|
80
|
+
|
81
|
+
def on_sclass(node)
|
82
|
+
add_offense(node, message: METACLASS_MSG) unless body_or_allowed_comment_lines?(node)
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def body_or_allowed_comment_lines?(node)
|
88
|
+
node.body || (cop_config['AllowComments'] && comment_lines?(node))
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -3,8 +3,14 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Lint
|
6
|
-
# This cop looks for uses of flip-flop operator
|
7
|
-
#
|
6
|
+
# This cop looks for uses of flip-flop operator
|
7
|
+
# based on the Ruby Style Guide.
|
8
|
+
#
|
9
|
+
# Here is the history of flip-flops in Ruby.
|
10
|
+
# flip-flop operator is deprecated in Ruby 2.6.0 and
|
11
|
+
# the deprecation has been reverted by Ruby 2.7.0 and
|
12
|
+
# backported to Ruby 2.6.
|
13
|
+
# See: https://bugs.ruby-lang.org/issues/5400
|
8
14
|
#
|
9
15
|
# @example
|
10
16
|
# # bad
|
@@ -27,21 +27,33 @@ module RuboCop
|
|
27
27
|
|
28
28
|
def on_interpolation(begin_node)
|
29
29
|
final_node = begin_node.children.last
|
30
|
-
return unless final_node
|
31
|
-
|
32
|
-
|
30
|
+
return unless offending?(final_node)
|
31
|
+
|
32
|
+
# %W and %I split the content into words before expansion
|
33
|
+
# treating each interpolation as a word component, so
|
34
|
+
# interpolation should not be removed if the expanded value
|
35
|
+
# contains a space character.
|
36
|
+
expanded_value = autocorrected_value(final_node)
|
37
|
+
return if in_array_percent_literal?(begin_node) &&
|
38
|
+
/\s/.match?(expanded_value)
|
33
39
|
|
34
40
|
add_offense(final_node) do |corrector|
|
35
41
|
return if final_node.dstr_type? # nested, fixed in next iteration
|
36
42
|
|
37
|
-
|
38
|
-
|
39
|
-
corrector.replace(final_node.parent, value)
|
43
|
+
corrector.replace(final_node.parent, expanded_value)
|
40
44
|
end
|
41
45
|
end
|
42
46
|
|
43
47
|
private
|
44
48
|
|
49
|
+
def offending?(node)
|
50
|
+
node &&
|
51
|
+
!special_keyword?(node) &&
|
52
|
+
prints_as_self?(node) &&
|
53
|
+
# Special case for Layout/TrailingWhitespace
|
54
|
+
!(space_literal?(node) && ends_heredoc_line?(node))
|
55
|
+
end
|
56
|
+
|
45
57
|
def special_keyword?(node)
|
46
58
|
# handle strings like __FILE__
|
47
59
|
(node.str_type? && !node.loc.respond_to?(:begin)) ||
|
@@ -92,6 +104,26 @@ module RuboCop
|
|
92
104
|
(COMPOSITE.include?(node.type) &&
|
93
105
|
node.children.all? { |child| prints_as_self?(child) })
|
94
106
|
end
|
107
|
+
|
108
|
+
def space_literal?(node)
|
109
|
+
node.str_type? && node.value.blank?
|
110
|
+
end
|
111
|
+
|
112
|
+
def ends_heredoc_line?(node)
|
113
|
+
grandparent = node.parent.parent
|
114
|
+
return false unless grandparent&.dstr_type? && grandparent&.heredoc?
|
115
|
+
|
116
|
+
line = processed_source.lines[node.last_line - 1]
|
117
|
+
line.size == node.loc.last_column + 1
|
118
|
+
end
|
119
|
+
|
120
|
+
def in_array_percent_literal?(node)
|
121
|
+
parent = node.parent
|
122
|
+
return false unless parent.dstr_type? || parent.dsym_type?
|
123
|
+
|
124
|
+
grandparent = parent.parent
|
125
|
+
grandparent&.array_type? && grandparent&.percent_literal?
|
126
|
+
end
|
95
127
|
end
|
96
128
|
end
|
97
129
|
end
|
@@ -5,6 +5,10 @@ module RuboCop
|
|
5
5
|
module Lint
|
6
6
|
# This cop checks for uses of `begin...end while/until something`.
|
7
7
|
#
|
8
|
+
# The cop is marked as unsafe because behaviour can change in some cases, including
|
9
|
+
# if a local variable inside the loop body is accessed outside of it, or if the
|
10
|
+
# loop body raises a `StopIteration` exception (which `Kernel#loop` rescues).
|
11
|
+
#
|
8
12
|
# @example
|
9
13
|
#
|
10
14
|
# # bad
|
@@ -76,10 +80,6 @@ module RuboCop
|
|
76
80
|
conditional_keyword = node.while_post_type? ? 'unless' : 'if'
|
77
81
|
"break #{conditional_keyword} #{node.condition.source}\n#{indent(node)}"
|
78
82
|
end
|
79
|
-
|
80
|
-
def indent(node)
|
81
|
-
' ' * node.loc.column
|
82
|
-
end
|
83
83
|
end
|
84
84
|
end
|
85
85
|
end
|
@@ -15,6 +15,20 @@ module RuboCop
|
|
15
15
|
# valid_attributes: %i[name content],
|
16
16
|
# nested_attributes: %i[name content %i[incorrectly nested]]
|
17
17
|
# }
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
#
|
21
|
+
# # Neither is incompatible with the bad case, but probably the intended code.
|
22
|
+
# attributes = {
|
23
|
+
# valid_attributes: %i[name content],
|
24
|
+
# nested_attributes: [:name, :content, %i[incorrectly nested]]
|
25
|
+
# }
|
26
|
+
#
|
27
|
+
# attributes = {
|
28
|
+
# valid_attributes: %i[name content],
|
29
|
+
# nested_attributes: [:name, :content, [:incorrectly, :nested]]
|
30
|
+
# }
|
31
|
+
#
|
18
32
|
class NestedPercentLiteral < Base
|
19
33
|
include PercentLiteral
|
20
34
|
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# Checks for the presence of a `return` inside a `begin..end` block
|
7
|
+
# in assignment contexts.
|
8
|
+
# In this situation, the `return` will result in an exit from the current
|
9
|
+
# method, possibly leading to unexpected behavior.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
#
|
13
|
+
# # bad
|
14
|
+
#
|
15
|
+
# @some_variable ||= begin
|
16
|
+
# return some_value if some_condition_is_met
|
17
|
+
#
|
18
|
+
# do_something
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
#
|
25
|
+
# @some_variable ||= begin
|
26
|
+
# if some_condition_is_met
|
27
|
+
# some_value
|
28
|
+
# else
|
29
|
+
# do_something
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# # good
|
34
|
+
#
|
35
|
+
# some_variable = if some_condition_is_met
|
36
|
+
# return if another_condition_is_met
|
37
|
+
#
|
38
|
+
# some_value
|
39
|
+
# else
|
40
|
+
# do_something
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
class NoReturnInBeginEndBlocks < Cop
|
44
|
+
MSG = 'Do not `return` in `begin..end` blocks in assignment contexts.'
|
45
|
+
|
46
|
+
def on_lvasgn(node)
|
47
|
+
node.each_node(:kwbegin) do |kwbegin_node|
|
48
|
+
kwbegin_node.each_node(:return) do |return_node|
|
49
|
+
add_offense(return_node)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
alias on_or_asgn on_lvasgn
|
54
|
+
alias on_op_asgn on_lvasgn
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -7,6 +7,17 @@ module RuboCop
|
|
7
7
|
# number conversion can cause unexpected error if auto type conversion
|
8
8
|
# fails. Cop prefer parsing with number class instead.
|
9
9
|
#
|
10
|
+
# Conversion with `Integer`, `Float`, etc. will raise an `ArgumentError`
|
11
|
+
# if given input that is not numeric (eg. an empty string), whereas
|
12
|
+
# `to_i`, etc. will try to convert regardless of input (`''.to_i => 0`).
|
13
|
+
# As such, this cop is disabled by default because it's not necessarily
|
14
|
+
# always correct to raise if a value is not numeric.
|
15
|
+
#
|
16
|
+
# NOTE: Some values cannot be converted properly using one of the `Kernel`
|
17
|
+
# method (for instance, `Time` and `DateTime` values are allowed by this
|
18
|
+
# cop by default). Similarly, Rails' duration methods do not work well
|
19
|
+
# with `Integer()` and can be ignored with `IgnoredMethods`.
|
20
|
+
#
|
10
21
|
# @example
|
11
22
|
#
|
12
23
|
# # bad
|
@@ -20,8 +31,19 @@ module RuboCop
|
|
20
31
|
# Integer('10', 10)
|
21
32
|
# Float('10.2')
|
22
33
|
# Complex('10')
|
34
|
+
#
|
35
|
+
# @example IgnoredMethods: [minutes]
|
36
|
+
#
|
37
|
+
# # good
|
38
|
+
# 10.minutes.to_i
|
39
|
+
#
|
40
|
+
# @example IgnoredClasses: [Time, DateTime] (default)
|
41
|
+
#
|
42
|
+
# # good
|
43
|
+
# Time.now.to_datetime.to_i
|
23
44
|
class NumberConversion < Base
|
24
45
|
extend AutoCorrector
|
46
|
+
include IgnoredMethods
|
25
47
|
|
26
48
|
CONVERSION_METHOD_CLASS_MAPPING = {
|
27
49
|
to_i: "#{Integer.name}(%<number_object>s, 10)",
|
@@ -38,13 +60,9 @@ module RuboCop
|
|
38
60
|
(send $_ ${:to_i :to_f :to_c})
|
39
61
|
PATTERN
|
40
62
|
|
41
|
-
def_node_matcher :datetime?, <<~PATTERN
|
42
|
-
(send (const {nil? (cbase)} {:Time :DateTime}) ...)
|
43
|
-
PATTERN
|
44
|
-
|
45
63
|
def on_send(node)
|
46
64
|
to_method(node) do |receiver, to_method|
|
47
|
-
next if receiver.nil? ||
|
65
|
+
next if receiver.nil? || ignore_receiver?(receiver)
|
48
66
|
|
49
67
|
message = format(
|
50
68
|
MSG,
|
@@ -60,18 +78,33 @@ module RuboCop
|
|
60
78
|
|
61
79
|
private
|
62
80
|
|
63
|
-
def
|
64
|
-
|
65
|
-
|
66
|
-
|
81
|
+
def correct_method(node, receiver)
|
82
|
+
format(CONVERSION_METHOD_CLASS_MAPPING[node.method_name],
|
83
|
+
number_object: receiver.source)
|
84
|
+
end
|
67
85
|
|
68
|
-
|
86
|
+
def ignore_receiver?(receiver)
|
87
|
+
if receiver.send_type? && ignored_method?(receiver.method_name)
|
88
|
+
true
|
89
|
+
elsif (receiver = top_receiver(receiver))
|
90
|
+
receiver.const_type? && ignored_class?(receiver.const_name)
|
91
|
+
else
|
92
|
+
false
|
69
93
|
end
|
70
94
|
end
|
71
95
|
|
72
|
-
def
|
73
|
-
|
74
|
-
|
96
|
+
def top_receiver(node)
|
97
|
+
receiver = node
|
98
|
+
receiver = receiver.receiver until receiver.receiver.nil?
|
99
|
+
receiver
|
100
|
+
end
|
101
|
+
|
102
|
+
def ignored_classes
|
103
|
+
cop_config.fetch('IgnoredClasses', [])
|
104
|
+
end
|
105
|
+
|
106
|
+
def ignored_class?(name)
|
107
|
+
ignored_classes.include?(name.to_s)
|
75
108
|
end
|
76
109
|
end
|
77
110
|
end
|