rubocop 1.33.0 → 1.34.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +7 -1
  4. data/lib/rubocop/cli/command/suggest_extensions.rb +53 -15
  5. data/lib/rubocop/config_loader.rb +12 -0
  6. data/lib/rubocop/config_loader_resolver.rb +1 -5
  7. data/lib/rubocop/cop/cop.rb +1 -1
  8. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +28 -0
  9. data/lib/rubocop/cop/internal_affairs/single_line_comparison.rb +61 -0
  10. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  11. data/lib/rubocop/cop/layout/block_end_newline.rb +2 -1
  12. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -1
  13. data/lib/rubocop/cop/legacy/corrections_proxy.rb +1 -1
  14. data/lib/rubocop/cop/legacy/corrector.rb +1 -1
  15. data/lib/rubocop/cop/lint/debugger.rb +15 -15
  16. data/lib/rubocop/cop/lint/empty_conditional_body.rb +5 -0
  17. data/lib/rubocop/cop/lint/shadowed_exception.rb +15 -0
  18. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +9 -1
  19. data/lib/rubocop/cop/mixin/enforce_superclass.rb +2 -1
  20. data/lib/rubocop/cop/mixin/range_help.rb +2 -2
  21. data/lib/rubocop/cop/style/double_negation.rb +2 -0
  22. data/lib/rubocop/cop/style/if_unless_modifier.rb +1 -1
  23. data/lib/rubocop/cop/style/numeric_literals.rb +16 -1
  24. data/lib/rubocop/cop/style/redundant_parentheses.rb +15 -22
  25. data/lib/rubocop/cop/style/sole_nested_conditional.rb +14 -3
  26. data/lib/rubocop/cop/style/symbol_proc.rb +5 -0
  27. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -13
  28. data/lib/rubocop/ext/range.rb +15 -0
  29. data/lib/rubocop/feature_loader.rb +88 -0
  30. data/lib/rubocop/formatter/clang_style_formatter.rb +1 -1
  31. data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -1
  32. data/lib/rubocop/formatter/html_formatter.rb +1 -1
  33. data/lib/rubocop/formatter/markdown_formatter.rb +1 -1
  34. data/lib/rubocop/formatter/tap_formatter.rb +1 -1
  35. data/lib/rubocop/version.rb +1 -1
  36. data/lib/rubocop.rb +2 -0
  37. metadata +11 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 141b5fc0ee48c7fdf1fc347bb2b59b59933f6d46f575cfb4f380bfc101090345
4
- data.tar.gz: 862c5d1952e777ec19bd1b8782c9e98e065c3d80db7a7d2c91da4406afeb886c
3
+ metadata.gz: d1c7a1f010764e941e11b523804a06d0fa77002769d763a4b1de81b459808c98
4
+ data.tar.gz: 28014c651859698a9a8fbdc122ba93159095f8a3182ac8bed497fbd9c2362c8e
5
5
  SHA512:
6
- metadata.gz: 2be012822408e3aa884dfe85e8a2153144f53d47550a0f7233a60448a8ab18c5c5411959742a4cb9652e972451f05cf0d447451eba53e46260baefa8f2c234e6
7
- data.tar.gz: 063e8ef0056793c86aee9f952db454eac5422c369bdf1ce4a719fcc21279214b86a800f58229b0ade17b963fb2c8e49608bd1883c840d8370b207b3aa4ee916c
6
+ metadata.gz: 9cf155f34f4e1a5520a03668455234518417a154029bafa1f8ee43713fd83f3d1745267abff56fcb487ad8254653cf55bb3d902f93eef8cac5f31fc01430d2f9
7
+ data.tar.gz: 0f5349bb191f7a321136cfe349497b2e3edb847849039cc4d6a9b520332388cf2538e62ec65ab70fc8572dac8d354d276b8a77674562e7ddd54cefaa44a317d4
data/README.md CHANGED
@@ -53,7 +53,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi
53
53
  in your `Gemfile`:
54
54
 
55
55
  ```rb
56
- gem 'rubocop', '~> 1.33', require: false
56
+ gem 'rubocop', '~> 1.34', require: false
57
57
  ```
58
58
 
59
59
  See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
data/config/default.yml CHANGED
@@ -1604,6 +1604,7 @@ Lint/Debugger:
1604
1604
  # a user's configuration, but are otherwise not significant.
1605
1605
  Kernel:
1606
1606
  - binding.irb
1607
+ - Kernel.binding.irb
1607
1608
  Byebug:
1608
1609
  - byebug
1609
1610
  - remote_byebug
@@ -1621,6 +1622,9 @@ Lint/Debugger:
1621
1622
  - binding.pry
1622
1623
  - binding.remote_pry
1623
1624
  - binding.pry_remote
