rubocop 1.31.2 → 1.34.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/config/default.yml +74 -16
  4. data/config/obsoletion.yml +23 -1
  5. data/lib/rubocop/cache_config.rb +29 -0
  6. data/lib/rubocop/cli/command/auto_genenerate_config.rb +2 -2
  7. data/lib/rubocop/cli/command/init_dotfile.rb +1 -1
  8. data/lib/rubocop/cli/command/suggest_extensions.rb +53 -15
  9. data/lib/rubocop/cli.rb +1 -0
  10. data/lib/rubocop/config.rb +1 -1
  11. data/lib/rubocop/config_finder.rb +68 -0
  12. data/lib/rubocop/config_loader.rb +12 -40
  13. data/lib/rubocop/config_loader_resolver.rb +1 -5
  14. data/lib/rubocop/config_obsoletion/changed_parameter.rb +5 -0
  15. data/lib/rubocop/config_obsoletion/parameter_rule.rb +4 -0
  16. data/lib/rubocop/config_obsoletion.rb +7 -2
  17. data/lib/rubocop/cop/cop.rb +1 -1
  18. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +28 -0
  19. data/lib/rubocop/cop/internal_affairs/single_line_comparison.rb +61 -0
  20. data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +7 -1
  21. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  22. data/lib/rubocop/cop/layout/block_end_newline.rb +33 -5
  23. data/lib/rubocop/cop/layout/first_argument_indentation.rb +6 -1
  24. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +57 -13
  25. data/lib/rubocop/cop/layout/line_length.rb +2 -0
  26. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -1
  27. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +4 -1
  28. data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +45 -0
  29. data/lib/rubocop/cop/legacy/corrections_proxy.rb +1 -1
  30. data/lib/rubocop/cop/legacy/corrector.rb +1 -1
  31. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +26 -6
  32. data/lib/rubocop/cop/lint/debugger.rb +26 -16
  33. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +10 -4
  34. data/lib/rubocop/cop/lint/empty_conditional_body.rb +65 -1
  35. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +55 -24
  36. data/lib/rubocop/cop/lint/number_conversion.rb +28 -6
  37. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +7 -0
  38. data/lib/rubocop/cop/lint/require_range_parentheses.rb +57 -0
  39. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +35 -1
  40. data/lib/rubocop/cop/lint/shadowed_exception.rb +15 -0
  41. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +9 -1
  42. data/lib/rubocop/cop/metrics/abc_size.rb +3 -1
  43. data/lib/rubocop/cop/metrics/block_length.rb +6 -6
  44. data/lib/rubocop/cop/metrics/method_length.rb +8 -7
  45. data/lib/rubocop/cop/mixin/allowed_methods.rb +15 -1
  46. data/lib/rubocop/cop/mixin/allowed_pattern.rb +9 -1
  47. data/lib/rubocop/cop/mixin/check_line_breakable.rb +4 -0
  48. data/lib/rubocop/cop/mixin/comments_help.rb +5 -1
  49. data/lib/rubocop/cop/mixin/enforce_superclass.rb +2 -1
  50. data/lib/rubocop/cop/mixin/method_complexity.rb +4 -9
  51. data/lib/rubocop/cop/mixin/percent_array.rb +60 -1
  52. data/lib/rubocop/cop/mixin/range_help.rb +2 -2
  53. data/lib/rubocop/cop/naming/predicate_name.rb +30 -1
  54. data/lib/rubocop/cop/style/block_delimiters.rb +26 -7
  55. data/lib/rubocop/cop/style/class_and_module_children.rb +4 -4
  56. data/lib/rubocop/cop/style/class_equality_comparison.rb +50 -3
  57. data/lib/rubocop/cop/style/double_negation.rb +2 -0
  58. data/lib/rubocop/cop/style/empty_else.rb +37 -0
  59. data/lib/rubocop/cop/style/empty_heredoc.rb +73 -0
  60. data/lib/rubocop/cop/style/fetch_env_var.rb +10 -177
  61. data/lib/rubocop/cop/style/format_string_token.rb +25 -6
  62. data/lib/rubocop/cop/style/hash_except.rb +0 -4
  63. data/lib/rubocop/cop/style/if_unless_modifier.rb +1 -1
  64. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +2 -0
  65. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +5 -1
  66. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -7
  67. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +19 -2
  68. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +9 -0
  69. data/lib/rubocop/cop/style/numeric_literals.rb +16 -1
  70. data/lib/rubocop/cop/style/numeric_predicate.rb +43 -9
  71. data/lib/rubocop/cop/style/redundant_condition.rb +19 -4
  72. data/lib/rubocop/cop/style/redundant_parentheses.rb +15 -22
  73. data/lib/rubocop/cop/style/redundant_sort.rb +21 -6
  74. data/lib/rubocop/cop/style/semicolon.rb +27 -3
  75. data/lib/rubocop/cop/style/sole_nested_conditional.rb +14 -3
  76. data/lib/rubocop/cop/style/symbol_array.rb +2 -3
  77. data/lib/rubocop/cop/style/symbol_proc.rb +40 -7
  78. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -13
  79. data/lib/rubocop/cop/style/top_level_method_definition.rb +2 -0
  80. data/lib/rubocop/cop/style/trivial_accessors.rb +3 -0
  81. data/lib/rubocop/cop/style/word_array.rb +2 -3
  82. data/lib/rubocop/ext/range.rb +15 -0
  83. data/lib/rubocop/feature_loader.rb +88 -0
  84. data/lib/rubocop/formatter/clang_style_formatter.rb +1 -1
  85. data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -1
  86. data/lib/rubocop/formatter/html_formatter.rb +1 -1
  87. data/lib/rubocop/formatter/markdown_formatter.rb +1 -1
  88. data/lib/rubocop/formatter/tap_formatter.rb +1 -1
  89. data/lib/rubocop/options.rb +3 -6
  90. data/lib/rubocop/result_cache.rb +22 -20
  91. data/lib/rubocop/rspec/shared_contexts.rb +14 -14
  92. data/lib/rubocop/rspec/support.rb +14 -0
  93. data/lib/rubocop/runner.rb +4 -0
  94. data/lib/rubocop/server/cache.rb +33 -1
  95. data/lib/rubocop/server/cli.rb +19 -2
  96. data/lib/rubocop/server/client_command/base.rb +1 -1
  97. data/lib/rubocop/version.rb +1 -1
  98. data/lib/rubocop.rb +5 -1
  99. metadata +16 -9
  100. data/lib/rubocop/cop/mixin/ignored_methods.rb +0 -52
