rubocop 1.36.0 → 1.38.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 (96) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +27 -1
  4. data/lib/rubocop/arguments_env.rb +17 -0
  5. data/lib/rubocop/arguments_file.rb +17 -0
  6. data/lib/rubocop/cli/command/execute_runner.rb +7 -7
  7. data/lib/rubocop/cli/command/suggest_extensions.rb +8 -1
  8. data/lib/rubocop/comment_config.rb +36 -1
  9. data/lib/rubocop/cop/commissioner.rb +3 -1
  10. data/lib/rubocop/cop/generator.rb +1 -2
  11. data/lib/rubocop/cop/internal_affairs/create_empty_file.rb +37 -0
  12. data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +111 -0
  13. data/lib/rubocop/cop/internal_affairs.rb +2 -0
  14. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -0
  15. data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
  16. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  17. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  18. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +13 -9
  19. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +30 -3
  20. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +6 -2
  21. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  22. data/lib/rubocop/cop/lint/duplicate_magic_comment.rb +73 -0
  23. data/lib/rubocop/cop/lint/duplicate_methods.rb +11 -1
  24. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +25 -6
  25. data/lib/rubocop/cop/lint/empty_class.rb +3 -1
  26. data/lib/rubocop/cop/lint/empty_conditional_body.rb +20 -8
  27. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +18 -3
  28. data/lib/rubocop/cop/lint/nested_method_definition.rb +50 -1
  29. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  30. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +4 -5
  31. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +1 -1
  32. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +23 -1
  33. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +7 -0
  34. data/lib/rubocop/cop/lint/redundant_require_statement.rb +38 -10
  35. data/lib/rubocop/cop/lint/require_parentheses.rb +1 -1
  36. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -2
  37. data/lib/rubocop/cop/lint/shadowed_exception.rb +0 -10
  38. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +3 -0
  39. data/lib/rubocop/cop/lint/unreachable_loop.rb +1 -1
  40. data/lib/rubocop/cop/lint/unused_method_argument.rb +4 -0
  41. data/lib/rubocop/cop/mixin/comments_help.rb +12 -0
  42. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +4 -0
  43. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +6 -3
  44. data/lib/rubocop/cop/mixin/rescue_node.rb +3 -1
  45. data/lib/rubocop/cop/mixin/surrounding_space.rb +10 -8
  46. data/lib/rubocop/cop/naming/inclusive_language.rb +1 -1
  47. data/lib/rubocop/cop/registry.rb +10 -4
  48. data/lib/rubocop/cop/style/access_modifier_declarations.rb +24 -2
  49. data/lib/rubocop/cop/style/accessor_grouping.rb +7 -3
  50. data/lib/rubocop/cop/style/block_delimiters.rb +2 -2
  51. data/lib/rubocop/cop/style/character_literal.rb +1 -1
  52. data/lib/rubocop/cop/style/class_equality_comparison.rb +1 -1
  53. data/lib/rubocop/cop/style/collection_compact.rb +11 -2
  54. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  55. data/lib/rubocop/cop/style/endless_method.rb +1 -1
  56. data/lib/rubocop/cop/style/explicit_block_argument.rb +4 -0
  57. data/lib/rubocop/cop/style/format_string_token.rb +1 -1
  58. data/lib/rubocop/cop/style/guard_clause.rb +62 -21
  59. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  60. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +25 -2
  61. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +13 -2
  62. data/lib/rubocop/cop/style/module_function.rb +28 -6
  63. data/lib/rubocop/cop/style/negated_if_else_condition.rb +7 -1
  64. data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
  65. data/lib/rubocop/cop/style/operator_method_call.rb +40 -0
  66. data/lib/rubocop/cop/style/redundant_begin.rb +1 -0
  67. data/lib/rubocop/cop/style/redundant_condition.rb +5 -2
  68. data/lib/rubocop/cop/style/redundant_each.rb +111 -0
  69. data/lib/rubocop/cop/style/redundant_initialize.rb +3 -1
  70. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +8 -1
  71. data/lib/rubocop/cop/style/redundant_string_escape.rb +181 -0
  72. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -1
  73. data/lib/rubocop/cop/style/static_class.rb +32 -1
  74. data/lib/rubocop/cop/style/symbol_array.rb +2 -0
  75. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  76. data/lib/rubocop/cop/style/word_array.rb +2 -0
  77. data/lib/rubocop/cop/team.rb +3 -4
  78. data/lib/rubocop/cop/variable_force/variable_table.rb +1 -1
  79. data/lib/rubocop/cops_documentation_generator.rb +4 -2
  80. data/lib/rubocop/ext/processed_source.rb +2 -0
  81. data/lib/rubocop/formatter/disabled_config_formatter.rb +8 -2
  82. data/lib/rubocop/formatter/offense_count_formatter.rb +8 -5
  83. data/lib/rubocop/formatter/worst_offenders_formatter.rb +6 -3
  84. data/lib/rubocop/options.rb +19 -15
  85. data/lib/rubocop/rspec/cop_helper.rb +21 -1
  86. data/lib/rubocop/rspec/shared_contexts.rb +14 -1
  87. data/lib/rubocop/runner.rb +15 -11
  88. data/lib/rubocop/server/cache.rb +5 -1
  89. data/lib/rubocop/server/cli.rb +9 -2
  90. data/lib/rubocop/server/client_command/exec.rb +5 -0
  91. data/lib/rubocop/server/core.rb +3 -1
  92. data/lib/rubocop/server/socket_reader.rb +5 -1
  93. data/lib/rubocop/server.rb +1 -1
  94. data/lib/rubocop/version.rb +8 -3
  95. data/lib/rubocop.rb +4 -0
  96. metadata +13 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4bc2d4bd29fd7e81f4d7118fef9f55bcd2f086fd20ce383d85c2a1b7b26ea4e9