1625
+ - Kernel.binding.pry
1626
+ - Kernel.binding.remote_pry
1627
+ - Kernel.binding.pry_remote
1624
1628
  - Pry.rescue
1625
1629
  Rails:
1626
1630
  - debugger
@@ -1753,9 +1757,10 @@ Lint/EmptyClass:
1753
1757
  Lint/EmptyConditionalBody:
1754
1758
  Description: 'Checks for the presence of `if`, `elsif` and `unless` branches without a body.'
1755
1759
  Enabled: true
1760
+ SafeAutoCorrect: false
1756
1761
  AllowComments: true
1757
1762
  VersionAdded: '0.89'
1758
- VersionChanged: '1.33'
1763
+ VersionChanged: '1.34'
1759
1764
 
1760
1765
  Lint/EmptyEnsure:
1761
1766
  Description: 'Checks for empty ensure block.'
@@ -4400,6 +4405,7 @@ Style/NumericLiterals:
4400
4405
  Strict: false
4401
4406
  # You can specify allowed numbers. (e.g. port number 3000, 8080, and etc)
4402
4407
  AllowedNumbers: []
4408
+ AllowedPatterns: []
4403
4409
 
4404
4410
  Style/NumericPredicate:
4405
4411
  Description: >-
@@ -17,20 +17,10 @@ module RuboCop
17
17
  def run
18
18
  return if skip? || extensions.none?
19
19
 
20
- puts
21
- puts 'Tip: Based on detected gems, the following ' \
22
- 'RuboCop extension libraries might be helpful:'
23
-
24
- extensions.sort.each do |extension|
25
- puts " * #{extension} (https://rubygems.org/gems/#{extension})"
26
- end
20
+ print_install_suggestions if not_installed_extensions.any?
21
+ print_load_suggestions if installed_and_not_loaded_extensions.any?
27
22
 
28
- puts
29
- puts 'You can opt out of this message by adding the following to your config ' \
30
- '(see https://docs.rubocop.org/rubocop/extensions.html#extension-suggestions ' \
31
- 'for more options):'
32
- puts ' AllCops:'
33
- puts ' SuggestExtensions: false'
23
+ print_opt_out_instruction
34
24
 
35
25
  puts if @options[:display_time]
36
26
  end
@@ -48,15 +38,63 @@ module RuboCop
48
38
  !INCLUDED_FORMATTERS.include?(current_formatter)
49
39
  end
50
40
 
41
+ def print_install_suggestions
42
+ puts
43
+ puts 'Tip: Based on detected gems, the following ' \
44
+ 'RuboCop extension libraries might be helpful:'
45
+
46
+ not_installed_extensions.sort.each do |extension|
47
+ puts " * #{extension} (https://rubygems.org/gems/#{extension})"
48
+ end
49
+ end
50
+
51
+ def print_load_suggestions
52
+ puts
53
+ puts 'The following RuboCop extension libraries are installed but not loaded in config:'
54
+
55
+ installed_and_not_loaded_extensions.sort.each do |extension|
56
+ puts " * #{extension}"
57
+ end
58
+ end
59
+
60
+ def print_opt_out_instruction
61
+ puts
62
+ puts 'You can opt out of this message by adding the following to your config ' \
63
+ '(see https://docs.rubocop.org/rubocop/extensions.html#extension-suggestions ' \
64
+ 'for more options):'
65
+ puts ' AllCops:'
66
+ puts ' SuggestExtensions: false'
67
+ end
68
+
51
69
  def current_formatter
52
70
  @options[:format] || @config_store.for_pwd.for_all_cops['DefaultFormatter'] || 'p'
53
71
  end
54
72
 
55
- def extensions
73
+ def all_extensions
56
74
  return [] unless lockfile.dependencies.any?
57
75
 
58
76
  extensions = @config_store.for_pwd.for_all_cops['SuggestExtensions'] || {}
59
- extensions.select { |_, v| (Array(v) & dependent_gems).any? }.keys - installed_gems
77
+ extensions.select { |_, v| (Array(v) & dependent_gems).any? }.keys
78
+ end
79
+
80
+ def extensions
81
+ not_installed_extensions + installed_and_not_loaded_extensions
82
+ end
83
+
84
+ def installed_extensions
85
+ all_extensions & installed_gems
86
+ end
87
+
88
+ def not_installed_extensions
89
+ all_extensions - installed_gems
90
+ end
91
+
92
+ def loaded_extensions
93
+ @config_store.for_pwd.loaded_features.to_a
94
+ end
95
+
96
+ def installed_and_not_loaded_extensions
97
+ installed_extensions - loaded_extensions
60
98
  end
61
99
 
62
100
  def lockfile
@@ -137,6 +137,18 @@ module RuboCop
137
137
  end
138
138
  end
139
139
 