@@ -4,6 +4,14 @@ module RuboCop
4
4
  module Cop
5
5
  module Lint
6
6
  # Checks for the presence of `if`, `elsif` and `unless` branches without a body.
7
+ #
8
+ # NOTE: empty `else` branches are handled by `Style/EmptyElse`.
9
+ #
10
+ # @safety
11
+ # Autocorrection for this cop is not safe. The conditions for empty branches that
12
+ # the autocorrection removes may have side effects, or the logic in subsequent
13
+ # branches may change due to the removal of a previous condition.
14
+ #
7
15
  # @example
8
16
  # # bad
9
17
  # if condition
@@ -53,7 +61,9 @@ module RuboCop
53
61
  # end
54
62
  #
55
63
  class EmptyConditionalBody < Base
64
+ extend AutoCorrector
56
65
  include CommentsHelp
66
+ include RangeHelp
57
67
 
58
68
  MSG = 'Avoid `%<keyword>s` branches without a body.'
59
69
 
@@ -61,7 +71,61 @@ module RuboCop
61
71
  return if node.body
62
72
  return if cop_config['AllowComments'] && contains_comments?(node)
63
73
 
64
- add_offense(node, message: format(MSG, keyword: node.keyword))
74
+ add_offense(node, message: format(MSG, keyword: node.keyword)) do |corrector|
75
+ autocorrect(corrector, node)
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ def autocorrect(corrector, node)
82
+ remove_comments(corrector, node)
83
+ remove_empty_branch(corrector, node)
84
+ correct_other_branches(corrector, node)
85
+ end
86
+
87
+ def remove_comments(corrector, node)
88
+ comments_in_range(node).each do |comment|
89
+ range = range_by_whole_lines(comment.loc.expression, include_final_newline: true)
90
+ corrector.remove(range)
91
+ end
92
+ end
93
+
94
+ def remove_empty_branch(corrector, node)
95
+ corrector.remove(deletion_range(branch_range(node)))
96
+ end
97
+
98
+ def correct_other_branches(corrector, node)
99
+ return unless (node.if? || node.unless?) && node.else_branch
100
+
101
+ if node.else_branch.if_type?
102
+ # Replace an orphaned `elsif` with `if`
103
+ corrector.replace(node.else_branch.loc.keyword, 'if')
104
+ else
105
+ # Flip orphaned `else`
106
+ corrector.replace(node.loc.else, "#{node.inverse_keyword} #{node.condition.source}")
107
+ end
108
+ end
109
+
110
+ def branch_range(node)
111
+ if node.loc.else
112
+ node.source_range.with(end_pos: node.loc.else.begin_pos - 1)
113
+ else
114
+ node.source_range
115
+ end
116
+ end
117
+
118
+ def deletion_range(range)
119
+ # Collect a range between the start of the `if` node and the next relevant node,
120
+ # including final new line.
121
+ # Based on `RangeHelp#range_by_whole_lines` but allows the `if` to not start
122
+ # on the first column.
123
+ buffer = @processed_source.buffer
124
+
125
+ last_line = buffer.source_line(range.last_line)
126
+ end_offset = last_line.length - range.last_column + 1
127
+
128
+ range.adjust(end_pos: end_offset).intersect(buffer.source_range)
65
129
  end
