rubocop 1.81.7 → 1.82.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.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +21 -5
  4. data/config/obsoletion.yml +4 -0
  5. data/lib/rubocop/cli.rb +2 -1
  6. data/lib/rubocop/comment_config.rb +62 -17
  7. data/lib/rubocop/config_loader.rb +2 -1
  8. data/lib/rubocop/config_loader_resolver.rb +2 -2
  9. data/lib/rubocop/cop/autocorrect_logic.rb +2 -0
  10. data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -3
  11. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +10 -5
  12. data/lib/rubocop/cop/internal_affairs/location_exists.rb +28 -2
  13. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +1 -1
  14. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +3 -0
  15. data/lib/rubocop/cop/layout/end_alignment.rb +4 -0
  16. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  17. data/lib/rubocop/cop/layout/heredoc_indentation.rb +0 -4
  18. data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
  19. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  20. data/lib/rubocop/cop/layout/line_length.rb +7 -4
  21. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +5 -3
  22. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  23. data/lib/rubocop/cop/lint/circular_argument_reference.rb +47 -3
  24. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +1 -1
  25. data/lib/rubocop/cop/lint/else_layout.rb +19 -0
  26. data/lib/rubocop/cop/lint/literal_as_condition.rb +4 -0
  27. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  28. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +4 -0
  29. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +23 -9
  30. data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -2
  31. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +7 -1
  32. data/lib/rubocop/cop/lint/self_assignment.rb +9 -1
  33. data/lib/rubocop/cop/lint/unreachable_code.rb +5 -3
  34. data/lib/rubocop/cop/lint/useless_or.rb +15 -2
  35. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +1 -1
  36. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -3
  37. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +0 -4
  38. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  39. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +1 -1
  40. data/lib/rubocop/cop/mixin/line_length_help.rb +21 -2
  41. data/lib/rubocop/cop/mixin/method_complexity.rb +1 -1
  42. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  43. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
  44. data/lib/rubocop/cop/mixin/statement_modifier.rb +0 -6
  45. data/lib/rubocop/cop/mixin/trailing_comma.rb +4 -5
  46. data/lib/rubocop/cop/naming/method_name.rb +1 -1
  47. data/lib/rubocop/cop/style/bare_percent_literals.rb +1 -2
  48. data/lib/rubocop/cop/style/case_equality.rb +11 -13
  49. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -0
  50. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -11
  51. data/lib/rubocop/cop/style/constant_visibility.rb +3 -3
  52. data/lib/rubocop/cop/style/empty_method.rb +0 -6
  53. data/lib/rubocop/cop/style/endless_method.rb +2 -2
  54. data/lib/rubocop/cop/style/guard_clause.rb +0 -11
  55. data/lib/rubocop/cop/style/if_unless_modifier.rb +3 -3
  56. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +12 -1
  57. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +17 -4
  58. data/lib/rubocop/cop/style/module_member_existence_check.rb +74 -0
  59. data/lib/rubocop/cop/style/multiline_method_signature.rb +0 -4
  60. data/lib/rubocop/cop/style/parallel_assignment.rb +2 -2
  61. data/lib/rubocop/cop/style/redundant_argument.rb +2 -0
  62. data/lib/rubocop/cop/style/redundant_percent_q.rb +1 -2
  63. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +5 -0
  64. data/lib/rubocop/cop/style/redundant_sort.rb +7 -7
  65. data/lib/rubocop/cop/style/super_arguments.rb +2 -2
  66. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +11 -11
  67. data/lib/rubocop/cop/util.rb +2 -3
  68. data/lib/rubocop/directive_comment.rb +46 -3
  69. data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -0
  70. data/lib/rubocop/lsp/diagnostic.rb +12 -17
  71. data/lib/rubocop/lsp/routes.rb +9 -36
  72. data/lib/rubocop/lsp/runtime.rb +2 -2
  73. data/lib/rubocop/lsp/server.rb +2 -2
  74. data/lib/rubocop/magic_comment.rb +20 -0
  75. data/lib/rubocop/rake_task.rb +1 -1
  76. data/lib/rubocop/remote_config.rb +7 -8
  77. data/lib/rubocop/rspec/shared_contexts.rb +2 -2
  78. data/lib/rubocop/rspec/support.rb +1 -1
  79. data/lib/rubocop/target_ruby.rb +1 -1
  80. data/lib/rubocop/version.rb +1 -1
  81. data/lib/rubocop.rb +1 -0
  82. metadata +7 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8acfafa4ed4b94c367e6ef95c84d2d02f01222a1e8a813790948977476f34456