140
+ # Returns the path RuboCop inferred as the root of the project. No file
141
+ # searches will go past this directory.
142
+ # @deprecated Use `RuboCop::ConfigFinder.project_root` instead.
143
+ def project_root
144
+ warn Rainbow(<<~WARNING).yellow
145
+ `RuboCop::ConfigLoader.project_root` is deprecated and will be removed in RuboCop 2.0. \
146
+ Use `RuboCop::ConfigFinder.project_root` instead.
147
+ WARNING
148
+
149
+ ConfigFinder.project_root
150
+ end
151
+
140
152
  PENDING_BANNER = <<~BANNER
141
153
  The following cops were added to RuboCop, but are not configured. Please set Enabled to either `true` or `false` in your `.rubocop.yml` file.
142
154
 
@@ -11,11 +11,7 @@ module RuboCop
11
11
  config_dir = File.dirname(path)
12
12
  hash.delete('require').tap do |loaded_features|
13
13
  Array(loaded_features).each do |feature|
14
- if feature.start_with?('.')
15
- require(File.join(config_dir, feature))
16
- else
17
- require(feature)
18
- end
14
+ FeatureLoader.load(config_directory_path: config_dir, feature: feature)
19
15
  end
20
16
  end
21
17
  end
@@ -7,7 +7,7 @@ module RuboCop
7
7
  module Cop
8
8
  # @deprecated Use Cop::Base instead
9
9
  # Legacy scaffold for Cops.
10
- # See https://docs.rubocop.org/rubocop/cop_api_v1_changelog.html
10
+ # See https://docs.rubocop.org/rubocop/v1_upgrade_notes.html
11
11
  class Cop < Base
12
12
  attr_reader :offenses
13
13
 
@@ -5,9 +5,13 @@ module RuboCop
5
5
  # This autocorrects parentheses
6
6
  class ParenthesesCorrector
7
7
  class << self
8
+ include RangeHelp
9
+
8
10
  def correct(corrector, node)
9
11
  corrector.remove(node.loc.begin)
10
12
  corrector.remove(node.loc.end)
13
+ handle_orphaned_comma(corrector, node)
14
+
11
15
  return unless ternary_condition?(node) && next_char_is_question_mark?(node)
12
16
 
13
17
  corrector.insert_after(node.loc.end, ' ')
@@ -22,6 +26,30 @@ module RuboCop
22
26
  def next_char_is_question_mark?(node)
23
27
  node.loc.last_column == node.parent.loc.question.column
24
28
  end
29
+
30
+ def only_closing_paren_before_comma?(node)
31
+ source_buffer = node.source_range.source_buffer
32
+ line_range = source_buffer.line_range(node.loc.end.line)
33
+
34
+ line_range.source.start_with?(/\s*\)\s*,/)
35
+ end
36
+
37
+ # If removing parentheses leaves a comma on its own line, remove all the whitespace
38
+ # preceding it to prevent a syntax error.
39
+ def handle_orphaned_comma(corrector, node)
40
+ return unless only_closing_paren_before_comma?(node)
41
+
42
+ range = range_with_surrounding_space(
43
+ range: node.loc.end,
44
+ buffer: node.source_range.source_buffer,
45
+ side: :left,
46
+ newlines: true,
47
+ whitespace: true,
48
+ continuations: true
49
+ )
50
+
51
+ corrector.remove(range)
52
+ end
25
53
  end
26
54
  end
27
55
  end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # Enforces the use of `node.single_line?` instead of
7
+ # comparing `first_line` and `last_line` for equality.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # node.loc.first_line == node.loc.last_line
12
+ #
13
+ # # bad
14
+ # node.loc.last_line == node.loc.first_line
15
+ #
16
+ # # bad
17
+ # node.loc.line == node.loc.last_line
18
+ #
19
+ # # bad
20
+ # node.loc.last_line == node.loc.line
21
+ #
22
+ # # bad
23
+ # node.first_line == node.last_line
24
+ #
25
+ # # good
26
+ # node.single_line?
27
+ #
28
+ class SingleLineComparison < Base
29
+ extend AutoCorrector
30
+
31
+ MSG = 'Use `%<preferred>s`.'
32
+ RESTRICT_ON_SEND = %i[==].freeze
33
+
34
+ # @!method single_line_comparison(node)
35
+ def_node_matcher :single_line_comparison, <<~PATTERN
36
+ {
37
+ (send (send $_receiver {:line :first_line}) :== (send _receiver :last_line))
38
+ (send (send $_receiver :last_line) :== (send _receiver {:line :first_line}))
39
+ }
40
+ PATTERN
41
+
42
+ def on_send(node)
43
+ return unless (receiver = single_line_comparison(node))
44
+
45
+ preferred = "#{extract_receiver(receiver)}.single_line?"
46
+
47
+ add_offense(node, message: format(MSG, preferred: preferred)) do |corrector|
48
+ corrector.replace(node, preferred)
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def extract_receiver(node)
55
+ node = node.receiver if node.send_type? && %i[loc source_range].include?(node.method_name)
56
+ node.source
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -17,6 +17,7 @@ require_relative 'internal_affairs/redundant_let_rubocop_config_new'
17
17
  require_relative 'internal_affairs/redundant_location_argument'