66
130
  end
67
131
  end
@@ -25,30 +25,37 @@ module RuboCop
25
25
  # to be strictly equivalent to that before the replacement.
26
26
  #
27
27
  # @example
28
- # # bad
29
- # unless FileTest.exist?(path)
30
- # FileUtils.makedirs(path)
28
+ # # bad - race condition with another process may result in an error in `mkdir`
29
+ # unless Dir.exist?(path)
30
+ # FileUtils.mkdir(path)
31
31
  # end
32
32
  #
33
- # if FileTest.exist?(path)
33
+ # # good - atomic and idempotent creation
34
+ # FileUtils.mkdir_p(path)
35
+ #
36
+ # # bad - race condition with another process may result in an error in `remove`
37
+ # if File.exist?(path)
34
38
  # FileUtils.remove(path)
35
39
  # end
36
40
  #
37
- # # good
38
- # FileUtils.mkdir_p(path)
39
- #
40
- # FileUtils.rm_rf(path)
41
+ # # good - atomic and idempotent removal
42
+ # FileUtils.rm_f(path)
41
43
  #
42
44
  class NonAtomicFileOperation < Base
43
45
  extend AutoCorrector
44
46
  include Alignment
45
47
  include RangeHelp
46
48
 
47
- MSG = 'Remove unnecessary existence checks `%<receiver>s.%<method_name>s`.'
48
- MAKE_METHODS = %i[makedirs mkdir mkdir_p mkpath].freeze
49
- REMOVE_METHODS = %i[remove remove_dir remove_entry remove_entry_secure delete unlink
50
- remove_file rm rm_f rm_r rm_rf rmdir rmtree safe_unlink].freeze
51
- RESTRICT_ON_SEND = (MAKE_METHODS + REMOVE_METHODS).freeze
49
+ MSG_REMOVE_FILE_EXIST_CHECK = 'Remove unnecessary existence check ' \
50
+ '`%<receiver>s.%<method_name>s`.'
51
+ MSG_CHANGE_FORCE_METHOD = 'Use atomic file operation method `FileUtils.%<method_name>s`.'
52
+ MAKE_FORCE_METHODS = %i[makedirs mkdir_p mkpath].freeze
53
+ MAKE_METHODS = %i[mkdir].freeze
54
+ REMOVE_FORCE_METHODS = %i[rm_f rm_rf].freeze
55
+ REMOVE_METHODS = %i[remove remove_dir remove_entry remove_entry_secure
56
+ delete unlink remove_file rm rmdir safe_unlink].freeze
57
+ RESTRICT_ON_SEND = (MAKE_METHODS + MAKE_FORCE_METHODS + REMOVE_METHODS +
58
+ REMOVE_FORCE_METHODS).freeze
52
59
 