4
- data.tar.gz: 0d2ad56c4a81da58046a098351606bfb8314f09f9e5f946ad1dd825aa70fe0f7
3
+ metadata.gz: 395ade8767ec51e33acbc9c14c0b11300df6100806f9e2c9bd23241f22f7f206
4
+ data.tar.gz: ed5bf8c5e1f3f7adc724fd900ec7d8b0cda4c91892276349177e7eb7c06d87a7
5
5
  SHA512:
6
- metadata.gz: ae4edea22ca7511df39ec99591df3d537bfd3fa8974d5bb461d2b1c674b48f15296f22c81b1cd9329719a68b0d3f8d36442eaa5742cbf8e0974c6e3e2e56e29c
7
- data.tar.gz: ffce467cec50d2ed0ef7b312a15dd17e9b7fd1622b70528d12f0cfe16056ae3b5111f2d360b4e855ea5811befd092a306ff56ad091b89a6050f3354f3db205a4
6
+ metadata.gz: 53fd7c4276518804677378c9c958c96c5512d9b3cfa89c0a5a8597a676a54efb18077a45e30d47f15c15a08448c9ad8c6b5c931cc0e244bb34abef88b5d30238
7
+ data.tar.gz: e7298eed68c7cb9d9d7c294bc7ca031b538ea1a3205bb34ab854ef76bf1230a4e664c178088f1489030f51dd1ad4f9e1bfd1dc4397a5a240904b27bc55965428
data/README.md CHANGED
@@ -51,7 +51,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi
51
51
  in your `Gemfile`:
52
52
 
53
53
  ```rb
54
- gem 'rubocop', '~> 1.81', require: false
54
+ gem 'rubocop', '~> 1.82', require: false
55
55
  ```
56
56
 
57
57
  See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
data/config/default.yml CHANGED
@@ -1102,7 +1102,7 @@ Layout/LineLength:
1102
1102
  StyleGuide: '#max-line-length'
1103
1103
  Enabled: true
1104
1104
  VersionAdded: '0.25'
1105
- VersionChanged: '1.69'
1105
+ VersionChanged: '1.82'
1106
1106
  Max: 120
1107
1107
  AllowHeredoc: true
1108
1108
  # To make it possible to copy or click on URIs in the code, we allow lines
@@ -1112,9 +1112,12 @@ Layout/LineLength:
1112
1112
  URISchemes:
1113
1113
  - http
1114
1114
  - https
1115
- # The IgnoreCopDirectives option causes the LineLength rule to ignore cop
1115
+ # The AllowRBSInlineAnnotation option makes LineLength allow RBS::Inline annotations
1116
+ # such as `#: (?date: Date) -> Foo` when calculating line length.
1117
+ AllowRBSInlineAnnotation: false
1118
+ # The AllowCopDirectives option causes the LineLength rule to ignore cop
1116
1119
  # directives like '# rubocop: enable ...' when calculating a line's length.
1117
- IgnoreCopDirectives: true
1120
+ AllowCopDirectives: true
1118
1121
  # The AllowedPatterns option is a list of !ruby/regexp and/or string
1119
1122
  # elements. Strings will be converted to Regexp objects. A line that matches
1120
1123
  # any regular expression listed in this option will be ignored by LineLength.
@@ -2669,7 +2672,10 @@ Lint/UselessNumericOperation:
2669
2672
  Lint/UselessOr:
2670
2673
  Description: 'Checks for useless OR expressions.'
2671
2674
  Enabled: pending
2675
+ SafeAutoCorrect: false
2676
+ AutoCorrect: contextual
2672
2677
  VersionAdded: '1.76'
2678
+ VersionChanged: '1.82'
2673
2679
 
2674
2680
  Lint/UselessRescue:
2675
2681
  Description: 'Checks for useless `rescue`s.'
@@ -3281,7 +3287,7 @@ Style/Alias:
3281
3287
 
3282
3288
  Style/AmbiguousEndlessMethodDefinition:
3283
3289
  Description: 'Checks for endless methods inside operators of lower precedence.'
3284
- StyleGuide: '#ambiguous-endless-method-defintions'
3290
+ StyleGuide: '#ambiguous-endless-method-definitions'
3285
3291
  Enabled: pending
3286
3292
  VersionAdded: '1.68'
3287
3293
 
@@ -3525,9 +3531,11 @@ Style/BlockDelimiters:
3525
3531
  BracesRequiredMethods: []