18
18
  require_relative 'internal_affairs/redundant_message_argument'
19
19
  require_relative 'internal_affairs/redundant_method_dispatch_node'
20
+ require_relative 'internal_affairs/single_line_comparison'
20
21
  require_relative 'internal_affairs/style_detected_api_use'
21
22
  require_relative 'internal_affairs/undefined_config'
22
23
  require_relative 'internal_affairs/useless_message_assertion'
@@ -60,9 +60,10 @@ module RuboCop
60
60
  end
61
61
 
62
62
  def last_heredoc_argument(node)
63
+ return unless node&.call_type?
63
64
  return unless (arguments = node&.arguments)
64
65
 
65
- heredoc = arguments.reverse.detect(&:heredoc?)
66
+ heredoc = arguments.reverse.detect { |arg| arg.str_type? && arg.heredoc? }
66
67
  return heredoc if heredoc
67
68
 
68
69
  last_heredoc_argument(node.children.first)
@@ -73,7 +73,7 @@ module RuboCop
73
73
  return if node.send_type? && node.loc.operator&.source != '='
74
74
  return unless rhs
75
75
  return unless supported_types.include?(rhs.type)
76
- return if rhs.first_line == rhs.last_line
76
+ return if rhs.single_line?
77
77
 
78
78
  check_by_enforced_style(node, rhs)
79
79
  end
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  module Legacy
6
6
  # Legacy support for Corrector#corrections
7
- # See https://docs.rubocop.org/rubocop/cop_api_v1_changelog.html
7
+ # See https://docs.rubocop.org/rubocop/v1_upgrade_notes.html
8
8
  class CorrectionsProxy
9
9
  def initialize(corrector)
10
10
  @corrector = corrector
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  module Legacy
6
6
  # Legacy Corrector for v0 API support.
7
- # See https://docs.rubocop.org/rubocop/cop_api_v1_changelog.html
7
+ # See https://docs.rubocop.org/rubocop/v1_upgrade_notes.html
8
8
  class Corrector < RuboCop::Cop::Corrector
9
9
  # Support legacy second argument
10
10
  def initialize(source, corr = [])
@@ -67,19 +67,6 @@ module RuboCop
67
67
  class Debugger < Base
68
68
  MSG = 'Remove debugger entry point `%<source>s`.'
69
69
 