53
60
  # @!method send_exist_node(node)
54
61
  def_node_search :send_exist_node, <<-PATTERN
@@ -71,10 +78,9 @@ module RuboCop
71
78
  PATTERN
72
79
 
73
80
  def on_send(node)
74
- return unless (parent = node.parent) && parent.if_type?
75
- return if allowable_use_with_if?(parent)
81
+ return unless if_node_child?(node)
76
82
  return if explicit_not_force?(node)
77
- return unless (exist_node = send_exist_node(parent).first)
83
+ return unless (exist_node = send_exist_node(node.parent).first)
78
84
  return unless exist_node.first_argument == node.first_argument
79
85
 
80
86
  register_offense(node, exist_node)
@@ -82,44 +88,69 @@ module RuboCop
82
88
 
83
89
  private
84
90
 
91
+ def if_node_child?(node)
92
+ return false unless (parent = node.parent)
93
+
94
+ parent.if_type? && !allowable_use_with_if?(parent)
95
+ end
96
+
85
97
  def allowable_use_with_if?(if_node)
86
98
  if_node.condition.and_type? || if_node.condition.or_type? || if_node.else_branch
87
99
  end
88
100
 
89
101
  def register_offense(node, exist_node)
102
+ unless force_method?(node)
103
+ add_offense(node,
104
+ message: format(MSG_CHANGE_FORCE_METHOD,
105
+ method_name: replacement_method(node)))
106
+ end
107
+
90
108
  range = range_between(node.parent.loc.keyword.begin_pos,
91
109
  exist_node.loc.expression.end_pos)
92
-
93
- add_offense(range, message: message(exist_node)) do |corrector|
110
+ add_offense(range, message: message_remove_file_exist_check(exist_node)) do |corrector|
94
111
  autocorrect(corrector, node, range)
95
112
  end
96
113
  end
97
114
 
98
- def message(node)
115
+ def message_remove_file_exist_check(node)
99
116
  receiver, method_name = receiver_and_method_name(node)
100
- format(MSG, receiver: receiver, method_name: method_name)
117
+ format(MSG_REMOVE_FILE_EXIST_CHECK, receiver: receiver, method_name: method_name)
101
118
  end
102
119
 
103
120
  def autocorrect(corrector, node, range)
104
121
  corrector.remove(range)
122
+ autocorrect_replace_method(corrector, node)
123
+ corrector.remove(node.parent.loc.end) if node.parent.multiline?
124
+ end
125
+
126
+ def autocorrect_replace_method(corrector, node)
127
+ return if force_method?(node)
128
+
105
129
  corrector.replace(node.child_nodes.first.loc.name, 'FileUtils')
106
130
  corrector.replace(node.loc.selector, replacement_method(node))
107
- corrector.remove(node.parent.loc.end) if node.parent.multiline?
108
131
  end
109
132
 
110
133
  def replacement_method(node)
111
- return node.method_name if force_option?(node)
112
-
113
134
  if MAKE_METHODS.include?(node.method_name)
114
135
  'mkdir_p'
115
136
  elsif REMOVE_METHODS.include?(node.method_name)
116
- 'rm_rf'
137
+ 'rm_f'
138
+ else
139
+ node.method_name
117
140
  end
118
141
  end
119
142
 
143
+ def force_method?(node)
144
+ force_method_name?(node) || force_option?(node)
145
+ end
146
+
120
147
  def force_option?(node)
121
148
  node.arguments.any? { |arg| force?(arg) }
122
149
  end
150
+
151
+ def force_method_name?(node)
152
+ (MAKE_FORCE_METHODS + REMOVE_FORCE_METHODS).include?(node.method_name)
153
+ end
123
154
  end
124
155
  end
125
156
  end
@@ -16,7 +16,8 @@ module RuboCop
16
16
  # NOTE: Some values cannot be converted properly using one of the `Kernel`
17
17
  # method (for instance, `Time` and `DateTime` values are allowed by this
18
18
  # cop by default). Similarly, Rails' duration methods do not work well