3526
3532
 
3527
3533
  Style/CaseEquality:
3528
- Description: 'Avoid explicit use of the case equality operator(===).'
3534
+ Description: 'Avoid explicit use of the case equality operator (`===`).'
3529
3535
  StyleGuide: '#no-case-equality'
3530
3536
  Enabled: true
3537
+ References:
3538
+ - 'https://docs.rubocop.org/rubocop-performance/cops_performance.html#performanceregexpmatch'
3531
3539
  VersionAdded: '0.9'
3532
3540
  VersionChanged: '0.89'
3533
3541
  # If `AllowOnConstant` option is enabled, the cop will ignore violations when the receiver of
@@ -4625,6 +4633,7 @@ Style/MethodCallWithArgsParentheses:
4625
4633
  AllowedMethods: []
4626
4634
  AllowedPatterns: []
4627
4635
  IncludedMacros: []
4636
+ IncludedMacroPatterns: []
4628
4637
  AllowParenthesesInMultilineCall: false
4629
4638
  AllowParenthesesInChaining: false
4630
4639
  AllowParenthesesInCamelCaseMethod: false
@@ -4735,6 +4744,11 @@ Style/ModuleFunction:
4735
4744
  Autocorrect: false
4736
4745
  SafeAutoCorrect: false
4737
4746
 
4747
+ Style/ModuleMemberExistenceCheck:
4748
+ Description: 'Checks for usage of `Module` methods returning arrays that can be replaced with equivalent predicates.'
4749
+ Enabled: pending
4750
+ VersionAdded: '1.82'
4751
+
4738
4752
  Style/MultilineBlockChain:
4739
4753
  Description: 'Avoid multi-line chains of blocks.'
4740
4754
  StyleGuide: '#single-line-blocks'
@@ -5222,6 +5236,8 @@ Style/RedundantArgument:
5222
5236
  exit: true
5223
5237
  # Kernel.#exit!
5224
5238
  exit!: false
5239
+ # String#to_i
5240
+ to_i: 10
5225
5241
  # String#split
5226
5242
  split: ' '
5227
5243
  # String#chomp
@@ -145,6 +145,10 @@ changed_parameters:
145
145
  - cops: Layout/CaseIndentation
146
146
  parameters: IndentWhenRelativeTo
147
147
  alternative: EnforcedStyle
148
+ - cops: Layout/LineLength
149
+ parameters: IgnoreCopDirectives
150
+ alternative: AllowCopDirectives
151
+ severity: warning
148
152
  - cops:
149
153
  - Lint/BlockAlignment
150
154
  - Layout/BlockAlignment
data/lib/rubocop/cli.rb CHANGED
@@ -66,7 +66,7 @@ module RuboCop
66
66
  STATUS_INTERRUPTED
67
67
  rescue Finished
68
68
  STATUS_SUCCESS
69
- rescue OptionParser::InvalidOption => e
69
+ rescue OptionParser::ParseError => e
70
70
  warn e.message
71
71
  warn 'For usage information, use --help'
72
72
  STATUS_ERROR
@@ -183,6 +183,7 @@ module RuboCop
183
183
  ConfigLoader.enable_pending_cops = @options[:enable_pending_cops]
184
184
  ConfigLoader.ignore_parent_exclusion = @options[:ignore_parent_exclusion]
185
185
  ConfigLoader.ignore_unrecognized_cops = @options[:ignore_unrecognized_cops]
186
+ ConfigLoader.cache_root = ResultCache.cache_root(@config_store, @options[:cache_root])
186
187
  end
187
188
 
188
189
  def set_options_to_pending_cops_reporter
@@ -34,6 +34,7 @@ module RuboCop
34
34
  def initialize(processed_source)
35
35
  @processed_source = processed_source
36
36
  @no_directives = !processed_source.raw_source.include?('rubocop')
37
+ @stack = []
37
38
  end
38
39
 
39
40
  def cop_enabled_at_line?(cop, line_number)
@@ -93,16 +94,23 @@ module RuboCop
93
94
  end
94
95
  end
95
96
 
96
- def analyze # rubocop:todo Metrics/AbcSize
97
+ def analyze # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
97
98
  return {} if @no_directives
98
99
 
99
100
  analyses = Hash.new { |hash, key| hash[key] = CopAnalysis.new([], nil) }
100
101
  inject_disabled_cops_directives(analyses)
101
102
 
102
103
  each_directive do |directive|