4
- data.tar.gz: 9cb3cef8736b03dd0834b94fefe4415a301d78d842df184a1a1d290973376070
3
+ metadata.gz: 1ed47ef39bb205db15249a399d0502f730c2656f712293357651cc0ddd328715
4
+ data.tar.gz: 5e6411a2b459e0ac426e0ef0d8b8508b9b1693fea0c5b8cb5d82ed23f71d39b9
5
5
  SHA512:
6
- metadata.gz: abeba580bae46458f6427c3d21c6532e95ff88e7d981d196f5013fee29c8f911146f229daa8e6f92d2f28bb14790edc46373a4366f9881700df18a7f5a1352fa
7
- data.tar.gz: 4bee5fc42db940e95cd6e3fba83680e6d7fd573a8ed3b7c65136a48d5f3b67ba8d299ecdd30a422afb95961d84c9b0452e3d1b6c1952e0edee1cea97ca704952
6
+ metadata.gz: 5cfd13ac3c283e77e300f87ee923be282740f2763e26c73d21d27b5ae176629d15d8d580d9d326349acf5b71c74e6335689c2192050a818577b9f2e0a6a4484f
7
+ data.tar.gz: 27f36547ccdf6cc1336e1c44c5e164410f65344cba77fda76ad2db463054adaa93c96a406939b3826412280463a1d6a2940708f4080bc3170999bcc81594ba9d
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.36', require: false
56
+ gem 'rubocop', '~> 1.38', 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
@@ -1707,6 +1707,11 @@ Lint/DuplicateHashKey:
1707
1707
  VersionAdded: '0.34'
1708
1708
  VersionChanged: '0.77'
1709
1709
 
1710
+ Lint/DuplicateMagicComment:
1711
+ Description: 'Check for duplicated magic comments.'
1712
+ Enabled: pending
1713
+ VersionAdded: '1.37'
1714
+
1710
1715
  Lint/DuplicateMethods:
1711
1716
  Description: 'Check for duplicate method definitions.'
1712
1717
  Enabled: true
@@ -1957,6 +1962,8 @@ Lint/NestedMethodDefinition:
1957
1962
  Description: 'Do not use nested method definitions.'
1958
1963
  StyleGuide: '#no-nested-methods'
1959
1964
  Enabled: true
1965
+ AllowedMethods: []
1966
+ AllowedPatterns: []
1960
1967
  VersionAdded: '0.32'
1961
1968
 
1962
1969
  Lint/NestedPercentLiteral:
@@ -2021,7 +2028,9 @@ Lint/OrAssignmentToConstant:
2021
2028
  Lint/OrderedMagicComments:
2022
2029
  Description: 'Checks the proper ordering of magic comments and whether a magic comment is not placed before a shebang.'
2023
2030
  Enabled: true
2031
+ SafeAutoCorrect: false
2024
2032
  VersionAdded: '0.53'
2033
+ VersionChanged: '1.37'
2025
2034
 