19
- # with `Integer()` and can be ignored with `IgnoredMethods`.
19
+ # with `Integer()` and can be allowed with `AllowedMethods`. By default,
20
+ # there are no methods to allowed.
20
21
  #
21
22
  # @safety
22
23
  # Autocorrection is unsafe because it is not guaranteed that the
@@ -45,7 +46,22 @@ module RuboCop
45
46
  # foo.try { |i| Float(i) }
46
47
  # bar.send { |i| Complex(i) }
47
48
  #
48
- # @example IgnoredMethods: [minutes]
49
+ # @example AllowedMethods: [] (default)
50
+ #
51
+ # # bad
52
+ # 10.minutes.to_i
53
+ #
54
+ # @example AllowedMethods: [minutes]
55
+ #
56
+ # # good
57
+ # 10.minutes.to_i
58
+ #
59
+ # @example AllowedPatterns: [] (default)
60
+ #
61
+ # # bad
62
+ # 10.minutes.to_i
63
+ #
64
+ # @example AllowedPatterns: [/min*/]
49
65
  #
50
66
  # # good
51
67
  # 10.minutes.to_i
@@ -56,7 +72,8 @@ module RuboCop
56
72
  # Time.now.to_datetime.to_i
57
73
  class NumberConversion < Base
58
74
  extend AutoCorrector
59
- include IgnoredMethods
75
+ include AllowedMethods
76
+ include AllowedPattern
60
77
 