103
- directive.cop_names.each do |cop_name|
104
- cop_name = qualified_cop_name(cop_name)
105
- analyses[cop_name] = analyze_cop(analyses[cop_name], directive)
104
+ if directive.push?
105
+ @stack.push(snapshot_analyses(analyses))
106
+ apply_push_args(analyses, directive)
107
+ elsif directive.pop?
108
+ pop_state(analyses, directive.line_number) if @stack.any?
109
+ else
110
+ directive.cop_names.each do |cop_name|
111
+ cop_name = qualified_cop_name(cop_name)
112
+ analyses[cop_name] = analyze_cop(analyses[cop_name], directive)
113
+ end
106
114
  end
107
115
  end
108
116
 
@@ -112,6 +120,51 @@ module RuboCop
112
120
  end
113
121
  end
114
122
 
123
+ def snapshot_analyses(analyses)
124
+ analyses.transform_values { |a| CopAnalysis.new(a.line_ranges.dup, a.start_line_number) }
125
+ end
126
+
127
+ def pop_state(analyses, pop_line)
128
+ analyses.each do |cop_name, analysis|
129
+ next unless analysis.start_line_number
130
+
131
+ analyses[cop_name] = CopAnalysis.new(
132
+ analysis.line_ranges + [analysis.start_line_number...pop_line], nil
133
+ )
134
+ end
135
+
136
+ @stack.pop.each do |cop_name, saved_analysis|
137
+ current = analyses[cop_name]
138
+ new_start = saved_analysis.start_line_number ? pop_line : nil
139
+ analyses[cop_name] = CopAnalysis.new(current.line_ranges, new_start)
140
+ end
141
+ end
142
+
143
+ def apply_push_args(analyses, directive)
144
+ directive.push_args.each do |operation, cop_names|
145
+ cop_names.each do |cop_name|
146
+ apply_cop_operation(analyses, operation, qualified_cop_name(cop_name),
147
+ directive.line_number)
148
+ end
149
+ end
150
+ end
151
+
152
+ def apply_cop_operation(analyses, operation, cop_name, line)
153
+ analysis = analyses[cop_name]
154
+ start_line = analysis.start_line_number
155
+
156
+ case operation
157
+ when '+' # Enable cop
158
+ return unless start_line
159
+
160
+ analyses[cop_name] = CopAnalysis.new(analysis.line_ranges + [start_line..line], nil)
161
+ when '-' # Disable cop
162
+ return if start_line
163
+
164
+ analyses[cop_name] = CopAnalysis.new(analysis.line_ranges, line)
165
+ end
166
+ end
167
+
115
168
  def inject_disabled_cops_directives(analyses)
116
169
  registry.disabled(config).each do |cop|