2026
2035
  Lint/OutOfRangeRegexpRef:
2027
2036
  Description: 'Checks for out of range reference for Regexp because it always returns nil.'
@@ -3850,7 +3859,7 @@ Style/HashSyntax:
3850
3859
  - never
3851
3860
  # accepts both shorthand and explicit use of hash literal value.
3852
3861
  - either
3853
- # like "always", but will avoid mixing styles in a single hash
3862
+ # like "either", but will avoid mixing styles in a single hash
3854
3863
  - consistent
3855
3864
  # Force hashes that have a symbol value to use hash rockets
3856
3865
  UseHashRocketsWithSymbolValues: false
@@ -4498,6 +4507,12 @@ Style/OpenStructUse:
4498
4507
  Enabled: pending
4499
4508
  VersionAdded: '1.23'
4500
4509
 
4510
+ Style/OperatorMethodCall:
4511
+ Description: 'Checks for redundant dot before operator method call.'
4512
+ StyleGuide: '#operator-method-call'
4513
+ Enabled: pending
4514
+ VersionAdded: '1.37'
4515
+
4501
4516
  Style/OptionHash:
4502
4517
  Description: "Don't use option hashes when you can use keyword arguments."
4503
4518
  Enabled: false
@@ -4681,6 +4696,12 @@ Style/RedundantConditional:
4681
4696
  Enabled: true
4682
4697
  VersionAdded: '0.50'
4683
4698
 
4699
+ Style/RedundantEach:
4700
+ Description: 'Checks for redundant `each`.'
4701
+ Enabled: pending
4702
+ Safe: false
4703
+ VersionAdded: '1.38'
4704
+
4684
4705
  Style/RedundantException:
4685
4706
  Description: "Checks for an obsolete RuntimeException argument in raise/fail."
4686
4707
  StyleGuide: '#no-explicit-runtimeerror'
@@ -4792,6 +4813,11 @@ Style/RedundantSortBy:
4792
4813
  Enabled: true
4793
4814
  VersionAdded: '0.36'
4794
4815
 
4816
+ Style/RedundantStringEscape:
4817
+ Description: 'Checks for redundant escapes in string literals.'
4818
+ Enabled: pending
4819
+ VersionAdded: '1.37'
4820
+
4795
4821
  Style/RegexpLiteral:
4796
4822
  Description: 'Use / or %r around regular expressions.'
4797
4823
  StyleGuide: '#percent-r'
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # This is a class that reads optional command line arguments to rubocop from environment variable.
5
+ # @api private
6
+ class ArgumentsEnv
7
+ def self.read_as_arguments
8
+ if (arguments = ENV.fetch('RUBOCOP_OPTS', '')).empty?
9
+ []
10
+ else
11
+ require 'shellwords'
12
+
13
+ Shellwords.split(arguments)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # This is a class that reads optional command line arguments to rubocop from .rubocop file.
5
+ # @api private
6
+ class ArgumentsFile
7
+ def self.read_as_arguments
8
+ if File.exist?('.rubocop') && !File.directory?('.rubocop')
9
+ require 'shellwords'
10
+
11
+ File.read('.rubocop').shellsplit
12
+ else
13
+ []
14
+ end
15
+ end
16
+ end
17
+ end
@@ -41,13 +41,13 @@ module RuboCop
41
41
 
42
42
  def with_redirect
43
43
  if @options[:stderr]
44
- orig_stdout = $stdout.dup
45
- $stdout.reopen($stderr)
46
-
47
- result = yield
48
-
49
- $stdout.reopen(orig_stdout)
50
- result
44
+ orig_stdout = $stdout
45
+ begin
46
+ $stdout = $stderr
47
+ yield
48
+ ensure
49
+ $stdout = orig_stdout
50
+ end
51
51
  else
52
52
  yield
53
53
  end
@@ -73,7 +73,14 @@ module RuboCop
73
73
  def all_extensions
74
74
  return [] unless lockfile.dependencies.any?
75
75
 
76
- extensions = @config_store.for_pwd.for_all_cops['SuggestExtensions'] || {}
76
+ extensions = @config_store.for_pwd.for_all_cops['SuggestExtensions']
77
+ case extensions
78
+ when true
79
+ extensions = ConfigLoader.default_configuration.for_all_cops['SuggestExtensions']
80
+ when false, nil
81
+ extensions = {}
82
+ end
83
+
77
84
  extensions.select { |_, v| (Array(v) & dependent_gems).any? }.keys
