rubocop 1.13.0 → 1.14.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 580e325f7dc471739e2f4857d82e279776098ae14fff7367eaa803f688c9e58d
4
- data.tar.gz: c8331c8b2ebbecb239d4a63dee21375172d1d2939572516bba910109ef5375a1
3
+ metadata.gz: bf17e65fbe829bbc1328bfc1898fe4df1fc1a1e9773b946e1136b3dc16625fd5
4
+ data.tar.gz: a35649eeecce4e75f2fb623094b4cc5f93323c1b44393482c0b57e3b37358d86
5
5
  SHA512:
6
- metadata.gz: 5f9ecd1e28e7fa7946b22aaecc0d5571337d85ca6b9a198e9542a9d26d05c9d0806597ccd66d0090c4318d349c7023fe201103fa197eaa88d5e98534d25ff755
7
- data.tar.gz: 4bc7ec5d20c127826a1761b9495669242d53b740509ca13060c1202d0114062fb73a52738b9fb717f5348fdad7cb2a2fb5ee0ea4cc8a9b5823298908a0ce6a5b
6
+ metadata.gz: 696e932f19f9f47448de8858c7444be66fb1a58fa25588e26a8b3842a786677bcf3bd49f9f80b945cbbbed3f5826939fbdf8a8cf106e93843e9c659e20d47b10
7
+ data.tar.gz: 70729c8cc362bca0ee4397881f2acd0ba088430bfc1c6867e17a9a3da61eff8a0aa911d44a673e1e70d44978638675aedd3717dccfafe3f8cfc78c3f09baeabf
data/README.md CHANGED
@@ -52,7 +52,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi
52
52
  in your `Gemfile`:
53
53
 
54
54
  ```rb
55
- gem 'rubocop', '~> 1.13', require: false
55
+ gem 'rubocop', '~> 1.14', require: false
56
56
  ```
57
57
 
58
58
  See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
data/config/default.yml CHANGED
@@ -130,7 +130,7 @@ AllCops:
130
130
  # What MRI version of the Ruby interpreter is the inspected code intended to
131
131
  # run on? (If there is more than one, set this to the lowest version.)
132
132
  # If a value is specified for TargetRubyVersion then it is used. Acceptable
133
- # values are specificed as a float (i.e. 2.5); the teeny version of Ruby
133
+ # values are specificed as a float (i.e. 3.0); the teeny version of Ruby
134
134
  # should not be included. If the project specifies a Ruby version in the
135
135
  # .tool-versions or .ruby-version files, Gemfile or gems.rb file, RuboCop will
136
136
  # try to determine the desired version of Ruby by inspecting the
@@ -138,7 +138,7 @@ AllCops:
138
138
  # or gems.locked file. (Although the Ruby version is specified in the Gemfile
139
139
  # or gems.rb file, RuboCop reads the final value from the lock file.) If the
140
140
  # Ruby version is still unresolved, RuboCop will use the oldest officially
141
- # supported Ruby version (currently Ruby 2.4).
141
+ # supported Ruby version (currently Ruby 2.5).
142
142
  TargetRubyVersion: ~
143
143
  # Determines if a notification for extension libraries should be shown when
144
144
  # rubocop is run. Keys are the name of the extension, and values are an array
@@ -174,6 +174,20 @@ Bundler/GemComment:
174
174
  IgnoredGems: []
175
175
  OnlyFor: []
176
176
 
177
+ Bundler/GemVersion:
178
+ Description: 'Requires or forbids specifying gem versions.'
179
+ Enabled: false
180
+ VersionAdded: '1.14'
181
+ EnforcedStyle: 'required'
182
+ SupportedStyles:
183
+ - 'required'
184
+ - 'forbidden'
185
+ Include:
186
+ - '**/*.gemfile'
187
+ - '**/Gemfile'
188
+ - '**/gems.rb'
189
+ AllowedGems: []
190
+
177
191
  Bundler/InsecureProtocolSource:
178
192
  Description: >-
179
193
  The source `:gemcutter`, `:rubygems` and `:rubyforge` are deprecated
@@ -1114,6 +1128,11 @@ Layout/RescueEnsureAlignment:
1114
1128
  Enabled: true
1115
1129
  VersionAdded: '0.49'
1116
1130
 