117
170
  analyses[cop.cop_name] = analyze_cop(
@@ -136,29 +189,21 @@ module RuboCop
136
189
  return analysis unless directive.disabled?
137
190
 
138
191
  line = directive.line_number
139
- start_line = analysis.start_line_number
140
-
141
- CopAnalysis.new(analysis.line_ranges + [(line..line)], start_line)
192
+ CopAnalysis.new(analysis.line_ranges + [(line..line)], analysis.start_line_number)
142
193
  end
143
194
 
144
195
  def analyze_disabled(analysis, directive)
145
196
  line = directive.line_number
146
197
  start_line = analysis.start_line_number
147
-
148
- # Cop already disabled on this line, so we end the current disabled
149
- # range before we start a new range.
150
- return CopAnalysis.new(analysis.line_ranges + [start_line..line], line) if start_line
151
-
152
- CopAnalysis.new(analysis.line_ranges, line)
198
+ new_ranges = start_line ? analysis.line_ranges + [start_line..line] : analysis.line_ranges
199
+ CopAnalysis.new(new_ranges, line)
153
200
  end
154
201
 
155
202
  def analyze_rest(analysis, directive)
156
203
  line = directive.line_number
157
204
  start_line = analysis.start_line_number
158
-
159
- return CopAnalysis.new(analysis.line_ranges + [start_line..line], nil) if start_line
160
-
161
- CopAnalysis.new(analysis.line_ranges, nil)
205
+ new_ranges = start_line ? analysis.line_ranges + [start_line..line] : analysis.line_ranges
206
+ CopAnalysis.new(new_ranges, nil)
162
207
  end
163
208
 
164
209
  def cop_line_ranges(analysis)
@@ -23,7 +23,7 @@ module RuboCop
23
23
  include FileFinder
24
24
 
25
25
  attr_accessor :debug, :ignore_parent_exclusion, :disable_pending_cops, :enable_pending_cops,
26
- :ignore_unrecognized_cops
26
+ :ignore_unrecognized_cops, :cache_root
27
27
  attr_writer :default_configuration
28
28
  attr_reader :loaded_plugins, :loaded_features
29
29
 
@@ -38,6 +38,7 @@ module RuboCop
38
38
  @enable_pending_cops = nil
39
39
  @ignore_parent_exclusion = nil
40
40
  @ignore_unrecognized_cops = nil
41
+ @cache_root = nil
41
42
  FileFinder.root_level = nil
42
43
  end
43
44
 
@@ -246,7 +246,7 @@ module RuboCop
246
246
  def inherited_file(path, inherit_from, file)
247
247
  if PathUtil.remote_file?(inherit_from)
248
248
  # A remote configuration, e.g. `inherit_from: http://example.com/rubocop.yml`.
249
- RemoteConfig.new(inherit_from, File.dirname(path))
249
+ RemoteConfig.new(inherit_from, ConfigLoader.cache_root)
250
250
  elsif Pathname.new(inherit_from).absolute?
251
251
  # An absolute path to a config, e.g. `inherit_from: /Users/me/rubocop.yml`.
252
252
  # The path may come from `inherit_gem` option, where a gem name is expanded
@@ -256,7 +256,7 @@ module RuboCop
256
256
  elsif file.is_a?(RemoteConfig)
257
257
  # A path relative to a URL, e.g. `inherit_from: configs/default.yml`
258
258
  # in a config included with `inherit_from: http://example.com/rubocop.yml`
259
- file.inherit_from_remote(inherit_from, path)
259
+ file.inherit_from_remote(inherit_from)
260
260
  else
261
261
  # A local relative path, e.g. `inherit_from: default.yml`
262
262
  print 'Inheriting ' if ConfigLoader.debug?
@@ -133,6 +133,8 @@ module RuboCop
133
133
  end
134
134
 
135
135
  def max_line_length
136
+ return unless config.cop_enabled?('Layout/LineLength')
137
+
136
138
  config.for_cop('Layout/LineLength')['Max'] || 120
137
139
  end
138
140
 
@@ -76,9 +76,7 @@ module RuboCop
76
76
  # nil.
77
77
  # - The source map of `__FILE__` responds to neither :begin nor :end.
78
78
  def delimited_string_literal?(node)
79
- loc = node.location
80
-
81
- loc.respond_to?(:begin) && loc.begin && loc.respond_to?(:end) && loc.end
79
+ node.loc?(:begin) && node.loc?(:end)
82
80
  end
83
81
 
84
82
  def block_comment_within?(expr)
@@ -3,8 +3,8 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Gemspec
6
- # Checks that `RUBY_VERSION` constant is not used in gemspec.
7
- # Using `RUBY_VERSION` is dangerous because value of the
6
+ # Checks that `RUBY_VERSION` and `Ruby::VERSION` constants are not used in gemspec.
7
+ # Using `RUBY_VERSION` and `Ruby::VERSION` are dangerous because value of the
8
8
  # constant is determined by `rake release`.
9
9
  # It's possible to have dependency based on ruby version used
10
10
  # to execute `rake release` and not user's ruby version.
@@ -28,15 +28,20 @@ module RuboCop
28
28
  class RubyVersionGlobalsUsage < Base
29
29
  include GemspecHelp
30
30
 
31
- MSG = 'Do not use `RUBY_VERSION` in gemspec file.'
31
+ MSG = 'Do not use `%<ruby_version>s` in gemspec file.'
32
32
 
33
33
  # @!method ruby_version?(node)
34
- def_node_matcher :ruby_version?, '(const {cbase nil?} :RUBY_VERSION)'
34
+ def_node_matcher :ruby_version?, <<~PATTERN
35
+ {
36
+ (const {cbase nil?} :RUBY_VERSION)
37
+ (const (const {cbase nil?} :Ruby) :VERSION)
38
+ }
39
+ PATTERN
35
40
 
36
41
  def on_const(node)
37
42
  return unless gem_spec_with_ruby_version?(node)
38
43
 
39
- add_offense(node)
44
+ add_offense(node, message: format(MSG, ruby_version: node.source))
40
45
  end
41
46
 
42
47
  private
@@ -9,6 +9,12 @@ module RuboCop
9
9
  #
10
10
  # @example
11
11
  # # bad
12
+ # node.loc.respond_to?(:begin)
13
+ #
14
+ # # good
15
+ # node.loc?(:begin)
16
+ #
17
+ # # bad
12
18
  # node.loc.respond_to?(:begin) && node.loc.begin
13
19
  #
14
20
  # # good
@@ -29,7 +35,16 @@ module RuboCop
29
35
  class LocationExists < Base
30
36
  extend AutoCorrector
31
37
 
32
- MSG = 'Use `%<replacement>s` instead of `%<source>s`.'
38
+ MSG = 'Use `node.loc?` instead of `loc.respond_to?`.'
39
+ MSG_CORRECTABLE = 'Use `%<replacement>s` instead of `%<source>s`.'
40
+ RESTRICT_ON_SEND = %i[respond_to?].freeze
41
+
42
+ # @!method loc_respond_to?(node)
43
+ def_node_matcher :loc_respond_to?, <<~PATTERN
44
+ (call
45
+ (call $_receiver :loc) :respond_to?
46
+ $(sym _location))
47
+ PATTERN
33
48
 
34
49
  # @!method replaceable_with_loc_is(node)
35
50
  def_node_matcher :replaceable_with_loc_is, <<~PATTERN
@@ -64,6 +79,15 @@ module RuboCop
64
79
  replace_with_loc(node) || replace_with_loc_is(node)
65
80
  end
66
81
 
82
+ def on_send(node)
83
+ return if ignored_node?(node.parent)
84
+
85
+ loc_respond_to?(node) do |receiver, location|
86
+ register_offense(node, replacement(receiver, "loc?(#{location.source})"))
87
+ end
88
+ end
89
+ alias on_csend on_send
90
+
67
91
  private
68
92
 
69
93
  def replace_with_loc(node)
@@ -84,11 +108,13 @@ module RuboCop
84
108
  end
85
109
 
86
110
  def register_offense(node, replacement)
87
- message = format(MSG, replacement: replacement, source: node.source)
111
+ message = format(MSG_CORRECTABLE, replacement: replacement, source: node.source)
88
112
 
89
113
  add_offense(node, message: message) do |corrector|
90
114
  corrector.replace(node, replacement)
91
115
  end
116
+
117
+ ignore_node(node)
92
118
  end
93
119
 
94
120
  def replacement(receiver, rest)
@@ -7,7 +7,7 @@ module RuboCop
7
7
  # AST Processor for NodePattern ASTs, for use with `InternalAffairs/NodePatternGroups`.
8
8
  #
9
9
  # Looks for sequences and subsequences where the first item is a `node_type` node,
10
- # and converts them to `node_sequence` nodes (not a true `Rubocop::AST::NodePattern`
10
+ # and converts them to `node_sequence` nodes (not a true `RuboCop::AST::NodePattern`
11
11
  # node type).
12
12
  #
13
13
  # The resulting AST will be walked by `InternalAffairs::NodePatternGroups::ASTWalker`
@@ -145,8 +145,10 @@ module RuboCop
145
145
  next_sibling.if_type? && contains_guard_clause?(next_sibling)
146
146
  end
147
147
 
148
+ # rubocop:disable Metrics/CyclomaticComplexity
148
149
  def last_heredoc_argument(node)
149
150
  n = last_heredoc_argument_node(node)
151
+ n = n.children.first while n.respond_to?(:begin_type?) && n.begin_type?
150
152
 
151
153
  return n if heredoc?(n)
152
154
  return unless n.respond_to?(:arguments)
@@ -158,6 +160,7 @@ module RuboCop
158
160
 
159
161
  last_heredoc_argument(n.receiver) if n.respond_to?(:receiver)
160
162
  end
163
+ # rubocop:enable Metrics/CyclomaticComplexity
161
164
 
162
165
  def last_heredoc_argument_node(node)
163
166
  return node unless node.respond_to?(:if_branch)
@@ -128,6 +128,10 @@ module RuboCop
128
128
  # assignment, we let rhs be the receiver of those method calls before
129
129
  # we check if it's an if/unless/while/until.
130
130
  return unless (rhs = first_part_of_call_chain(rhs))
131
+
132
+ # If `rhs` is a `begin` node, find the first non-`begin` child.
133
+ rhs = rhs.child_nodes.first while rhs.begin_type?
134
+
131
135
  return unless rhs.conditional?
132
136
  return if rhs.if_type? && rhs.ternary?
133
137
 
@@ -168,7 +168,7 @@ module RuboCop
168
168
 
169
169
  def subsequent_closing_parentheses_in_same_line?(outermost_send)
170
170
  last_arg_of_outer_send = outermost_send.last_argument
171
- return false unless last_arg_of_outer_send&.loc.respond_to?(:end) &&
171
+ return false unless last_arg_of_outer_send&.loc?(:end) &&
172
172
  (end_of_last_arg_of_outer_send = last_arg_of_outer_send.loc.end)
173
173
 
174
174
  end_of_outer_send = outermost_send.loc.end
@@ -230,7 +230,7 @@ module RuboCop
230
230
 
231
231
  def find_most_bottom_of_heredoc_end(arguments)
232
232
  arguments.filter_map do |argument|
233
- argument.loc.heredoc_end.end_pos if argument.loc.respond_to?(:heredoc_end)
233
+ argument.loc.heredoc_end.end_pos if argument.loc?(:heredoc_end)
234
234
  end.max
235
235
  end
236
236
 
@@ -108,10 +108,6 @@ module RuboCop
108
108
  config.for_cop('Layout/LineLength')['AllowHeredoc']
109
109
  end
110
110
 
111
- def max_line_length
112
- config.for_cop('Layout/LineLength')['Max']
113
- end
114
-
115
111
  def adjust_squiggly(corrector, node)
116
112
  corrector.replace(node.loc.heredoc_body, indented_body(node))
117
113
  corrector.replace(node.loc.heredoc_end, indented_end(node))
@@ -99,7 +99,7 @@ module RuboCop
99
99
 
100
100
  if str.heredoc?
101
101
  ranges << loc.heredoc_body
102
- elsif loc.respond_to?(:begin) && loc.begin
102
+ elsif str.loc?(:begin)
103
103
  ranges << loc.expression
104
104
  end
105
105
  end
@@ -101,7 +101,7 @@ module RuboCop
101
101
  ranges << loc.expression
102
102
  elsif literal.heredoc?
103
103
  ranges << loc.heredoc_body
104
- elsif (loc.respond_to?(:begin) && loc.begin) || ignored_parent?(literal)
104
+ elsif literal.loc?(:begin) || ignored_parent?(literal)
105
105
  ranges << loc.expression
106
106
  end
107
107
  end
@@ -252,17 +252,22 @@ module RuboCop
252
252
  [max - indentation_difference(line), 0].max
253
253
  end
254
254
 
255
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
255
256
  def check_line(line, line_index)
256
257
  return if line_length(line) <= max
257
258
  return if allowed_line?(line, line_index)
259
+ if allow_rbs_inline_annotation? && rbs_inline_annotation_on_source_line?(line_index)
260
+ return
261
+ end
258
262
 
259
- if ignore_cop_directives? && directive_on_source_line?(line_index)
263
+ if allow_cop_directives? && directive_on_source_line?(line_index)
260
264
  return check_directive_line(line, line_index)
261
265
  end
262
266
  return check_line_for_exemptions(line, line_index) if allow_uri? || allow_qualified_name?
263
267
 
264
268
  register_offense(excess_range(nil, line, line_index), line, line_index)
265
269
  end
270
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
266
271
 
267
272
  def allowed_line?(line, line_index)
268
273
  matches_allowed_pattern?(line) ||
@@ -401,9 +406,7 @@ module RuboCop
401
406
 
402
407
  def string_delimiter(node)
403
408
  delimiter = node.loc.begin
404
- if node.parent&.dstr_type? && node.parent.loc.respond_to?(:begin)
405
- delimiter ||= node.parent.loc.begin
406
- end
409
+ delimiter ||= node.parent.loc.begin if node.parent&.dstr_type? && node.parent.loc?(:begin)
407
410
  delimiter = delimiter&.source
408
411
 
409
412
  delimiter if %w[' "].include?(delimiter)
@@ -29,7 +29,7 @@ module RuboCop
29
29
  MSG = '`%<kw_loc>s` at %<kw_loc_line>d, %<kw_loc_column>d is not ' \
30
30
  'aligned with `%<beginning>s` at ' \
31
31
  '%<begin_loc_line>d, %<begin_loc_column>d.'
32
- ANCESTOR_TYPES = %i[kwbegin any_def class module any_block].freeze
32
+ ANCESTOR_TYPES = %i[kwbegin any_def class module sclass any_block].freeze
33
33
  ALTERNATIVE_ACCESS_MODIFIERS = %i[public_class_method private_class_method].freeze
34
34
 
35
35
  def on_resbody(node)
@@ -91,7 +91,7 @@ module RuboCop
91
91
  )
92
92
  end
93
93
 
94
- # rubocop:disable Metrics/AbcSize
94
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
95
95
  def alignment_source(node, starting_loc)
96
96
  ending_loc =
97
97
  case node.type
@@ -100,6 +100,8 @@ module RuboCop
100
100
  when :def, :defs, :class, :module,
101
101
  :lvasgn, :ivasgn, :cvasgn, :gvasgn, :casgn
102
102
  node.loc.name
103
+ when :sclass
104
+ node.identifier.source_range
103
105
  when :masgn
104
106
  node.lhs.source_range
105
107
  else
@@ -109,7 +111,7 @@ module RuboCop
109
111
 
110
112
  range_between(starting_loc.begin_pos, ending_loc.end_pos).source
111
113
  end
112
- # rubocop:enable Metrics/AbcSize
114
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
113
115
 
114
116
  # We will use ancestor or wrapper with access modifier.
115
117
 
@@ -167,7 +167,7 @@ module RuboCop
167
167
 
168
168
  def check(node, locations, begin_keyword = DO)
169
169
  locations.each do |loc|
170
- next unless node.loc.respond_to?(loc)
170
+ next unless node.loc?(loc)
171
171
 
172
172
  range = node.loc.public_send(loc)
173
173
  next unless range
@@ -35,6 +35,26 @@ module RuboCop
35
35
  # def cook(dry_ingredients = self.dry_ingredients)
36
36
  # dry_ingredients.combine
37
37
  # end
38
+ #
39
+ # # bad
40
+ # def foo(pie = pie = pie)
41
+ # pie.heat_up
42
+ # end
43
+ #
44
+ # # good
45
+ # def foo(pie)
46
+ # pie.heat_up
47
+ # end
48
+ #
49
+ # # bad
50
+ # def foo(pie = cake = pie)
51
+ # [pie, cake].each(&:heat_up)
52
+ # end
53
+ #
54
+ # # good
55
+ # def foo(cake = pie)
56
+ # [pie, cake].each(&:heat_up)
57
+ # end
38
58
  class CircularArgumentReference < Base
39
59
  extend TargetRubyVersion
40
60
 
@@ -51,11 +71,35 @@ module RuboCop
51
71
  private
52
72
 
53
73
  def check_for_circular_argument_references(arg_name, arg_value)
54
- return unless arg_value.lvar_type?
55
- return unless arg_value.to_a == [arg_name]
74
+ if arg_value.lvar_type? && arg_value.to_a == [arg_name]
75
+ add_offense(arg_value, message: format(MSG, arg_name: arg_name))
76
+
77
+ return
78
+ end
79
+
80
+ check_assignment_chain(arg_name, arg_value)
81
+ end
82
+
83
+ # rubocop:disable Metrics/AbcSize
84
+ def check_assignment_chain(arg_name, node)
85
+ return unless node.lvasgn_type?
86
+
87
+ seen_variables = Set[]
88
+ current_node = node
89
+
90
+ while current_node.lvasgn_type?
91
+ seen_variables << current_node.children.first if current_node.lvasgn_type?
92
+ current_node = current_node.children.last
93
+ end
94
+
95
+ return unless current_node.lvar_type?
96
+
97
+ variable_node = current_node.children.first
98
+ return unless seen_variables.include?(variable_node) || variable_node == arg_name
56
99
 
57
- add_offense(arg_value, message: format(MSG, arg_name: arg_name))
100
+ add_offense(current_node, message: format(MSG, arg_name: arg_name))
58
101
  end
102
+ # rubocop:enable Metrics/AbcSize
59
103
  end
60
104
  end
61
105
  end
@@ -46,7 +46,7 @@ module RuboCop
46
46
  COMMON_MSG = 'Malformed directive comment detected.'
47
47
 
48
48
  MISSING_MODE_NAME_MSG = 'The mode name is missing.'
49
- INVALID_MODE_NAME_MSG = 'The mode name must be one of `enable`, `disable`, or `todo`.'
49
+ INVALID_MODE_NAME_MSG = 'The mode name must be one of `enable`, `disable`, `todo`, `push`, or `pop`.' # rubocop:disable Layout/LineLength
50
50
  MISSING_COP_NAME_MSG = 'The cop name is missing.'
51
51
  MALFORMED_COP_NAMES_MSG = 'Cop names must be separated by commas. ' \
52
52
  'Comment in the directive must start with `--`.'