78
85
  end
79
86
 
@@ -4,10 +4,31 @@ module RuboCop
4
4
  # This class parses the special `rubocop:disable` comments in a source
5
5
  # and provides a way to check if each cop is enabled at arbitrary line.
6
6
  class CommentConfig
7
+ extend Forwardable
8
+
9
+ CONFIG_DISABLED_LINE_RANGE_MIN = -Float::INFINITY
10
+
11
+ # This class provides an API compatible with RuboCop::DirectiveComment
12
+ # to be used for cops that are disabled in the config file
13
+ class ConfigDisabledCopDirectiveComment
14
+ attr_reader :text, :loc, :line_number
15
+
16
+ Loc = Struct.new(:expression)
17
+ Expression = Struct.new(:line)
18
+
19
+ def initialize(cop_name)
20
+ @text = "# rubocop:disable #{cop_name}"
21
+ @line_number = CONFIG_DISABLED_LINE_RANGE_MIN
22
+ @loc = Loc.new(Expression.new(CONFIG_DISABLED_LINE_RANGE_MIN))
23
+ end
24
+ end
25
+
7
26
  CopAnalysis = Struct.new(:line_ranges, :start_line_number)
8
27
 
9
28
  attr_reader :processed_source
10
29
 
30
+ def_delegators :@processed_source, :config, :registry
31
+
11
32
  def initialize(processed_source)
12
33
  @processed_source = processed_source
13
34
  end
@@ -25,7 +46,11 @@ module RuboCop
25
46
  end
26
47
 
27
48
  def extra_enabled_comments
28
- extra_enabled_comments_with_names(extras: Hash.new { |h, k| h[k] = [] }, names: Hash.new(0))
49
+ disable_count = Hash.new(0)
50
+ registry.disabled(config).each do |cop|
51
+ disable_count[cop.cop_name] += 1
52
+ end
53
+ extra_enabled_comments_with_names(extras: Hash.new { |h, k| h[k] = [] }, names: disable_count)
29
54
  end
30
55
 
31
56
  def comment_only_line?(line_number)
@@ -50,6 +75,7 @@ module RuboCop
50
75
 
51
76
  def analyze # rubocop:todo Metrics/AbcSize
52
77
  analyses = Hash.new { |hash, key| hash[key] = CopAnalysis.new([], nil) }
78
+ inject_disabled_cops_directives(analyses)
53
79
 
54
80
  each_directive do |directive|
55
81
  directive.cop_names.each do |cop_name|
@@ -64,6 +90,15 @@ module RuboCop
64
90
  end
65
91
  end
66
92
 
93
+ def inject_disabled_cops_directives(analyses)
94
+ registry.disabled(config).each do |cop|
95
+ analyses[cop.cop_name] = analyze_cop(
96
+ analyses[cop.cop_name],
97
+ DirectiveComment.new(ConfigDisabledCopDirectiveComment.new(cop.cop_name))
98
+ )
99
+ end
100
+ end
101
+
67
102
  def analyze_cop(analysis, directive)
68
103
  # Disabling cops after comments like `#=SomeDslDirective` does not related to single line
69
104
  if !comment_only_line?(directive.line_number) || directive.single_line?
@@ -159,9 +159,11 @@ module RuboCop
159
159
  def with_cop_error_handling(cop, node = nil)
160
160
  yield
161
161
  rescue StandardError => e
162
- raise e if @options[:raise_error]
162
+ raise e if @options[:raise_error] # For internal testing
163
163
 
164
164
  err = ErrorWithAnalyzedFileLocation.new(cause: e, node: node, cop: cop)
165
+ raise err if @options[:raise_cop_error] # From user-input option
166
+
165
167
  @errors << err
166
168
  end
167
169
  end
@@ -206,9 +206,8 @@ module RuboCop
206
206
  end
207
207
 
208
208
  def snake_case(camel_case_string)
209
- return 'rspec' if camel_case_string == 'RSpec'
210
-
211
209
  camel_case_string
210
+ .gsub('RSpec', 'Rspec')
212
211
  .gsub(%r{([^A-Z/])([A-Z]+)}, '\1_\2')
213
212
  .gsub(%r{([A-Z])([A-Z][^A-Z\d/]+)}, '\1_\2')