1131
+ Layout/SingleLineBlockChain:
1132
+ Description: 'Put method call on a separate line if chained to a single line block.'
1133
+ Enabled: false
1134
+ VersionAdded: '1.14'
1135
+
1117
1136
  Layout/SpaceAfterColon:
1118
1137
  Description: 'Use spaces after colons.'
1119
1138
  StyleGuide: '#spaces-operators'
data/lib/rubocop.rb CHANGED
@@ -82,6 +82,7 @@ require_relative 'rubocop/cop/mixin/end_keyword_alignment'
82
82
  require_relative 'rubocop/cop/mixin/enforce_superclass'
83
83
  require_relative 'rubocop/cop/mixin/first_element_line_break'
84
84
  require_relative 'rubocop/cop/mixin/frozen_string_literal'
85
+ require_relative 'rubocop/cop/mixin/gem_declaration'
85
86
  require_relative 'rubocop/cop/mixin/hash_alignment_styles'
86
87
  require_relative 'rubocop/cop/mixin/hash_transform_method'
87
88
  require_relative 'rubocop/cop/mixin/ignored_pattern'
@@ -148,6 +149,7 @@ require_relative 'rubocop/cop/correctors/unused_arg_corrector'
148
149
 
149
150
  require_relative 'rubocop/cop/bundler/duplicated_gem'
150
151
  require_relative 'rubocop/cop/bundler/gem_comment'
152
+ require_relative 'rubocop/cop/bundler/gem_version'
151
153
  require_relative 'rubocop/cop/bundler/insecure_protocol_source'
152
154
  require_relative 'rubocop/cop/bundler/ordered_gems'
153
155
 
@@ -223,6 +225,7 @@ require_relative 'rubocop/cop/layout/multiline_operation_indentation'
223
225
  require_relative 'rubocop/cop/layout/parameter_alignment'
224
226
  require_relative 'rubocop/cop/layout/redundant_line_break'
225
227
  require_relative 'rubocop/cop/layout/rescue_ensure_alignment'
228
+ require_relative 'rubocop/cop/layout/single_line_block_chain'
226
229
  require_relative 'rubocop/cop/layout/space_after_colon'
227
230
  require_relative 'rubocop/cop/layout/space_after_comma'
228
231
  require_relative 'rubocop/cop/layout/space_after_method_name'
@@ -82,6 +82,7 @@ module RuboCop
82
82
  #
83
83
  class GemComment < Base
84
84
  include DefNode
85
+ include GemDeclaration
85
86
 
86
87
  MSG = 'Missing gem description comment.'
87
88
  CHECKED_OPTIONS_CONFIG = 'OnlyFor'
@@ -90,9 +91,6 @@ module RuboCop
90
91
  RESTRICTIVE_VERSION_PATTERN = /<|~>/.freeze
91
92
  RESTRICT_ON_SEND = %i[gem].freeze
92
93
 
93
- # @!method gem_declaration?(node)
94
- def_node_matcher :gem_declaration?, '(send nil? :gem str ...)'
95
-
96
94
  def on_send(node)
97
95
  return unless gem_declaration?(node)
