rubocop 1.13.0 → 1.14.0

Sign up to get free protection for your applications and to get access to all the features.
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: []