214
213
  .downcase
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # Checks for uses of `create_file` with empty string second argument.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # create_file(path, '')
12
+ #
13
+ # # good
14
+ # create_empty_file(path)
15
+ #
16
+ class CreateEmptyFile < Base
17
+ extend AutoCorrector
18
+
19
+ MSG = 'Use `%<replacement>s`.'
20
+ RESTRICT_ON_SEND = %i[create_file].freeze
21
+
22
+ def on_send(node)
23
+ return if node.receiver
24
+ return unless (argument = node.arguments[1])
25
+ return unless argument.str_type? && argument.value.empty?
26
+
27
+ replacement = "create_empty_file(#{node.first_argument.source})"
28
+ message = format(MSG, replacement: replacement)
29
+
30
+ add_offense(node, message: message) do |corrector|
31
+ corrector.replace(node, replacement)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # Use `RUBY` for heredoc delimiter of example Ruby code.
7
+ #
8
+ # Some editors may apply better syntax highlighting by using appropriate language names for
9
+ # the delimiter.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # expect_offense(<<~CODE)
14
+ # example_ruby_code
15
+ # CODE
16
+ #
17
+ # # good
18
+ # expect_offense(<<~RUBY)
19
+ # example_ruby_code
20
+ # RUBY
21
+ class ExampleHeredocDelimiter < Base
22
+ extend AutoCorrector
23
+
24
+ EXPECTED_HEREDOC_DELIMITER = 'RUBY'
25
+
26
+ MSG = 'Use `RUBY` for heredoc delimiter of example Ruby code.'
27
+
28
+ RESTRICT_ON_SEND = %i[
29
+ expect_correction
30
+ expect_no_corrections
31
+ expect_no_offenses
32
+ expect_offense
33
+ ].freeze
34
+
35
+ # @param node [RuboCop::AST::SendNode]
36
+ # @return [void]
37
+ def on_send(node)
38
+ heredoc_node = heredoc_node_from(node)
39
+ return unless heredoc_node
40
+ return if expected_heredoc_delimiter?(heredoc_node)
41
+ return if expected_heredoc_delimiter_in_body?(heredoc_node)
42
+
43
+ add_offense(heredoc_node) do |corrector|
44
+ autocorrect(corrector, heredoc_node)
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ # @param corrector [RuboCop::Cop::Corrector]
51
+ # @param node [RuboCop::AST::StrNode]
52
+ # @return [void]
53
+ def autocorrect(corrector, node)
54
+ [
55
+ heredoc_openning_delimiter_range_from(node),
56
+ heredoc_closing_delimiter_range_from(node)
57
+ ].each do |range|
58
+ corrector.replace(range, EXPECTED_HEREDOC_DELIMITER)
59
+ end
60
+ end
61
+
62
+ # @param node [RuboCop::AST::StrNode]
63
+ # @return [Boolean]
64
+ def expected_heredoc_delimiter_in_body?(node)
65
+ node.location.heredoc_body.source.lines.any? do |line|
66
+ line.strip == EXPECTED_HEREDOC_DELIMITER
67
+ end
68
+ end
69
+
70
+ # @param node [RuboCop::AST::StrNode]
71
+ # @return [Boolean]
72
+ def expected_heredoc_delimiter?(node)
73
+ heredoc_delimiter_string_from(node) == EXPECTED_HEREDOC_DELIMITER
74
+ end
75
+
76
+ # @param node [RuboCop::AST::SendNode]
77
+ # @return [RuboCop::AST::StrNode, nil]
78
+ def heredoc_node_from(node)
79
+ return unless node.first_argument.respond_to?(:heredoc?)
80
+ return unless node.first_argument.heredoc?
81
+
82
+ node.first_argument
83
+ end
84
+
85
+ # @param node [RuboCop::AST::StrNode]
86
+ # @return [String]
87
+ def heredoc_delimiter_string_from(node)
88
+ node.source[Heredoc::OPENING_DELIMITER, 2]
89
+ end
90
+
91
+ # @param node [RuboCop::AST::StrNode]
92
+ # @return [Parser::Source::Range]
93
+ def heredoc_openning_delimiter_range_from(node)
94
+ match_data = node.source.match(Heredoc::OPENING_DELIMITER)
95
+ node.location.expression.begin.adjust(
96
+ begin_pos: match_data.begin(2),
97
+ end_pos: match_data.end(2)
98
+ )
99
+ end
100
+
101
+ # @param node [RuboCop::AST::StrNode]
102
+ # @return [Parser::Source::Range]
103
+ def heredoc_closing_delimiter_range_from(node)
104
+ node.location.heredoc_end.end.adjust(
105
+ begin_pos: -heredoc_delimiter_string_from(node).length
106
+ )
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'internal_affairs/cop_description'
4
+ require_relative 'internal_affairs/create_empty_file'
4
5
  require_relative 'internal_affairs/empty_line_between_expect_offense_and_correction'