98
96
  return if ignored_gem?(node)
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Bundler
6
+ # Enforce that Gem version specifications are either required
7
+ # or forbidden.
8
+ #
9
+ # @example EnforcedStyle: required (default)
10
+ # # bad
11
+ # gem 'rubocop'
12
+ #
13
+ # # good
14
+ # gem 'rubocop', '~> 1.12'
15
+ #
16
+ # # good
17
+ # gem 'rubocop', '>= 1.10.0'
18
+ #
19
+ # # good
20
+ # gem 'rubocop', '>= 1.5.0', '< 1.10.0'
21
+ #
22
+ # @example EnforcedStyle: forbidden
23
+ # # good
24
+ # gem 'rubocop'
25
+ #
26
+ # # bad
27
+ # gem 'rubocop', '~> 1.12'
28
+ #
29
+ # # bad
30
+ # gem 'rubocop', '>= 1.10.0'
31
+ #
32
+ # # bad
33
+ # gem 'rubocop', '>= 1.5.0', '< 1.10.0'
34
+ #
35
+ class GemVersion < Base
36
+ include ConfigurableEnforcedStyle
37
+ include GemDeclaration
38
+
39
+ REQUIRED_MSG = 'Gem version specification is required.'
40
+ FORBIDDEN_MSG = 'Gem version specification is forbidden.'
41
+ VERSION_SPECIFICATION_REGEX = /^\s*[~<>=]*\s*[0-9.]+/.freeze
42
+
43
+ # @!method includes_version_specification?(node)
44
+ def_node_matcher :includes_version_specification?, <<~PATTERN
45
+ (send nil? :gem <(str #version_specification?) ...>)
46
+ PATTERN
47
+
48
+ def on_send(node)
49
+ return unless gem_declaration?(node)
50
+ return if allowed_gem?(node)
51
+
52
+ if offense?(node)
53
+ add_offense(node)
54
+ opposite_style_detected
55
+ else
56
+ correct_style_detected
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def allowed_gem?(node)
63
+ allowed_gems.include?(node.first_argument.value)
64
+ end
65
+
66
+ def allowed_gems
67
+ Array(cop_config['AllowedGems'])
68
+ end
69
+
70
+ def message(range)
71
+ gem_specification = range.source
72
+
73
+ if required_style?
74
+ format(REQUIRED_MSG, gem_specification: gem_specification)
75
+ elsif forbidden_style?
76
+ format(FORBIDDEN_MSG, gem_specification: gem_specification)
77
+ end
78
+ end
79
+
80
+ def offense?(node)
81
+ (required_style? && !includes_version_specification?(node)) ||
82
+ (forbidden_style? && includes_version_specification?(node))
83
+ end
84
+
85
+ def forbidden_style?
86
+ style == :forbidden
87
+ end
88
+
89
+ def required_style?
90
+ style == :required
91
+ end
92
+
93
+ def version_specification?(expression)
94
+ expression.match?(VERSION_SPECIFICATION_REGEX)
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -79,8 +79,20 @@ module RuboCop
79
79
  end
80
80
 
81
81
  def configured_to_not_be_inspected?(node)
82
+ return true if other_cop_takes_precedence?(node)
83
+
82
84
  !cop_config['InspectBlocks'] && (node.block_type? ||
83
- node.each_child_node(:block).any?(&:multiline?))
85
+ node.each_descendant(:block).any?(&:multiline?))
86
+ end
87
+
88
+ def other_cop_takes_precedence?(node)
89
+ single_line_block_chain_enabled? && node.each_descendant(:block).any? do |block_node|
90
+ block_node.parent.send_type? && block_node.parent.loc.dot && !block_node.multiline?
91
+ end
92
+ end
93
+
94
+ def single_line_block_chain_enabled?
95
+ @config.for_cop('Layout/SingleLineBlockChain')['Enabled']
84
96
  end
85
97
 
86
98
  def suitable_as_single_line?(node)
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Layout
6
+ # This cop checks if method calls are chained onto single line blocks. It considers that a
7
+ # line break before the dot improves the readability of the code.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # example.select { |item| item.cond? }.join('-')
12
+ #
13
+ # # good
14
+ # example.select { |item| item.cond? }
15
+ # .join('-')
16
+ #
17
+ # # good (not a concern for this cop)
18
+ # example.select do |item|
19
+ # item.cond?
20
+ # end.join('-')
21
+ #
22
+ class SingleLineBlockChain < Base
23
+ include RangeHelp
24
+ extend AutoCorrector
25
+
26
+ MSG = 'Put method call on a separate line if chained to a single line block.'
27
+
28
+ def on_send(node)
29
+ range = offending_range(node)
30
+ add_offense(range) { |corrector| corrector.insert_before(range, "\n") } if range
31
+ end
32
+
33
+ private
34
+
35
+ def offending_range(node)
36
+ receiver = node.receiver
37
+ return unless receiver&.block_type?
38
+
39
+ receiver_location = receiver.loc
40
+ closing_block_delimiter_line_number = receiver_location.end.line
41
+ return if receiver_location.begin.line < closing_block_delimiter_line_number
42
+
43
+ node_location = node.loc
44
+ dot_range = node_location.dot
45
+ return unless dot_range
46
+ return if dot_range.line > closing_block_delimiter_line_number
47
+
48
+ range_between(dot_range.begin_pos, node_location.selector.end_pos)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -24,17 +24,17 @@ module RuboCop
24
24
  extend AutoCorrector
25
25
 
26
26
  # Inner class to DeprecatedClassMethods.
27
- # This class exists to add abstraction and clean naming to the
28
- # objects that are going to be operated on.
27
+ # This class exists to add abstraction and clean naming
28
+ # to the deprecated objects
29
29
  class DeprecatedClassMethod
30
30
  include RuboCop::AST::Sexp
31
31
 
32
- attr_reader :class_constant, :deprecated_method, :replacement_method
32
+ attr_reader :method, :class_constant
33
33
 
34
- def initialize(deprecated:, replacement:, class_constant: nil)
35
- @deprecated_method = deprecated
36
- @replacement_method = replacement
34
+ def initialize(method, class_constant: nil, correctable: true)
35
+ @method = method
37
36
  @class_constant = class_constant
37
+ @correctable = correctable
38
38
  end
39
39
 
40
40
  def class_nodes
@@ -48,28 +48,80 @@ module RuboCop
48
48
  [nil]
49
49
  end
50
50
  end
51
+
52
+ def correctable?
53
+ @correctable
54
+ end
55
+
56
+ def to_s
57
+ [class_constant, method].compact.join(delimeter)
58
+ end
59
+
60
+ private
61
+
62
+ def delimeter
63
+ CLASS_METHOD_DELIMETER
64
+ end
65
+ end
66
+
67
+ # Inner class to DeprecatedClassMethods.
68
+ # This class exists to add abstraction and clean naming
69
+ # to the replacements for deprecated objects
70
+ class Replacement
71
+ attr_reader :method, :class_constant
72
+
73
+ def initialize(method, class_constant: nil, instance_method: false)
74
+ @method = method
75
+ @class_constant = class_constant
76
+ @instance_method = instance_method
77
+ end
78
+
79
+ def to_s
80
+ [class_constant, method].compact.join(delimeter)
81
+ end
82
+
83
+ private
84
+
85
+ def delimeter
86
+ instance_method? ? INSTANCE_METHOD_DELIMETER : CLASS_METHOD_DELIMETER
87
+ end
88
+
89
+ def instance_method?
90
+ @instance_method
91
+ end
51
92
  end
52
93
 
53
94
  MSG = '`%<current>s` is deprecated in favor of `%<prefer>s`.'
54
- DEPRECATED_METHODS_OBJECT = [
55
- DeprecatedClassMethod.new(deprecated: :exists?,
56
- replacement: :exist?,
57
- class_constant: :File),
58
- DeprecatedClassMethod.new(deprecated: :exists?,
59
- replacement: :exist?,
60
- class_constant: :Dir),
61
- DeprecatedClassMethod.new(deprecated: :iterator?, replacement: :block_given?)
62
- ].freeze
63
-
64
- RESTRICT_ON_SEND = DEPRECATED_METHODS_OBJECT.map(&:deprecated_method).freeze
95
+
96
+ DEPRECATED_METHODS_OBJECT = {
97
+ DeprecatedClassMethod.new(:exists?, class_constant: :File) =>
98
+ Replacement.new(:exist?, class_constant: :File),
99
+
100
+ DeprecatedClassMethod.new(:exists?, class_constant: :Dir) =>
101
+ Replacement.new(:exist?, class_constant: :Dir),
102
+
103
+ DeprecatedClassMethod.new(:iterator?) => Replacement.new(:block_given?),
104
+
105
+ DeprecatedClassMethod.new(:gethostbyaddr, class_constant: :Socket, correctable: false) =>
106
+ Replacement.new(:getnameinfo, class_constant: :Addrinfo, instance_method: true),
107
+
108
+ DeprecatedClassMethod.new(:gethostbyname, class_constant: :Socket, correctable: false) =>
109
+ Replacement.new(:getaddrinfo, class_constant: :Addrinfo, instance_method: true)
110
+ }.freeze
111
+
112
+ RESTRICT_ON_SEND = DEPRECATED_METHODS_OBJECT.keys.map(&:method).freeze
113
+
114
+ CLASS_METHOD_DELIMETER = '.'
115
+ INSTANCE_METHOD_DELIMETER = '#'
65
116
 
66
117
  def on_send(node)
67
- check(node) do |data|
68
- message = format(MSG, current: deprecated_method(data),
69
- prefer: replacement_method(data))
118
+ check(node) do |deprecated|
119
+ message = format(MSG, current: deprecated, prefer: replacement(deprecated))
70
120
 
71
121
  add_offense(node.loc.selector, message: message) do |corrector|
72
- corrector.replace(node.loc.selector, data.replacement_method.to_s)
122
+ if deprecated.correctable?
123
+ corrector.replace(node.loc.selector, replacement(deprecated).method)
124
+ end
73
125
  end
74
126
  end
75
127
  end
@@ -77,28 +129,16 @@ module RuboCop
77
129
  private
78
130
 
79
131
  def check(node)
80
- DEPRECATED_METHODS_OBJECT.each do |data|
81
- next unless data.class_nodes.include?(node.receiver)
82
- next unless node.method?(data.deprecated_method)
132
+ DEPRECATED_METHODS_OBJECT.each_key do |deprecated|
133
+ next unless deprecated.class_nodes.include?(node.receiver)
134
+ next unless node.method?(deprecated.method)
83
135
 
84
- yield data
136
+ yield deprecated
85
137
  end
86
138
  end
87
139
 
88
- def deprecated_method(data)
89
- method_call(data.class_constant, data.deprecated_method)
90
- end
91
-
92
- def replacement_method(data)
93
- method_call(data.class_constant, data.replacement_method)
94
- end
95
-
96
- def method_call(class_constant, method)
97
- if class_constant
98
- format('%<constant>s.%<method>s', constant: class_constant, method: method)
99
- else
100
- format('%<method>s', method: method)
101
- end
140
+ def replacement(deprecated)
141
+ DEPRECATED_METHODS_OBJECT[deprecated]
102
142
  end
103
143
  end
104
144
  end
@@ -97,7 +97,7 @@ module RuboCop
97
97
 
98
98
  def handle_as_symbol(node)
99
99
  to_method_symbol(node) do |receiver, sym_node, to_method|
100
- next if receiver.nil?
100
+ next if receiver.nil? || !node.arguments.one?
101
101
 
102
102
  message = format(
103
103
  MSG,
@@ -87,6 +87,7 @@ module RuboCop
87
87
  include IgnoredPattern
88
88
 
89
89
  MSG = 'This loop will have at most one iteration.'
90
+ CONTINUE_KEYWORDS = %i[next redo].freeze
90
91
 
91
92
  def on_while(node)
92
93
  check(node)
@@ -116,7 +117,10 @@ module RuboCop
116
117
  break_statement = statements.find { |statement| break_statement?(statement) }
117
118
  return unless break_statement
118
119
 
119
- add_offense(node) unless preceded_by_continue_statement?(break_statement)
120
+ unless preceded_by_continue_statement?(break_statement) ||
121
+ conditional_continue_keyword?(break_statement)
122
+ add_offense(node)
123
+ end
120
124
  end
121
125
 
122
126
  def statements(node)
@@ -177,9 +181,15 @@ module RuboCop
177
181
  break_statement.left_siblings.any? do |sibling|
178
182
  next if sibling.loop_keyword? || loop_method?(sibling)
179
183
 
180
- sibling.each_descendant(:next, :redo).any?
184
+ sibling.each_descendant(*CONTINUE_KEYWORDS).any?
181
185
  end
182
186
  end
187
+
188
+ def conditional_continue_keyword?(break_statement)
189
+ or_node = break_statement.each_descendant(:or).to_a.last
190
+
191
+ or_node && CONTINUE_KEYWORDS.include?(or_node.rhs.type)
192
+ end
183
193
  end
184
194
  end
185
195
  end
@@ -67,11 +67,17 @@ module RuboCop
67
67
  end
68
68
 
69
69
  def check_argument(variable)
70
- return if allowed_block?(variable) || allowed_keyword_argument?(variable)
70
+ return if allowed_block?(variable) ||
71
+ allowed_keyword_argument?(variable) ||
72
+ used_block_local?(variable)
71
73
 
72
74
  super
73
75
  end
74
76
 
77
+ def used_block_local?(variable)
78
+ variable.explicit_block_local_variable? && !variable.assignments.empty?
79
+ end
80
+
75
81
  def allowed_block?(variable)
76
82
  !variable.block_argument? || (ignore_empty_blocks? && empty_block?(variable))
77
83
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Common functionality for checking gem declarations.
6
+ module GemDeclaration
7
+ extend NodePattern::Macros
8
+
9
+ # @!method gem_declaration?(node)
10
+ def_node_matcher :gem_declaration?, '(send nil? :gem str ...)'
11
+ end
12
+ end
13
+ end
@@ -15,7 +15,7 @@ module RuboCop
15
15
  if style == :single_quotes
16
16
  !double_quotes_required?(src)
17
17
  else
18
- !/" | \\[^'] | \#(@|\{)/x.match?(src)
18
+ !/" | \\[^'\\] | \#[@{$]/x.match?(src)
19
19
  end
20
20
  end
21
21
  end
@@ -132,7 +132,7 @@ module RuboCop
132
132
  end
133
133
 
134
134
  def check_compact_style(node, body)
135
- return unless one_child?(body) && !compact_node_name?(node)
135
+ return unless needs_compacting?(body)
136
136
 
137
137
  add_offense(node.loc.name, message: COMPACT_MSG) do |corrector|
138
138
  autocorrect(corrector, node)
@@ -145,12 +145,12 @@ module RuboCop
145
145
  nest_or_compact(corrector, node)
146
146
  end
147
147
 
148
- def one_child?(body)
148
+ def needs_compacting?(body)
149
149
  body && %i[module class].include?(body.type)
150
150
  end
151
151
 
152
152
  def compact_node_name?(node)
153
- /::/.match?(node.loc.name.source)
153
+ /::/.match?(node.identifier.source)
154
154
  end
155
155
  end
156
156
  end
@@ -67,15 +67,14 @@ module RuboCop
67
67
 
68
68
  def autocorrect(corrector, node)
69
69
  replacement = if node.modifier_form?
70
- indentation = ' ' * node.source_range.column
71
- last_argument = node.if_branch.last_argument
70
+ last_argument = node.if_branch.last_argument if node.if_branch.send_type?
72
71
 
73
72
  if last_argument.respond_to?(:heredoc?) && last_argument.heredoc?
74
73
  heredoc = extract_heredoc_from(last_argument)
75
74
  remove_heredoc(corrector, heredoc)
76
- to_normal_form_with_heredoc(node, indentation, heredoc)
75
+ to_normal_form_with_heredoc(node, indent(node), heredoc)
77
76
  else
78
- to_normal_form(node, indentation)
77
+ to_normal_form(node, indent(node))
79
78
  end
80
79
  else
81
80
  to_modifier_form(node)
@@ -29,7 +29,6 @@ module RuboCop
29
29
  #
30
30
  class NegatedIfElseCondition < Base
31
31
  include RangeHelp
32
- include CommentsHelp
33
32
  extend AutoCorrector
34
33
 
35
34
  MSG = 'Invert the negated condition and swap the %<type>s branches.'
@@ -98,21 +97,30 @@ module RuboCop
98
97
  if node.if_branch.nil?
99
98
  corrector.remove(range_by_whole_lines(node.loc.else, include_final_newline: true))
100
99
  else
101
- if_range = node_with_comments(node.if_branch)
102
- else_range = node_with_comments(node.else_branch)
100
+ if_range = if_range(node)
101
+ else_range = else_range(node)
103
102
 
104
103
  corrector.replace(if_range, else_range.source)
105
104
  corrector.replace(else_range, if_range.source)
106
105
  end
107
106
  end
108
107
 
109
- def node_with_comments(node)
110
- first_statement = node.begin_type? ? node.children[0] : node
111
- return node if processed_source.ast_with_comments[first_statement].empty?
108
+ # Collect the entire if branch, including whitespace and comments
109
+ def if_range(node)
110
+ if node.ternary?
111
+ node.if_branch
112
+ else
113
+ range_between(node.condition.loc.expression.end_pos, node.loc.else.begin_pos)
114
+ end
115
+ end
112
116
 
113
- begin_pos = source_range_with_comment(first_statement).begin_pos
114
- end_pos = node.source_range.end_pos
115
- Parser::Source::Range.new(buffer, begin_pos, end_pos)
117
+ # Collect the entire else branch, including whitespace and comments
118
+ def else_range(node)
119
+ if node.ternary?
120
+ node.else_branch
121
+ else
122
+ range_between(node.loc.else.end_pos, node.loc.end.begin_pos)
123
+ end
116
124
  end
117
125
  end
118
126
  end
@@ -68,6 +68,7 @@ module RuboCop
68
68
  return false unless endless_method_config['Enabled']
69
69
  return false if endless_method_config['EnforcedStyle'] == 'disallow'
70
70
  return false unless body_node
71
+ return false if body_node.parent.assignment_method?
71
72
 
72
73
  !(body_node.begin_type? || body_node.kwbegin_type?)
73
74
  end
@@ -115,15 +116,19 @@ module RuboCop
115
116
  end
116
117
 
117
118
  def method_body_source(method_body)
118
- if !method_body.send_type? || method_body.arguments.empty? || method_body.parenthesized?
119
- method_body.source
120
- else
119
+ if require_parentheses?(method_body)
121
120
  arguments_source = method_body.arguments.map(&:source).join(', ')
122
121
  body_source = "#{method_body.method_name}(#{arguments_source})"
123
122
 
124
123
  method_body.receiver ? "#{method_body.receiver.source}.#{body_source}" : body_source
124
+ else
125
+ method_body.source
125
126
  end
126
127
  end
128
+
129
+ def require_parentheses?(method_body)
130
+ method_body.send_type? && !method_body.arguments.empty? && !method_body.comparison_method?
131
+ end
127
132
  end
128
133
  end
129
134
  end
@@ -80,10 +80,7 @@ module RuboCop
80
80
  def autocorrect(corrector, node, if_branch)
81
81
  corrector.wrap(node.condition, '(', ')') if node.condition.or_type?
82
82
 
83
- if node.unless?
84
- corrector.replace(node.loc.keyword, 'if')
85
- corrector.insert_before(node.condition, '!')
86
- end
83
+ correct_from_unless_to_if(corrector, node) if node.unless?
87
84
 
88
85
  and_operator = if_branch.unless? ? ' && !' : ' && '
89
86
  if if_branch.modifier_form?
@@ -94,6 +91,17 @@ module RuboCop
94
91
  end
95
92
  end
96
93
 
94
+ def correct_from_unless_to_if(corrector, node)
95
+ corrector.replace(node.loc.keyword, 'if')
96
+
97
+ condition = node.condition
98
+ if condition.send_type? && condition.comparison_method? && !condition.parenthesized?
99
+ corrector.wrap(node.condition, '!(', ')')
100
+ else
101
+ corrector.insert_before(node.condition, '!')
102
+ end
103
+ end
104
+
97
105
  def correct_for_guard_condition_style(corrector, node, if_branch, and_operator)
98
106
  outer_condition = node.condition
99
107
  correct_outer_condition(corrector, outer_condition)
@@ -136,7 +144,8 @@ module RuboCop
136
144
  end
137
145
 
138
146
  def requrie_parentheses?(condition)
139
- condition.send_type? && !condition.arguments.empty? && !condition.parenthesized?
147
+ condition.send_type? && !condition.arguments.empty? && !condition.parenthesized? &&
148
+ !condition.comparison_method?
140
149
  end
141
150
 
142
151
  def arguments_range(node)
@@ -226,6 +226,21 @@ module RuboCop
226
226
  end
227
227
  end
228
228
 
229
+ # case target
230
+ # in pattern # in_pattern
231
+ # else
232
+ # else_body
233
+ # end
234
+ class CaseMatch < Base
235
+ define_predicate :target?, child_index: 0
236
+ define_predicate :in_pattern?, child_index: 1..-2
237
+ define_predicate :else_body?, child_index: -1
238
+
239
+ def always_run?
240
+ target?
241
+ end
242
+ end
243
+
229
244
  # for element in collection
230
245
  # loop_body
231
246
  # end
@@ -131,3 +131,7 @@ end
131
131
  RSpec.shared_context 'ruby 3.0', :ruby30 do
132
132
  let(:ruby_version) { 3.0 }
133
133
  end
134
+
135
+ RSpec.shared_context 'ruby 3.1', :ruby31 do
136
+ let(:ruby_version) { 3.1 }
137
+ end
@@ -100,12 +100,19 @@ module RuboCop
100
100
  next true if dir.end_with?('/./', '/../')
101
101
  next true if File.fnmatch?(exclude_pattern, dir, flags)
102
102
 
103
- File.symlink?(dir.chomp('/')) && File.fnmatch?(exclude_pattern,
104
- "#{File.realpath(dir)}/", flags)
103
+ symlink_excluded_or_infinite_loop?(base_dir, dir, exclude_pattern, flags)
105
104
  end
106
105
  dirs.flat_map { |dir| wanted_dir_patterns(dir, exclude_pattern, flags) }.unshift(base_dir)
107
106
  end
108
107
 
108
+ def symlink_excluded_or_infinite_loop?(base_dir, current_dir, exclude_pattern, flags)
109
+ dir_realpath = File.realpath(current_dir)
110
+ File.symlink?(current_dir.chomp('/')) && (
111
+ File.fnmatch?(exclude_pattern, "#{dir_realpath}/", flags) ||
112
+ File.realpath(base_dir).start_with?(dir_realpath)
113
+ )
114
+ end
115
+
109
116
  def combined_exclude_glob_patterns(base_dir)
110
117
  exclude = @config_store.for(base_dir).for_all_cops['Exclude']
111
118
  patterns = exclude.select { |pattern| pattern.is_a?(String) && pattern.end_with?('/**/*') }
@@ -4,7 +4,7 @@ module RuboCop
4
4
  # The kind of Ruby that code inspected by RuboCop is written in.
5
5
  # @api private
6
6
  class TargetRuby
7
- KNOWN_RUBIES = [2.5, 2.6, 2.7, 3.0].freeze
7
+ KNOWN_RUBIES = [2.5, 2.6, 2.7, 3.0, 3.1].freeze
8
8
  DEFAULT_VERSION = KNOWN_RUBIES.first
9
9
 
10
10
  OBSOLETE_RUBIES = {
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.13.0'
6
+ STRING = '1.14.0'
7
7
 
8
8
  MSG = '%<version>s (using Parser %<parser_version>s, '\
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.13.0
4
+ version: 1.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2021-04-20 00:00:00.000000000 Z
13
+ date: 2021-05-05 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: parallel
@@ -100,7 +100,7 @@ dependencies:
100
100
  requirements:
101
101
  - - ">="
102
102
  - !ruby/object:Gem::Version
103
- version: 1.2.0
103
+ version: 1.5.0
104
104
  - - "<"
105
105
  - !ruby/object:Gem::Version
106
106
  version: '2.0'
@@ -110,7 +110,7 @@ dependencies:
110
110
  requirements:
111
111
  - - ">="
112
112
  - !ruby/object:Gem::Version
113
- version: 1.2.0
113
+ version: 1.5.0
114
114
  - - "<"
115
115
  - !ruby/object:Gem::Version
116
116
  version: '2.0'
@@ -221,6 +221,7 @@ files:
221
221
  - lib/rubocop/cop/base.rb
222
222
  - lib/rubocop/cop/bundler/duplicated_gem.rb
223
223
  - lib/rubocop/cop/bundler/gem_comment.rb
224
+ - lib/rubocop/cop/bundler/gem_version.rb
224
225
  - lib/rubocop/cop/bundler/insecure_protocol_source.rb
225
226
  - lib/rubocop/cop/bundler/ordered_gems.rb
226
227
  - lib/rubocop/cop/commissioner.rb
@@ -333,6 +334,7 @@ files:
333
334
  - lib/rubocop/cop/layout/parameter_alignment.rb
334
335
  - lib/rubocop/cop/layout/redundant_line_break.rb
335
336
  - lib/rubocop/cop/layout/rescue_ensure_alignment.rb
337
+ - lib/rubocop/cop/layout/single_line_block_chain.rb
336
338
  - lib/rubocop/cop/layout/space_after_colon.rb
337
339
  - lib/rubocop/cop/layout/space_after_comma.rb
338
340
  - lib/rubocop/cop/layout/space_after_method_name.rb
@@ -524,6 +526,7 @@ files:
524
526
  - lib/rubocop/cop/mixin/enforce_superclass.rb
525
527
  - lib/rubocop/cop/mixin/first_element_line_break.rb
526
528
  - lib/rubocop/cop/mixin/frozen_string_literal.rb
529
+ - lib/rubocop/cop/mixin/gem_declaration.rb
527
530
  - lib/rubocop/cop/mixin/hash_alignment_styles.rb
528
531
  - lib/rubocop/cop/mixin/hash_transform_method.rb
529
532
  - lib/rubocop/cop/mixin/heredoc.rb
@@ -872,7 +875,7 @@ metadata:
872
875
  homepage_uri: https://rubocop.org/
873
876
  changelog_uri: https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md
874
877
  source_code_uri: https://github.com/rubocop/rubocop/
875
- documentation_uri: https://docs.rubocop.org/rubocop/1.13/
878
+ documentation_uri: https://docs.rubocop.org/rubocop/1.14/
876
879
  bug_tracker_uri: https://github.com/rubocop/rubocop/issues
877
880
  post_install_message:
878
881
  rdoc_options: []