61
78
  CONVERSION_METHOD_CLASS_MAPPING = {
62
79
  to_i: "#{Integer.name}(%<number_object>s, 10)",
@@ -91,7 +108,7 @@ module RuboCop
91
108
 
92
109
  def handle_conversion_method(node)
93
110
  to_method(node) do |receiver, to_method|
94
- next if receiver.nil? || ignore_receiver?(receiver)
111
+ next if receiver.nil? || allow_receiver?(receiver)
95
112
 
96
113
  message = format(
97
114
  MSG,
@@ -135,9 +152,10 @@ module RuboCop
135
152
  corrector.remove(node.loc.end)
136
153
  end
137
154
 
138
- def ignore_receiver?(receiver)
155
+ def allow_receiver?(receiver)
139
156
  if receiver.numeric_type? || (receiver.send_type? &&
140
- (conversion_method?(receiver.method_name) || ignored_method?(receiver.method_name)))
157
+ (conversion_method?(receiver.method_name) ||
158
+ allowed_method_name?(receiver.method_name)))
141
159
  true
142
160
  elsif (receiver = top_receiver(receiver))
143
161
  receiver.const_type? && ignored_class?(receiver.const_name)
@@ -146,6 +164,10 @@ module RuboCop
146
164
  end
147
165
  end
148
166
 
167
+ def allowed_method_name?(name)
168
+ allowed_method?(name) || matches_allowed_pattern?(name)
169
+ end
170
+
149
171
  def top_receiver(node)
150
172
  receiver = node
151
173
  receiver = receiver.receiver until receiver.receiver.nil?
@@ -35,6 +35,13 @@ module RuboCop
35
35
  # # good - without `&.` this will always return `true`
36
36
  # foo&.respond_to?(:to_a)
37
37
  #
38
+ # @example AllowedMethods: [foo?]
39
+ # # bad
40
+ # do_something if attrs&.foo?(:[])
41
+ #
42
+ # # good
43
+ # do_something if attrs&.bar?(:[])
44
+ #
38
45
  class RedundantSafeNavigation < Base
39
46
  include AllowedMethods
40
47
  include RangeHelp
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks that a range literal is enclosed in parentheses when the end of the range is
7
+ # at a line break.
8
+ #
9
+ # NOTE: The following is maybe intended for `(42..)`. But, compatible is `42..do_something`.
10
+ # So, this cop does not provide autocorrection because it is left to user.
11
+ #
12
+ # [source,ruby]
13
+ # ----
14
+ # case condition
15
+ # when 42..
16
+ # do_something
17
+ # end
18
+ # ----
19
+ #
20
+ # @example
21
+ #
22
+ # # bad - Represents `(1..42)`, not endless range.
23
+ # 1..
24
+ # 42
25
+ #
26
+ # # good - It's incompatible, but your intentions when using endless range may be:
27
+ # (1..)
28
+ # 42
29
+ #
30
+ # # good
31
+ # 1..42
32
+ #
33
+ # # good
34
+ # (1..42)
35
+ #
36
+ # # good
37
+ # (1..
38
+ # 42)
39
+ #
40
+ class RequireRangeParentheses < Base
41
+ MSG = 'Wrap the endless range literal `%<range>s` to avoid precedence ambiguity.'
42
+
43
+ def on_irange(node)
44
+ return if node.parent&.begin_type?
45
+ return unless node.begin && node.end
46
+ return if same_line?(node.begin, node.end)
47
+
48
+ message = format(MSG, range: "#{node.begin.source}#{node.loc.operator.source}")
49
+
50
+ add_offense(node, message: message)
51
+ end
52
+
53
+ alias on_erange on_irange
54
+ end
55
+ end
56
+ end
57
+ end
@@ -25,6 +25,7 @@ module RuboCop
25
25
  # x&.foo || bar
26
26
  class SafeNavigationChain < Base
27
27
  include NilMethods
28
+ extend AutoCorrector
28
29
  extend TargetRubyVersion
29
30
 
30
31
  minimum_target_ruby_version 2.3
@@ -48,12 +49,45 @@ module RuboCop
48
49
  Parser::Source::Range.new(node.source_range.source_buffer,
49
50
  safe_nav.source_range.end_pos,
50
51
  method_chain.source_range.end_pos)
51
- add_offense(location)
52
+ add_offense(location) do |corrector|
53
+ autocorrect(corrector, offense_range: location, send_node: method_chain)
54
+ end
52
55
  end
53
56
  end
54
57
 
55
58
  private
56
59
 
60
+ # @param [Parser::Source::Range] offense_range
61
+ # @param [RuboCop::AST::SendNode] send_node
62
+ # @return [String]
63
+ def add_safe_navigation_operator(offense_range:, send_node:)
64
+ source = \
65
+ if send_node.method?(:[]) || send_node.method?(:[]=)
66
+ format(
67
+ '%<method_name>s(%<arguments>s)',
68
+ arguments: send_node.arguments.map(&:source).join(', '),
69
+ method_name: send_node.method_name
70
+ )
71
+ else
72
+ offense_range.source.dup
73
+ end
74
+ source.prepend('.') unless send_node.dot?
75
+ source.prepend('&')
76
+ end
77
+
78
+ # @param [RuboCop::Cop::Corrector] corrector
79
+ # @param [Parser::Source::Range] offense_range
80
+ # @param [RuboCop::AST::SendNode] send_node
81
+ def autocorrect(corrector, offense_range:, send_node:)
82
+ corrector.replace(
83
+ offense_range,
84
+ add_safe_navigation_operator(
85
+ offense_range: offense_range,
86
+ send_node: send_node
87
+ )
88
+ )
89
+ end
90
+
57
91
  def method_chain(node)
58
92
  chain = node
59
93
  chain = chain.parent if chain.send_type? && chain.parent&.call_type?
@@ -7,6 +7,14 @@ module RuboCop
7
7
  # less specific exception being rescued before a more specific
8
8
  # exception is rescued.
9
9
  #
10
+ # An exception is considered shadowed if it is rescued after its
11
+ # ancestor is, or if it and its ancestor are both rescued in the
12
+ # same `rescue` statement. In both cases, the more specific rescue is
13
+ # unnecessary because it is covered by rescuing the less specific
14
+ # exception. (ie. `rescue Exception, StandardError` has the same behavior
15
+ # whether `StandardError` is included or not, because all `StandardError`s
16
+ # are rescued by `rescue Exception`).
17
+ #
10
18
  # @example
11
19
  #
12
20
  # # bad
@@ -19,6 +27,13 @@ module RuboCop
19
27
  # handle_standard_error
20
28
  # end
21
29
  #
30
+ # # bad
31
+ # begin
32
+ # something
33
+ # rescue Exception, StandardError
34
+ # handle_error
35
+ # end
36
+ #
22
37
  # # good
23
38
  #
24
39
  # begin
@@ -67,10 +67,18 @@ module RuboCop
67
67
  variable_node = variable.scope.node.parent
68
68
  return false unless variable_node.conditional?
69
69
 
70
- outer_local_variable_node = outer_local_variable.scope.node
70
+ outer_local_variable_node =
71
+ find_conditional_node_from_ascendant(outer_local_variable.declaration_node)
71
72
 
72
73
  outer_local_variable_node.conditional? && variable_node == outer_local_variable_node
73
74
  end
75
+
76
+ def find_conditional_node_from_ascendant(node)
77
+ return unless (parent = node.parent)
78
+ return parent if parent.conditional?
79
+
80
+ find_conditional_node_from_ascendant(parent)
81
+ end
74
82
  end
75
83
  end
76
84
  end
@@ -33,7 +33,9 @@ module RuboCop
33
33
  # render 'pages/search/page'
34
34
  # end
35
35
  #
36
- # This cop also takes into account `IgnoredMethods` (defaults to `[]`)
36
+ # This cop also takes into account `AllowedMethods` (defaults to `[]`)
37
+ # And `AllowedPatterns` (defaults to `[]`)
38
+ #
37
39
  class AbcSize < Base
38
40
  include MethodComplexity
39
41
 
@@ -14,7 +14,8 @@ module RuboCop
14
14
  #
15
15
  #
16
16
  # NOTE: The `ExcludedMethods` configuration is deprecated and only kept
17
- # for backwards compatibility. Please use `IgnoredMethods` instead.
17
+ # for backwards compatibility. Please use `AllowedMethods` and `AllowedPatterns`
18
+ # instead. By default, there are no methods to allowed.
18
19
  #
19
20
  # @example CountAsOne: ['array', 'heredoc']
20
21
  #
@@ -37,14 +38,13 @@ module RuboCop
37
38
  # NOTE: This cop does not apply for `Struct` definitions.
38
39
  class BlockLength < Base
39
40
  include CodeLength
40
- include IgnoredMethods
41
-
42
- ignored_methods deprecated_key: 'ExcludedMethods'
41
+ include AllowedMethods
42
+ include AllowedPattern
43
43
 
44
44
  LABEL = 'Block'
45
45
 
46
46
  def on_block(node)
47
- return if ignored_method?(node.method_name)
47
+ return if allowed_method?(node.method_name) || matches_allowed_pattern?(node.method_name)
48
48
  return if method_receiver_excluded?(node)
49
49
  return if node.class_constructor? || node.struct_constructor?
50
50
 
@@ -58,7 +58,7 @@ module RuboCop
58
58
  node_receiver = node.receiver&.source&.gsub(/\s+/, '')
59
59
  node_method = String(node.method_name)
60
60
 
61
- ignored_methods.any? do |config|
61
+ allowed_methods.any? do |config|
62
62
  next unless config.is_a?(String)
63
63
 
64
64
  receiver, method = config.split('.')
@@ -4,15 +4,17 @@ module RuboCop
4
4
  module Cop
5
5
  module Metrics
6
6
  # Checks if the length of a method exceeds some maximum value.
7
- # Comment lines can optionally be ignored.
7
+ # Comment lines can optionally be allowed.
8
8
  # The maximum allowed length is configurable.
9
9
  #
10
10
  # You can set literals you want to fold with `CountAsOne`.
11
11
  # Available are: 'array', 'hash', and 'heredoc'. Each literal
12
12
  # will be counted as one line regardless of its actual size.
13
13
  #
14
- # NOTE: The `ExcludedMethods` configuration is deprecated and only kept
15
- # for backwards compatibility. Please use `IgnoredMethods` instead.
14
+ # NOTE: The `ExcludedMethods` and `IgnoredMethods` configuration is
15
+ # deprecated and only kept for backwards compatibility.
16
+ # Please use `AllowedMethods` and `AllowedPatterns` instead.
17
+ # By default, there are no methods to allowed.
16
18
  #
17
19
  # @example CountAsOne: ['array', 'heredoc']
18
20
  #
@@ -34,14 +36,13 @@ module RuboCop
34
36
  #
35
37
  class MethodLength < Base
36
38
  include CodeLength
37
- include IgnoredMethods
38
-
39
- ignored_methods deprecated_key: 'ExcludedMethods'
39
+ include AllowedMethods
40
+ include AllowedPattern
40
41
 
41
42
  LABEL = 'Method'
42
43
 
43
44
  def on_def(node)
44
- return if ignored_method?(node.method_name)
45
+ return if allowed_method?(node.method_name) || matches_allowed_pattern?(node.method_name)
45
46
 
46
47
  check_code_length(node)
47
48
  end
@@ -12,10 +12,24 @@ module RuboCop
12
12
  allowed_methods.include?(name.to_s)
13
13
  end
14
14
 
15
+ # @deprecated Use allowed_method? instead
16
+ alias ignored_method? allowed_method?
17
+
15
18
  # @api public
16
19
  def allowed_methods
17
- cop_config.fetch('AllowedMethods', [])
20
+ deprecated_values = cop_config_deprecated_values
21
+ if deprecated_values.any?(Regexp)
22
+ cop_config.fetch('AllowedMethods', [])
23
+ else
24
+ Array(cop_config['AllowedMethods']).concat(deprecated_values)
25
+ end
26
+ end
27
+
28
+ def cop_config_deprecated_values
29
+ Array(cop_config['IgnoredMethods']).concat(Array(cop_config['ExcludedMethods']))
18
30
  end
19
31
  end
32
+ # @deprecated IgnoredMethods class has been replaced with AllowedMethods.
33
+ IgnoredMethods = AllowedMethods
20
34
  end
21
35
  end
@@ -30,7 +30,15 @@ module RuboCop
30
30
  def allowed_patterns
31
31
  # Since there could be a pattern specified in the default config, merge the two
32
32
  # arrays together.
33
- Array(cop_config['AllowedPatterns']).concat(Array(cop_config['IgnoredPatterns']))
33
+ patterns = Array(cop_config['AllowedPatterns']).concat(Array(cop_config['IgnoredPatterns']))
34
+ deprecated_values = cop_config_deprecated_methods_values
35
+ return patterns unless deprecated_values.any?(Regexp)
36
+
37
+ Array(patterns.concat(deprecated_values))
38
+ end
39
+
40
+ def cop_config_deprecated_methods_values
41
+ Array(cop_config['IgnoredMethods']).concat(Array(cop_config['ExcludedMethods']))
34
42
  end
35
43
  end
36
44
 
@@ -46,6 +46,8 @@ module RuboCop
46
46
  if node.send_type?
47
47
  args = process_args(node.arguments)
48
48
  return extract_breakable_node_from_elements(node, args, max)
49
+ elsif node.def_type?
50
+ return extract_breakable_node_from_elements(node, node.arguments, max)
49
51
  elsif node.array_type? || node.hash_type?
50
52
  return extract_breakable_node_from_elements(node, node.children, max)
51
53
  end
@@ -216,6 +218,8 @@ module RuboCop
216
218
 
217
219
  # @api private
218
220
  def already_on_multiple_lines?(node)
221
+ return node.first_line != node.arguments.last.last_line if node.def_type?
222
+
219
223
  node.first_line != node.last_line
220
224
  end
221
225
  end
@@ -12,10 +12,14 @@ module RuboCop
12
12
  end
13
13
 
14
14
  def contains_comments?(node)
15
+ comments_in_range(node).any?
16
+ end
17
+
18
+ def comments_in_range(node)
15
19
  start_line = node.source_range.line
16
20
  end_line = find_end_line(node)
17
21
 
18
- processed_source.each_comment_in_lines(start_line...end_line).any?
22
+ processed_source.each_comment_in_lines(start_line...end_line)
19
23
  end
20
24
 
21
25
  private
@@ -6,7 +6,8 @@ module RuboCop
6
6
  #
7
7
  # IMPORTANT: RuboCop core depended on this module when it supported Rails department.
8
8
  # Rails department has been extracted to RuboCop Rails gem.
9
- # This module is deprecated and will be removed by RuboCop 2.0.
9
+ #
10
+ # @deprecated This module is deprecated and will be removed by RuboCop 2.0.
10
11
  # It will not be updated to `RuboCop::Cop::Base` v1 API to maintain compatibility
11
12
  # with existing RuboCop Rails 2.8 or lower.
12
13
  #