5
6
  require_relative 'internal_affairs/example_description'
7
+ require_relative 'internal_affairs/example_heredoc_delimiter'
6
8
  require_relative 'internal_affairs/inherit_deprecated_cop_class'
7
9
  require_relative 'internal_affairs/location_line_equality_comparison'
8
10
  require_relative 'internal_affairs/method_name_end_with'
@@ -162,6 +162,7 @@ module RuboCop
162
162
  check_alignment([node.first_argument], indent)
163
163
  end
164
164
  alias on_csend on_send
165
+ alias on_super on_send
165
166
 
166
167
  private
167
168
 
@@ -90,7 +90,7 @@ module RuboCop
90
90
  # which lines start inside a string literal?
91
91
  return [] if ast.nil?
92
92
 
93
- ast.each_node(:str, :dstr).each_with_object(Set.new) do |str, ranges|
93
+ ast.each_node(:str, :dstr).with_object(Set.new) do |str, ranges|
94
94
  loc = str.location
95
95
 
96
96
  if str.heredoc?
@@ -343,7 +343,7 @@ module RuboCop
343
343
  end
344
344
 
345
345
  def skip_check?(base_loc, body_node)
346
- return true if ignored_line?(base_loc)
346
+ return true if allowed_line?(base_loc)
347
347
  return true unless body_node
348
348
 
349
349
  # Don't check if expression is on same line as "then" keyword, etc.
@@ -92,7 +92,7 @@ module RuboCop
92
92
  # which lines start inside a string literal?
93
93
  return [] if ast.nil?
94
94
 
95
- ast.each_node(:str, :dstr).each_with_object(Set.new) do |str, ranges|
95
+ ast.each_node(:str, :dstr).with_object(Set.new) do |str, ranges|
96
96
  loc = str.location
97
97
 
98
98
  if str.heredoc?
@@ -169,19 +169,22 @@ module RuboCop
169
169
 
170
170
  def qualifies_for_compact?(node, token, side: :right)
171
171
  if side == :right
172
- multi_dimensional_array?(node, token) && !next_to_bracket?(token)
172
+ multi_dimensional_array?(node, token) && token.space_before?
173
173
  else
174
- multi_dimensional_array?(node, token, side: :left) &&
175
- !next_to_bracket?(token, side: :left)
174
+ multi_dimensional_array?(node, token, side: :left) && token.space_after?
176
175
  end
177
176
  end
178
177
 
179
178
  def multi_dimensional_array?(node, token, side: :right)
180
- i = index_for(node, token)
179
+ offset = side == :right ? -1 : +1
180
+ i = index_for(node, token) + offset
181
+ # TODO: change this type check once
182
+ # https://github.com/rubocop/rubocop-ast/pull/240 is merged
183
+ i += offset while processed_source.tokens_within(node)[i].new_line?
181
184
  if side == :right
182
- processed_source.tokens_within(node)[i - 1].right_bracket?
185
+ processed_source.tokens_within(node)[i].right_bracket?
183
186
  else
184
- processed_source.tokens_within(node)[i + 1].left_array_bracket?
187
+ processed_source.tokens_within(node)[i].left_array_bracket?
185
188
  end
186
189
  end
187
190
 
@@ -200,12 +203,13 @@ module RuboCop
200
203
  end
201
204
 
202
205
  def compact_corrections(corrector, node, left, right)
203
- if qualifies_for_compact?(node, left, side: :left)
206
+ if multi_dimensional_array?(node, left, side: :left)
204
207
  compact(corrector, left, :right)
205
208
  elsif !left.space_after?
206
209
  corrector.insert_after(left.pos, ' ')
207
210
  end
208
- if qualifies_for_compact?(node, right)
211
+
212
+ if multi_dimensional_array?(node, right)
209
213
  compact(corrector, right, :left)
210
214
  elsif !right.space_before?
211
215
  corrector.insert_before(right.pos, ' ')
@@ -213,7 +217,7 @@ module RuboCop
213
217
  end
214
218
 
215
219
  def compact(corrector, bracket, side)