70
- # @!method kernel?(node)
71
- def_node_matcher :kernel?, <<~PATTERN
72
- (const {nil? cbase} :Kernel)
73
- PATTERN
74
-
75
- # @!method valid_receiver?(node, arg1)
76
- def_node_matcher :valid_receiver?, <<~PATTERN
77
- {
78
- (const {nil? cbase} %1)
79
- (send {nil? #kernel?} %1)
80
- }
81
- PATTERN
82
-
83
70
  def on_send(node)
84
71
  return unless debugger_method?(node)
85
72
 
@@ -101,7 +88,7 @@ module RuboCop
101
88
 
102
89
  *receiver, method_name = v.split('.')
103
90
  {
104
- receiver: receiver.empty? ? nil : receiver.join.to_sym,
91
+ receiver: receiver.empty? ? nil : receiver.map(&:to_sym),
105
92
  method_name: method_name.to_sym
106
93
  }
107
94
  end.compact
@@ -115,10 +102,23 @@ module RuboCop
115
102
  if method[:receiver].nil?
116
103
  send_node.receiver.nil?
117
104
  else
118
- valid_receiver?(send_node.receiver, method[:receiver])
105
+ method[:receiver] == receiver_chain(send_node)
119
106
  end
120
107
  end
121
108
  end
109
+
110
+ def receiver_chain(send_node)
111
+ receivers = []
112
+ receiver = send_node.receiver
113
+
114
+ while receiver
115
+ name = receiver.send_type? ? receiver.method_name : receiver.const_name&.to_sym
116
+ receivers.unshift(name)
117
+ receiver = receiver.receiver
118
+ end
119
+
120
+ receivers
121
+ end
122
122
  end
123
123
  end
124
124
  end
@@ -7,6 +7,11 @@ module RuboCop
7
7
  #
8
8
  # NOTE: empty `else` branches are handled by `Style/EmptyElse`.
9
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
+ #
10
15
  # @example
11
16
  # # bad
12
17
  # if condition
@@ -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
@@ -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
  #
@@ -54,11 +54,11 @@ module RuboCop
54
54
  NOT_GIVEN = Module.new
55
55
  def range_with_surrounding_space(range_positional = NOT_GIVEN, # rubocop:disable Metrics/ParameterLists
56
56
  range: NOT_GIVEN, side: :both, newlines: true,
57
- whitespace: false, continuations: false)
57
+ whitespace: false, continuations: false,
58
+ buffer: @processed_source.buffer)
58
59
 
59
60
  range = range_positional unless range_positional == NOT_GIVEN
60
61
 
61
- buffer = @processed_source.buffer
62
62
  src = buffer.source
63
63
 
64
64
  go_left, go_right = directions(side)
@@ -93,6 +93,8 @@ module RuboCop
93
93
 
94
94
  if conditional_node
95
95
  double_negative_condition_return_value?(node, last_child, conditional_node)
96
+ elsif last_child.pair_type? || last_child.hash_type? || last_child.parent.array_type?
97
+ false
96
98
  else
97
99
  last_child.last_line <= node.last_line
98
100
  end
@@ -96,7 +96,7 @@ module RuboCop
96
96
  return false unless max_line_length
97
97
 
98
98
  range = node.source_range
99
- return false unless range.first_line == range.last_line
99
+ return false unless range.single_line?
100
100
  return false unless line_length_enabled_at_line?(range.first_line)
101
101
 
102
102
  line = range.source_line
@@ -3,9 +3,17 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for big numeric literals without _ between groups
6
+ # Checks for big numeric literals without `_` between groups
7
7
  # of digits in them.
8
8
  #
9
+ # Additional allowed patterns can be added by adding regexps to
10
+ # the `AllowedPatterns` configuration. All regexps are treated
11
+ # as anchored even if the patterns do not contain anchors (so
12
+ # `\d{4}_\d{4}` will allow `1234_5678` but not `1234_5678_9012`).
13
+ #
14
+ # NOTE: Even if `AllowedPatterns` are given, autocorrection will
15
+ # only correct to the standard pattern of an `_` every 3 digits.
16
+ #
9
17
  # @example
10
18
  #
11
19
  # # bad
@@ -34,6 +42,7 @@ module RuboCop
34
42
  #
35
43
  class NumericLiterals < Base
36
44
  include IntegerNode
45
+ include AllowedPattern
37
46
  extend AutoCorrector
38
47
 
39
48
  MSG = 'Use underscores(_) as thousands separator and separate every 3 digits with them.'
@@ -59,6 +68,7 @@ module RuboCop
59
68
  # TODO: handle non-decimal literals as well
60
69
  return if int.start_with?('0')
61
70
  return if allowed_numbers.include?(int)
71
+ return if matches_allowed_pattern?(int)
62
72
  return unless int.size >= min_digits
63
73
 
64
74
  case int
@@ -108,6 +118,11 @@ module RuboCop
108
118
  def allowed_numbers
109
119
  cop_config.fetch('AllowedNumbers', []).map(&:to_s)
110
120
  end
121
+
122
+ def allowed_patterns
123
+ # Convert the patterns to be anchored
124
+ super.map { |regexp| Regexp.new(/\A#{regexp}\z/) }
125
+ end
111
126
  end
112
127
  end
113
128
  end
@@ -57,8 +57,8 @@ module RuboCop
57
57
  def allowed_expression?(node)
58
58
  allowed_ancestor?(node) ||
59
59
  allowed_method_call?(node) ||
60
- allowed_array_or_hash_element?(node) ||
61
- allowed_multiple_expression?(node)
60
+ allowed_multiple_expression?(node) ||
61
+ allowed_ternary?(node)
62
62
  end
63
63
 
64
64
  def allowed_ancestor?(node)
@@ -80,6 +80,19 @@ module RuboCop
80
80
  !ancestor.begin_type? && !ancestor.def_type? && !ancestor.block_type?
81
81
  end
82
82
 
83
+ def allowed_ternary?(node)
84
+ return unless node&.parent&.if_type?
85
+
86
+ node.parent.ternary? && ternary_parentheses_required?
87
+ end
88
+
89
+ def ternary_parentheses_required?
90
+ config = @config.for_cop('Style/TernaryParentheses')
91
+ allowed_styles = %w[require_parentheses require_parentheses_when_complex]
92
+
93
+ config.fetch('Enabled') && allowed_styles.include?(config['EnforcedStyle'])
94
+ end
95
+
83
96
  def like_method_argument_parentheses?(node)
84
97
  node.send_type? && node.arguments.one? &&
85
98
  !node.arithmetic_operation? && node.first_argument.begin_type?
@@ -153,26 +166,6 @@ module RuboCop
153
166
  node.parent&.keyword?
154
167
  end
155
168
 
156
- def allowed_array_or_hash_element?(node)
157
- # Don't flag
158
- # ```
159
- # { a: (1
160
- # ), }
161
- # ```
162
- hash_or_array_element?(node) && only_closing_paren_before_comma?(node)
163
- end
164
-
165
- def hash_or_array_element?(node)
166
- node.each_ancestor(:array, :hash).any?
167
- end
168
-
169
- def only_closing_paren_before_comma?(node)
170
- source_buffer = node.source_range.source_buffer
171
- line_range = source_buffer.line_range(node.loc.end.line)
172
-
173
- /^\s*\)\s*,/.match?(line_range.source)
174
- end
175
-
176
169
  def disallowed_literal?(begin_node, node)
177
170
  node.literal? && !node.range_type? && !raised_to_power_negative_numeric?(begin_node, node)
178
171
  end
@@ -148,7 +148,18 @@ module RuboCop
148
148
  )
149
149
  corrector.replace(range, and_operator)
150
150
  corrector.remove(range_by_whole_lines(node.loc.end, include_final_newline: true))
151
- corrector.wrap(if_branch.condition, '(', ')') if wrap_condition?(if_branch.condition)
151
+
152
+ wrap_condition(corrector, if_branch.condition)
153
+ end
154
+
155
+ def wrap_condition(corrector, condition)
156
+ # Handle `send` and `block` nodes that need to be wrapped in parens
157
+ # FIXME: autocorrection prevents syntax errors by wrapping the entire node in parens,
158
+ # but wrapping the argument list would be a more ergonomic correction.
159
+ node_to_check = condition&.block_type? ? condition.send_node : condition
160
+ return unless wrap_condition?(node_to_check)
161
+
162
+ corrector.wrap(condition, '(', ')')
152
163
  end
153
164
 
154
165
  def correct_for_outer_condition_modify_form_style(corrector, node, if_branch)
@@ -207,7 +218,7 @@ module RuboCop
207
218
  end
208
219
 
209
220
  def require_parentheses?(condition)
210
- condition.send_type? && !condition.arguments.empty? && !condition.parenthesized? &&
221
+ condition.call_type? && !condition.arguments.empty? && !condition.parenthesized? &&
211
222
  !condition.comparison_method?
212
223
  end
213
224
 
@@ -219,7 +230,7 @@ module RuboCop
219
230
 
220
231
  def wrap_condition?(node)
221
232
  node.and_type? || node.or_type? ||
222
- (node.send_type? && node.arguments.any? && !node.parenthesized?)
233
+ (node.call_type? && node.arguments.any? && !node.parenthesized?)
223
234
  end
224
235
 
225
236
  def replace_condition(condition)
@@ -121,6 +121,7 @@ module RuboCop
121
121
  # we should allow lambdas & procs
122
122
  return if proc_node?(dispatch_node)
123
123
  return if unsafe_hash_usage?(dispatch_node)
124
+ return if unsafe_array_usage?(dispatch_node)
124
125
  return if %i[lambda proc].include?(dispatch_node.method_name)
125
126
  return if allowed_method_name?(dispatch_node.method_name)
126
127
  return if allow_if_method_has_argument?(node.send_node)
@@ -144,6 +145,10 @@ module RuboCop
144
145
  node.receiver&.hash_type? && %i[reject select].include?(node.method_name)
145
146
  end
146
147
 
148
+ def unsafe_array_usage?(node)
149
+ node.receiver&.array_type? && %i[min max].include?(node.method_name)
150
+ end
151
+
147
152
  def allowed_method_name?(name)
148
153
  allowed_method?(name) || matches_allowed_pattern?(name)
149
154
  end
@@ -71,7 +71,7 @@ module RuboCop
71
71
 
72
72
  return if only_closing_parenthesis_is_last_line?(condition)
73
73
  return if condition_as_parenthesized_one_line_pattern_matching?(condition)
74
- return unless node.ternary? && !infinite_loop? && offense?(node)
74
+ return unless node.ternary? && offense?(node)
75
75
 
76
76
  message = message(node)
77
77
 
@@ -166,22 +166,10 @@ module RuboCop
166
166
  style == :require_parentheses_when_complex
167
167
  end
168
168
 
169
- def redundant_parentheses_enabled?
170
- @config.for_cop('Style/RedundantParentheses').fetch('Enabled')
171
- end
172
-
173
169
  def parenthesized?(node)
174
170
  node.begin_type?
175
171
  end
176
172
 
177
- # When this cop is configured to enforce parentheses and the
178
- # `RedundantParentheses` cop is enabled, it will cause an infinite loop
179
- # as they compete to add and remove the parentheses respectively.
180
- def infinite_loop?
181
- (require_parentheses? || require_parentheses_when_complex?) &&
182
- redundant_parentheses_enabled?
183
- end
184
-
185
173
  def unsafe_autocorrect?(condition)
186
174
  condition.children.any? do |child|
187
175
  unparenthesized_method_call?(child) || below_ternary_precedence?(child)
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Ext
5
+ # Extensions to Parser::Source::Range
6
+ module Range
7
+ # Adds `Range#single_line?` to parallel `Node#single_line?`
8
+ def single_line?
9
+ first_line == last_line
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ Parser::Source::Range.include RuboCop::Ext::Range
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # This class handles loading files (a.k.a. features in Ruby) specified
5
+ # by `--require` command line option and `require` directive in the config.
6
+ #
7
+ # Normally, the given string is directly passed to `require`. If a string
8
+ # beginning with `.` is given, it is assumed to be relative to the given
9
+ # directory.
10
+ #
11
+ # If a string containing `-` is given, it will be used as is, but if we
12
+ # cannot find the file to load, we will replace `-` with `/` and try it
13
+ # again as when Bundler loads gems.
14
+ #
15
+ # @api private
16
+ class FeatureLoader
17
+ class << self
18
+ # @param [String] config_directory_path
19
+ # @param [String] feature
20
+ def load(config_directory_path:, feature:)
21
+ new(config_directory_path: config_directory_path, feature: feature).load
22
+ end
23
+ end
24
+
25
+ # @param [String] config_directory_path
26
+ # @param [String] feature
27
+ def initialize(config_directory_path:, feature:)
28
+ @config_directory_path = config_directory_path
29
+ @feature = feature
30
+ end
31
+
32
+ def load
33
+ ::Kernel.require(target)
34
+ rescue ::LoadError => e
35
+ raise if e.path != target
36
+
37
+ begin
38
+ ::Kernel.require(namespaced_target)
39
+ rescue ::LoadError => error_for_namespaced_target
40
+ raise e if error_for_namespaced_target.path == namespaced_target
41
+
42
+ raise error_for_namespaced_target
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ # @return [String]
49
+ def namespaced_feature
50
+ @feature.tr('-', '/')
51
+ end
52
+
53
+ # @return [String]
54
+ def namespaced_target
55
+ if relative?
56
+ relative(namespaced_feature)
57
+ else
58
+ namespaced_feature
59
+ end
60
+ end
61
+
62
+ # @param [String]
63
+ # @return [String]
64
+ def relative(feature)
65
+ ::File.join(@config_directory_path, feature)
66
+ end
67
+
68
+ # @return [Boolean]
69
+ def relative?
70
+ @feature.start_with?('.')
71
+ end
72
+
73
+ # @param [LoadError] error
74
+ # @return [Boolean]
75
+ def seems_cannot_load_such_file_error?(error)
76
+ error.path == target
77
+ end
78
+
79
+ # @return [String]
80
+ def target
81
+ if relative?
82
+ relative(@feature)
83
+ else
84
+ @feature
85
+ end
86
+ end
87
+ end
88
+ end
@@ -41,7 +41,7 @@ module RuboCop
41
41
  def report_line(location)
42
42
  source_line = location.source_line
43
43
 
44
- if location.first_line == location.last_line
44
+ if location.single_line?
45
45
  output.puts(source_line)
46
46
  else
47
47
  output.puts("#{source_line} #{yellow(ELLIPSES)}")
@@ -161,7 +161,7 @@ module RuboCop
161
161
  next unless value.is_a?(Array)
162
162
  next if value.empty?
163
163
 
164
- output_buffer.puts "# #{param}: #{value.join(', ')}"
164
+ output_buffer.puts "# #{param}: #{value.uniq.join(', ')}"
165
165
  end
166
166
  end
167
167
 
@@ -115,7 +115,7 @@ module RuboCop
115
115
  end
116
116
 
117
117
  def possible_ellipses(location)
118
- location.first_line == location.last_line ? '' : " #{ELLIPSES}"
118
+ location.single_line? ? '' : " #{ELLIPSES}"
119
119
  end
120
120
 
121
121
  def escape(string)
@@ -71,7 +71,7 @@ module RuboCop
71
71
  end
72
72
 
73
73
  def possible_ellipses(location)
74
- location.first_line == location.last_line ? '' : ' ...'
74
+ location.single_line? ? '' : ' ...'
75
75
  end
76
76
  end
77
77
  end
@@ -29,7 +29,7 @@ module RuboCop
29
29
  def report_line(location)
30
30
  source_line = location.source_line
31
31
 
32
- if location.first_line == location.last_line
32
+ if location.single_line?
33
33
  output.puts("# #{source_line}")
34
34
  else
35
35
  output.puts("# #{source_line} #{yellow(ELLIPSES)}")
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.33.0'
6
+ STRING = '1.34.0'
7
7
 
8
8
  MSG = '%<version>s (using Parser %<parser_version>s, ' \
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \
data/lib/rubocop.rb CHANGED
@@ -16,6 +16,7 @@ require 'rubocop-ast'
16
16
  require_relative 'rubocop/ast_aliases'
17
17
  require_relative 'rubocop/ext/regexp_node'
18
18
  require_relative 'rubocop/ext/regexp_parser'
19
+ require_relative 'rubocop/ext/range'
19
20
 
20
21
  require_relative 'rubocop/core_ext/string'
21
22
  require_relative 'rubocop/ext/processed_source'
@@ -689,6 +690,7 @@ require_relative 'rubocop/config_obsoletion/split_cop'
689
690
  require_relative 'rubocop/config_obsoletion'
690
691
  require_relative 'rubocop/config_store'
691
692
  require_relative 'rubocop/config_validator'
693
+ require_relative 'rubocop/feature_loader'
692
694
  require_relative 'rubocop/lockfile'
693
695
  require_relative 'rubocop/target_finder'
694
696
  require_relative 'rubocop/directive_comment'
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.33.0
4
+ version: 1.34.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: 2022-08-04 00:00:00.000000000 Z
13
+ date: 2022-08-09 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: json
@@ -46,14 +46,14 @@ dependencies:
46
46
  requirements:
47
47
  - - ">="
48
48
  - !ruby/object:Gem::Version
49
- version: 3.1.0.0
49
+ version: 3.1.2.1
50
50
  type: :runtime
51
51
  prerelease: false
52
52
  version_requirements: !ruby/object:Gem::Requirement
53
53
  requirements:
54
54
  - - ">="
55
55
  - !ruby/object:Gem::Version
56
- version: 3.1.0.0
56
+ version: 3.1.2.1
57
57
  - !ruby/object:Gem::Dependency
58
58
  name: rainbow
59
59
  requirement: !ruby/object:Gem::Requirement
@@ -120,7 +120,7 @@ dependencies:
120
120
  requirements:
121
121
  - - ">="
122
122
  - !ruby/object:Gem::Version
123
- version: 1.19.1
123
+ version: 1.20.0
124
124
  - - "<"
125
125
  - !ruby/object:Gem::Version
126
126
  version: '2.0'
@@ -130,7 +130,7 @@ dependencies:
130
130
  requirements:
131
131
  - - ">="
132
132
  - !ruby/object:Gem::Version
133
- version: 1.19.1
133
+ version: 1.20.0
134
134
  - - "<"
135
135
  - !ruby/object:Gem::Version
136
136
  version: '2.0'
@@ -300,6 +300,7 @@ files:
300
300
  - lib/rubocop/cop/internal_affairs/redundant_location_argument.rb
301
301
  - lib/rubocop/cop/internal_affairs/redundant_message_argument.rb
302
302
  - lib/rubocop/cop/internal_affairs/redundant_method_dispatch_node.rb
303
+ - lib/rubocop/cop/internal_affairs/single_line_comparison.rb
303
304
  - lib/rubocop/cop/internal_affairs/style_detected_api_use.rb
304
305
  - lib/rubocop/cop/internal_affairs/undefined_config.rb
305
306
  - lib/rubocop/cop/internal_affairs/useless_message_assertion.rb
@@ -898,8 +899,10 @@ files:
898
899
  - lib/rubocop/directive_comment.rb
899
900
  - lib/rubocop/error.rb
900
901
  - lib/rubocop/ext/processed_source.rb
902
+ - lib/rubocop/ext/range.rb
901
903
  - lib/rubocop/ext/regexp_node.rb
902
904
  - lib/rubocop/ext/regexp_parser.rb
905
+ - lib/rubocop/feature_loader.rb
903
906
  - lib/rubocop/file_finder.rb
904
907
  - lib/rubocop/formatter.rb
905
908
  - lib/rubocop/formatter/auto_gen_config_formatter.rb
@@ -972,7 +975,7 @@ metadata:
972
975
  homepage_uri: https://rubocop.org/
973
976
  changelog_uri: https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md
974
977
  source_code_uri: https://github.com/rubocop/rubocop/
975
- documentation_uri: https://docs.rubocop.org/rubocop/1.33/
978
+ documentation_uri: https://docs.rubocop.org/rubocop/1.34/
976
979
  bug_tracker_uri: https://github.com/rubocop/rubocop/issues
977
980
  rubygems_mfa_required: 'true'
978
981
  post_install_message:
@@ -990,7 +993,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
990
993
  - !ruby/object:Gem::Version
991
994
  version: '0'
992
995
  requirements: []
993
- rubygems_version: 3.1.2
996
+ rubygems_version: 3.2.22
994
997
  signing_key:
995
998
  specification_version: 4
996
999
  summary: Automatic Ruby code style checking tool.