rubocop 1.33.0 → 1.34.0

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