216
- range = side_space_range(range: bracket.pos, side: side)
220
+ range = side_space_range(range: bracket.pos, side: side, include_newlines: true)
217
221
  corrector.remove(range)
218
222
  end
219
223
  end
@@ -46,10 +46,13 @@ module RuboCop
46
46
  # # bad
47
47
  # foo = { }
48
48
  # bar = { }
49
+ # baz = {
50
+ # }
49
51
  #
50
52
  # # good
51
53
  # foo = {}
52
54
  # bar = {}
55
+ # baz = {}
53
56
  #
54
57
  # @example EnforcedStyleForEmptyBraces: space
55
58
  # # The `space` EnforcedStyleForEmptyBraces style enforces that
@@ -60,8 +63,9 @@ module RuboCop
60
63
  #
61
64
  # # good
62
65
  # foo = { }
63
- # foo = { }
64
- # foo = { }
66
+ # foo = { }
67
+ # foo = {
68
+ # }
65
69
  #
66
70
  class SpaceInsideHashLiteralBraces < Base
67
71
  include SurroundingSpace
@@ -77,6 +81,7 @@ module RuboCop
77
81
 
78
82
  check(tokens[0], tokens[1])
79
83
  check(tokens[-2], tokens[-1]) if tokens.size > 2
84
+ check_whitespace_only_hash(node) if enforce_no_space_style_for_empty_braces?
80
85
  end
81
86
 
82
87
  private
@@ -103,7 +108,7 @@ module RuboCop
103
108
  if is_same_braces && style == :compact
104
109
  false
105
110
  elsif is_empty_braces
106
- cop_config['EnforcedStyleForEmptyBraces'] != 'no_space'
111
+ !enforce_no_space_style_for_empty_braces?
107
112
  else
108
113
  style != :no_space
109
114
  end
@@ -175,6 +180,28 @@ module RuboCop
175
180
 
176
181
  range_between(begin_pos, range.end_pos - 1)
177
182
  end
183
+
184
+ def check_whitespace_only_hash(node)
185
+ range = range_inside_hash(node)
186
+ return unless range.source.match?(/\A\s+\z/m)
187
+
188
+ add_offense(
189
+ range,
190
+ message: format(MSG, problem: 'empty hash literal braces detected')
191
+ ) do |corrector|
192
+ corrector.remove(range)
193
+ end
194
+ end
195
+
196
+ def range_inside_hash(node)
197
+ return node.source_range if node.location.begin.nil?
198
+
199
+ range_between(node.location.begin.end_pos, node.location.end.begin_pos)
200
+ end
201
+
202
+ def enforce_no_space_style_for_empty_braces?
203
+ cop_config['EnforcedStyleForEmptyBraces'] == 'no_space'
204
+ end
178
205
  end
179
206
  end
180
207
  end
@@ -38,6 +38,8 @@ module RuboCop
38
38
  # # bad
39
39
  # foo[ ]
40
40
  # foo[ ]
41
+ # foo[
42
+ # ]
41
43
  #
42
44
  # # good
43
45
  # foo[]
@@ -49,6 +51,8 @@ module RuboCop
49
51
  # # bad
50
52
  # foo[]
51
53
  # foo[ ]
54
+ # foo[
55
+ # ]
52
56
  #
53
57
  # # good
54
58
  # foo[ ]
@@ -64,8 +68,6 @@ module RuboCop
64
68
  RESTRICT_ON_SEND = %i[[] []=].freeze
65
69
 
66
70
  def on_send(node)
67
- return if node.multiline?
68
-
69
71
  tokens = processed_source.tokens_within(node)
70
72
  left_token = left_ref_bracket(node, tokens)
71
73
  return unless left_token
@@ -76,6 +78,8 @@ module RuboCop
76
78
  return empty_offenses(node, left_token, right_token, EMPTY_MSG)
77
79
  end
78
80
 
81
+ return if node.multiline?
82
+
79
83
  if style == :no_space
80
84
  no_space_offenses(node, left_token, right_token, MSG)
81
85
  else
@@ -45,7 +45,7 @@ module RuboCop
45
45
  # # bad
46
46
  # expect { do_something }.to change { object.attribute }
47
47
  #
48
- # @example AllowedPatterns: [/change/]
48
+ # @example AllowedPatterns: ['change']
49
49
  #
50
50
  # # good
51
51
  # expect { do_something }.to change { object.attribute }