rubocop 1.75.3 → 1.76.2

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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -14
  3. data/config/default.yml +55 -7
  4. data/config/obsoletion.yml +6 -3
  5. data/lib/rubocop/cop/autocorrect_logic.rb +18 -10
  6. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
  7. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +50 -6
  8. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  9. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +1 -0
  10. data/lib/rubocop/cop/layout/class_structure.rb +35 -0
  11. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +7 -3
  12. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
  13. data/lib/rubocop/cop/layout/hash_alignment.rb +1 -1
  14. data/lib/rubocop/cop/layout/leading_comment_space.rb +13 -1
  15. data/lib/rubocop/cop/layout/space_after_semicolon.rb +10 -0
  16. data/lib/rubocop/cop/layout/space_before_brackets.rb +6 -32
  17. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +5 -1
  18. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +3 -0
  19. data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
  20. data/lib/rubocop/cop/lint/array_literal_in_regexp.rb +2 -3
  21. data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
  22. data/lib/rubocop/cop/lint/circular_argument_reference.rb +2 -5
  23. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +1 -1
  24. data/lib/rubocop/cop/lint/duplicate_methods.rb +84 -2
  25. data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
  26. data/lib/rubocop/cop/lint/float_comparison.rb +27 -0
  27. data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
  28. data/lib/rubocop/cop/lint/literal_as_condition.rb +31 -25
  29. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +4 -4
  30. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
  31. data/lib/rubocop/cop/lint/useless_access_modifier.rb +21 -4
  32. data/lib/rubocop/cop/lint/useless_assignment.rb +2 -0
  33. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +90 -0
  34. data/lib/rubocop/cop/lint/useless_or.rb +98 -0
  35. data/lib/rubocop/cop/lint/void.rb +2 -2
  36. data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
  37. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
  38. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +15 -14
  39. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  40. data/lib/rubocop/cop/mixin/trailing_comma.rb +6 -2
  41. data/lib/rubocop/cop/naming/predicate_method.rb +245 -0
  42. data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +2 -2
  43. data/lib/rubocop/cop/style/access_modifier_declarations.rb +32 -10
  44. data/lib/rubocop/cop/style/arguments_forwarding.rb +4 -1
  45. data/lib/rubocop/cop/style/class_and_module_children.rb +12 -2
  46. data/lib/rubocop/cop/style/command_literal.rb +1 -1
  47. data/lib/rubocop/cop/style/comparable_between.rb +5 -2
  48. data/lib/rubocop/cop/style/conditional_assignment.rb +3 -1
  49. data/lib/rubocop/cop/style/data_inheritance.rb +7 -0
  50. data/lib/rubocop/cop/style/def_with_parentheses.rb +18 -5
  51. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
  52. data/lib/rubocop/cop/style/identical_conditional_branches.rb +3 -3
  53. data/lib/rubocop/cop/style/if_unless_modifier.rb +22 -4
  54. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -7
  55. data/lib/rubocop/cop/style/it_block_parameter.rb +33 -14
  56. data/lib/rubocop/cop/style/map_to_hash.rb +11 -0
  57. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +4 -1
  58. data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
  59. data/lib/rubocop/cop/style/multiline_if_modifier.rb +2 -0
  60. data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
  61. data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
  62. data/lib/rubocop/cop/style/redundant_format.rb +6 -1
  63. data/lib/rubocop/cop/style/redundant_parentheses.rb +31 -5
  64. data/lib/rubocop/cop/style/redundant_self.rb +5 -5
  65. data/lib/rubocop/cop/style/regexp_literal.rb +1 -1
  66. data/lib/rubocop/cop/style/safe_navigation.rb +25 -12
  67. data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -2
  68. data/lib/rubocop/cop/style/string_concatenation.rb +1 -2
  69. data/lib/rubocop/cop/style/struct_inheritance.rb +8 -1
  70. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +7 -1
  71. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  72. data/lib/rubocop/cop/team.rb +1 -1
  73. data/lib/rubocop/cop/variable_force/assignment.rb +7 -3
  74. data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -0
  75. data/lib/rubocop/formatter/html_formatter.rb +1 -1
  76. data/lib/rubocop/rspec/cop_helper.rb +2 -2
  77. data/lib/rubocop/rspec/expect_offense.rb +9 -3
  78. data/lib/rubocop/rspec/shared_contexts.rb +1 -2
  79. data/lib/rubocop/version.rb +1 -1
  80. data/lib/rubocop.rb +6 -1
  81. metadata +12 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a997a626f52ebff384613dfc5885815cf0bcee74a22f53485f77a5ea95205a60
4
- data.tar.gz: b79e9c7f1b9b0716cf38b317174b49075c94dc39edd3d310a1a00d4fa6304a4f
3
+ metadata.gz: 0c98c6b67493a9eeb135c1e86846e068746676cd7caba305df7343f6da5a9307
4
+ data.tar.gz: a5e75c620016505e918bc98e273a729f14388b6c3cf95358c4975d785ca7fa57
5
5
  SHA512:
6
- metadata.gz: 9e2948dd0e2d446e78113d6575bf5fb2f96a2d40239db066b9e6476479efd22996081675bcf47925a8ce0fb6012495e678701ab7383cf1a534255747fc2a6d00
7
- data.tar.gz: 944bf135b7e8855c53172f14e33b040326cde4b0c14a8cad3656434765f1b9f9c2097e455437317aba2026f51c0ff0beb2a119b5e262bb4173664ab408e8aa8a
6
+ metadata.gz: '08701aa9d2fa3f7a1cf2b950b8bdf8640c47965345c3572645dd04f73507142ebf08aa16d5e7ab829a9e74330d45b4280ef700a7974eb02d7cf29e5890f67bb8'
7
+ data.tar.gz: 8d0f787edacfee68d6e6bc13db5c9f2247f137c3074fec0af47dae1b6b0b51403e9d071d1a40a81529271d35d1954a5848e16c489ebb175c9b2a24a4c1192a48
data/README.md CHANGED
@@ -36,10 +36,11 @@ Working on RuboCop is often fun, but it also requires a great deal of time and e
36
36
  **RuboCop**'s installation is pretty standard:
37
37
 
38
38
  ```sh
39
- $ gem install rubocop
39
+ gem install rubocop
40
40
  ```
41
41
 
42
- If you'd rather install RuboCop using `bundler`, add a line for it in your `Gemfile` (but set the `require` option to `false`, as it is a standalone tool):
42
+ If you'd rather install RuboCop using `bundler`, add a line for it in your
43
+ `Gemfile` (but set the `require` option to `false`, as it is a standalone tool):
43
44
 
44
45
  ```rb
45
46
  gem 'rubocop', require: false
@@ -52,7 +53,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi
52
53
  in your `Gemfile`:
53
54
 
54
55
  ```rb
55
- gem 'rubocop', '~> 1.75', require: false
56
+ gem 'rubocop', '~> 1.76', require: false
56
57
  ```
57
58
 
58
59
  See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
@@ -61,12 +62,15 @@ See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) fo
61
62
 
62
63
  Just type `rubocop` in a Ruby project's folder and watch the magic happen.
63
64
 
64
- ```
65
- $ cd my/cool/ruby/project
66
- $ rubocop
65
+ ```sh
66
+ cd my/cool/ruby/project
67
+ rubocop
67
68
  ```
68
69
 
69
- You can also use this magic in your favorite editor with RuboCop's [built-in LSP server](https://docs.rubocop.org/rubocop/usage/lsp.html).
70
+ > [!TIP]
71
+ >
72
+ > You can also use this magic in your favorite editor with RuboCop's
73
+ > [built-in LSP server](https://docs.rubocop.org/rubocop/usage/lsp.html).
70
74
 
71
75
  ## Documentation
72
76
 
@@ -79,7 +83,7 @@ RuboCop officially supports the following runtime Ruby implementations:
79
83
  * MRI 2.7+
80
84
  * JRuby 9.4+
81
85
 
82
- Targets Ruby 2.0+ code analysis.
86
+ It targets Ruby 2.0+ for code analysis.
83
87
 
84
88
  See the [compatibility documentation](https://docs.rubocop.org/rubocop/compatibility.html) for further details.
85
89
 
@@ -91,7 +95,6 @@ If you use RuboCop in your project, you can include one of these badges in your
91
95
 
92
96
  [![Ruby Style Guide](https://img.shields.io/badge/code_style-community-brightgreen.svg)](https://rubystyle.guide)
93
97
 
94
-
95
98
  Here are the Markdown snippets for the two badges:
96
99
 
97
100
  ``` markdown
@@ -109,7 +112,7 @@ Here's a list of RuboCop's core developers:
109
112
  * [Yuji Nakayama](https://github.com/yujinakayama) (retired)
110
113
  * [Evgeni Dzhelyov](https://github.com/edzhelyov) (retired)
111
114
  * [Ted Johansson](https://github.com/drenmi)
112
- * [Masataka Kuwabara](https://github.com/pocke)
115
+ * [Masataka Kuwabara](https://github.com/pocke) (retired)
113
116
  * [Koichi Ito](https://github.com/koic)
114
117
  * [Maxim Krizhanovski](https://github.com/darhazer)
115
118
  * [Benjamin Quorning](https://github.com/bquorning)
@@ -157,8 +160,8 @@ wide array of funding channels to account for your preferences
157
160
  currently [Open Collective](https://opencollective.com/rubocop) is our
158
161
  preferred funding platform).
159
162
 
160
- **If you're working in a company that's making significant use of RuboCop we'd appreciate it if you suggest to your company
161
- to become a RuboCop sponsor.**
163
+ **If you're working in a company that's making significant use of RuboCop we'd
164
+ appreciate it if you suggest to your company to become a RuboCop sponsor.**
162
165
 
163
166
  You can support the development of RuboCop via
164
167
  [GitHub Sponsors](https://github.com/sponsors/bbatsov),
@@ -168,8 +171,11 @@ You can support the development of RuboCop via
168
171
  and [Tidelift](https://tidelift.com/subscription/pkg/rubygems-rubocop?utm_source=rubygems-rubocop&utm_medium=referral&utm_campaign=readme)
169
172
  .
170
173
 
171
- **Note:** If doing a sponsorship in the form of donation is problematic for your company from an accounting standpoint, we'd recommend
172
- the use of Tidelift, where you can get a support-like subscription instead.
174
+ > [!NOTE]
175
+ >
176
+ > If doing a sponsorship in the form of donation is problematic for your company
177
+ > from an accounting standpoint, we'd recommend the use of Tidelift, where you
178
+ > can get a support-like subscription instead.
173
179
 
174
180
  ### Open Collective for Individuals
175
181
 
data/config/default.yml CHANGED
@@ -1923,7 +1923,7 @@ Lint/EmptyInterpolation:
1923
1923
  Enabled: true
1924
1924
  AutoCorrect: contextual
1925
1925
  VersionAdded: '0.20'
1926
- VersionChanged: '1.61'
1926
+ VersionChanged: '1.76'
1927
1927
 
1928
1928
  Lint/EmptyWhen:
1929
1929
  Description: 'Checks for `when` branches with empty bodies.'
@@ -2423,8 +2423,9 @@ Lint/ShadowingOuterLocalVariable:
2423
2423
  Description: >-
2424
2424
  Do not use the same name as outer local variable
2425
2425
  for block arguments or block local variables.
2426
- Enabled: true
2426
+ Enabled: false
2427
2427
  VersionAdded: '0.9'
2428
+ VersionChanged: '1.76'
2428
2429
 
2429
2430
  Lint/SharedMutableDefault:
2430
2431
  Description: 'Checks for mutable literals used as default arguments during Hash initialization.'
@@ -2612,6 +2613,13 @@ Lint/UselessConstantScoping:
2612
2613
  Enabled: pending
2613
2614
  VersionAdded: '1.72'
2614
2615
 
2616
+ Lint/UselessDefaultValueArgument:
2617
+ Description: 'Checks for usage of `fetch` or `Array.new` with default value argument and block.'
2618
+ Enabled: pending
2619
+ VersionAdded: '1.76'
2620
+ Safe: false
2621
+ AllowedReceivers: []
2622
+
2615
2623
  Lint/UselessDefined:
2616
2624
  Description: 'Checks for calls to `defined?` with strings and symbols. The result of such a call will always be truthy.'
2617
2625
  Enabled: pending
@@ -2636,6 +2644,11 @@ Lint/UselessNumericOperation:
2636
2644
  Enabled: pending
2637
2645
  VersionAdded: '1.66'
2638
2646
 
2647
+ Lint/UselessOr:
2648
+ Description: 'Checks for useless OR expressions.'
2649
+ Enabled: pending
2650
+ VersionAdded: '1.76'
2651
+
2639
2652
  Lint/UselessRescue:
2640
2653
  Description: 'Checks for useless `rescue`s.'
2641
2654
  Enabled: pending
@@ -2676,7 +2689,7 @@ Metrics/AbcSize:
2676
2689
  A calculated magnitude based on number of assignments,
2677
2690
  branches, and conditions.
2678
2691
  References:
2679
- - http://c2.com/cgi/wiki?AbcMetric
2692
+ - https://wiki.c2.com/?AbcMetric
2680
2693
  - https://en.wikipedia.org/wiki/ABC_Software_Metric
2681
2694
  Enabled: true
2682
2695
  VersionAdded: '0.27'
@@ -3046,8 +3059,23 @@ Naming/MethodParameterName:
3046
3059
  # Forbidden names that will register an offense
3047
3060
  ForbiddenNames: []
3048
3061
 
3049
- Naming/PredicateName:
3050
- Description: 'Check the names of predicate methods.'
3062
+ Naming/PredicateMethod:
3063
+ Description: 'Checks that predicate methods end with `?` and non-predicate methods do not.'
3064
+ Enabled: pending
3065
+ VersionAdded: '1.76'
3066
+ VersionChanged: '1.76'
3067
+ # In `aggressive` mode, the cop will register an offense for predicate methods that
3068
+ # may return a non-boolean value.
3069
+ # In `conservative` mode, the cop will *not* register an offense for predicate methods
3070
+ # that may return a non-boolean value.
3071
+ Mode: conservative
3072
+ AllowedMethods:
3073
+ - call
3074
+ AllowedPatterns: []
3075
+ AllowBangMethods: false
3076
+
3077
+ Naming/PredicatePrefix:
3078
+ Description: 'Predicate method names should not be prefixed and end with a `?`.'
3051
3079
  StyleGuide: '#bool-methods-qmark'
3052
3080
  Enabled: true
3053
3081
  VersionAdded: '0.50'
@@ -3702,7 +3730,9 @@ Style/CommentedKeyword:
3702
3730
  Style/ComparableBetween:
3703
3731
  Description: 'Enforces the use of `Comparable#between?` instead of logical comparison.'
3704
3732
  Enabled: pending
3733
+ Safe: false
3705
3734
  VersionAdded: '1.74'
3735
+ VersionChanged: '1.75'
3706
3736
  StyleGuide: '#ranges-or-between'
3707
3737
 
3708
3738
  Style/ComparableClamp:
@@ -3937,6 +3967,16 @@ Style/EmptyMethod:
3937
3967
  - compact
3938
3968
  - expanded
3939
3969
 
3970
+ Style/EmptyStringInsideInterpolation:
3971
+ Description: 'Checks for empty strings being assigned inside string interpolation.'
3972
+ StyleGuide: '#empty-strings-in-interpolation'
3973
+ Enabled: pending
3974
+ EnforcedStyle: trailing_conditional
3975
+ SupportedStyles:
3976
+ - trailing_conditional
3977
+ - ternary
3978
+ VersionAdded: '1.76'
3979
+
3940
3980
  Style/Encoding:
3941
3981
  Description: 'Use UTF-8 as the source file encoding.'
3942
3982
  StyleGuide: '#utf-8'
@@ -4431,12 +4471,14 @@ Style/ItAssignment:
4431
4471
  Style/ItBlockParameter:
4432
4472
  Description: 'Checks for blocks with one argument where `it` block parameter can be used.'
4433
4473
  Enabled: pending
4434
- EnforcedStyle: only_numbered_parameters
4474
+ EnforcedStyle: allow_single_line
4435
4475
  SupportedStyles:
4476
+ - allow_single_line
4436
4477
  - only_numbered_parameters
4437
4478
  - always
4438
4479
  - disallow
4439
4480
  VersionAdded: '1.75'
4481
+ VersionChanged: '1.76'
4440
4482
 
4441
4483
  Style/KeywordArgumentsMerging:
4442
4484
  Description: >-
@@ -4967,7 +5009,7 @@ Style/OpenStructUse:
4967
5009
  Avoid using OpenStruct. As of Ruby 3.0, use is officially discouraged due to performance,
4968
5010
  version compatibility, and potential security issues.
4969
5011
  References:
4970
- - https://docs.ruby-lang.org/en/3.0.0/OpenStruct.html#class-OpenStruct-label-Caveats
5012
+ - https://docs.ruby-lang.org/en/3.0/OpenStruct.html#class-OpenStruct-label-Caveats
4971
5013
 
4972
5014
  Enabled: pending
4973
5015
  Safe: false
@@ -5149,6 +5191,12 @@ Style/RedundantArrayConstructor:
5149
5191
  Enabled: pending
5150
5192
  VersionAdded: '1.52'
5151
5193
 
5194
+ Style/RedundantArrayFlatten:
5195
+ Description: 'Checks for redundant calls of `Array#flatten`.'
5196
+ Enabled: pending
5197
+ Safe: false
5198
+ VersionAdded: '1.76'
5199
+
5152
5200
  Style/RedundantAssignment:
5153
5201
  Description: 'Checks for redundant assignment before returning.'
5154
5202
  Enabled: true
@@ -31,6 +31,9 @@ renamed:
31
31
  Lint/UnneededRequireStatement: Lint/RedundantRequireStatement
32
32
  Lint/UnneededSplatExpansion: Lint/RedundantSplatExpansion
33
33
  Metrics/LineLength: Layout/LineLength
34
+ Naming/PredicateName:
35
+ new_name: Naming/PredicatePrefix
36
+ severity: warning
34
37
  Naming/UncommunicativeBlockParamName: Naming/BlockParameterName
35
38
  Naming/UncommunicativeMethodParamName: Naming/MethodParameterName
36
39
  Style/AccessorMethodName: Naming/AccessorMethodName
@@ -44,7 +47,7 @@ renamed:
44
47
  Style/MethodName: Naming/MethodName
45
48
  Style/OpMethod: Naming/BinaryOperatorParameterName
46
49
  Style/PredicateName:
47
- new_name: Naming/PredicateName
50
+ new_name: Naming/PredicatePrefix
48
51
  severity: warning
49
52
  Style/SingleSpaceBeforeFirstArg: Layout/SpaceBeforeFirstArg
50
53
  Style/UnneededCapitalW: Style/RedundantCapitalW
@@ -179,10 +182,10 @@ changed_parameters:
179
182
  - cops: Naming/HeredocDelimiterNaming
180
183
  parameters: Blacklist
181
184
  alternative: ForbiddenDelimiters
182
- - cops: Naming/PredicateName
185
+ - cops: Naming/PredicatePrefix
183
186
  parameters: NamePrefixBlacklist
184
187
  alternative: ForbiddenPrefixes
185
- - cops: Naming/PredicateName
188
+ - cops: Naming/PredicatePrefix
186
189
  parameters: NameWhitelist
187
190
  alternative: AllowedMethods
188
191
  - cops:
@@ -50,7 +50,7 @@ module RuboCop
50
50
 
51
51
  def disable_offense(offense_range)
52
52
  unbreakable_range = multiline_ranges(offense_range)&.find do |range|
53
- range_overlaps_offense?(offense_range, range)
53
+ eol_comment_would_be_inside_literal?(offense_range, range)
54
54
  end
55
55
 
56
56
  if unbreakable_range
@@ -75,18 +75,22 @@ module RuboCop
75
75
  end
76
76
 
77
77
  def disable_offense_with_eol_or_surround_comment(range)
78
- eol_comment = " # rubocop:todo #{cop_name}"
79
- needed_line_length = (range.source_line + eol_comment).length
80
-
81
- if needed_line_length <= max_line_length
82
- disable_offense_at_end_of_line(range_of_first_line(range), eol_comment)
83
- else
78
+ if line_with_eol_comment_too_long?(range)
84
79
  disable_offense_before_and_after(range_by_lines(range))
80
+ else
81
+ disable_offense_at_end_of_line(range_of_first_line(range))
85
82
  end
86
83
  end
87
84
 
88
- def range_overlaps_offense?(offense_range, range)
89
- offense_range.begin_pos > range.begin_pos && range.overlaps?(offense_range)
85
+ def eol_comment_would_be_inside_literal?(offense_range, literal_range)
86
+ return true if line_with_eol_comment_too_long?(offense_range)
87
+
88
+ offense_line = offense_range.line
89
+ offense_line >= literal_range.first_line && offense_line < literal_range.last_line
90
+ end
91
+
92
+ def line_with_eol_comment_too_long?(range)
93
+ (range.source_line + eol_comment).length > max_line_length
90
94
  end
91
95
 
92
96
  def surrounding_heredoc?(node)
@@ -132,10 +136,14 @@ module RuboCop
132
136
  config.for_cop('Layout/LineLength')['Max'] || 120
133
137
  end
134
138
 
135
- def disable_offense_at_end_of_line(range, eol_comment)
139
+ def disable_offense_at_end_of_line(range)
136
140
  Corrector.new(range).insert_after(range, eol_comment)
137
141
  end
138
142
 
143
+ def eol_comment
144
+ " # rubocop:todo #{cop_name}"
145
+ end
146
+
139
147
  def disable_offense_before_and_after(range_by_lines)
140
148
  range_with_newline = range_by_lines.resize(range_by_lines.size + 1)
141
149
  leading_whitespace = range_by_lines.source_line[/^\s*/]
@@ -45,7 +45,7 @@ module RuboCop
45
45
 
46
46
  gem_declarations(processed_source.ast)
47
47
  .each_cons(2) do |previous, current|
48
- next unless consecutive_lines(previous, current)
48
+ next unless consecutive_lines?(previous, current)
49
49
  next unless case_insensitive_out_of_order?(gem_name(current), gem_name(previous))
50
50
 
51
51
  register_offense(previous, current)
@@ -6,10 +6,11 @@ module RuboCop
6
6
  # An attribute assignment method calls should be listed only once
7
7
  # in a gemspec.
8
8
  #
9
- # Assigning to an attribute with the same name using `spec.foo =` will be
10
- # an unintended usage. On the other hand, duplication of methods such
11
- # as `spec.requirements`, `spec.add_runtime_dependency`, and others are
12
- # permitted because it is the intended use of appending values.
9
+ # Assigning to an attribute with the same name using `spec.foo =` or
10
+ # `spec.attribute#[]=` will be an unintended usage. On the other hand,
11
+ # duplication of methods such # as `spec.requirements`,
12
+ # `spec.add_runtime_dependency`, and others are permitted because it is
13
+ # the intended use of appending values.
13
14
  #
14
15
  # @example
15
16
  # # bad
@@ -34,6 +35,18 @@ module RuboCop
34
35
  # spec.add_dependency('parallel', '~> 1.10')
35
36
  # spec.add_dependency('parser', '>= 2.3.3.1', '< 3.0')
36
37
  # end
38
+ #
39
+ # # bad
40
+ # Gem::Specification.new do |spec|
41
+ # spec.metadata["key"] = "value"
42
+ # spec.metadata["key"] = "value"
43
+ # end
44
+ #
45
+ # # good
46
+ # Gem::Specification.new do |spec|
47
+ # spec.metadata["key"] = "value"
48
+ # end
49
+ #
37
50
  class DuplicatedAssignment < Base
38
51
  include RangeHelp
39
52
  include GemspecHelp
@@ -44,12 +57,29 @@ module RuboCop
44
57
  # @!method assignment_method_declarations(node)
45
58
  def_node_search :assignment_method_declarations, <<~PATTERN
46
59
  (send
47
- (lvar #match_block_variable_name?) _ ...)
60
+ (lvar {#match_block_variable_name? :_1 :it}) _ ...)
61
+ PATTERN
62
+
63
+ # @!method indexed_assignment_method_declarations(node)
64
+ def_node_search :indexed_assignment_method_declarations, <<~PATTERN
65
+ (send
66
+ (send (lvar {#match_block_variable_name? :_1 :it}) _)
67
+ :[]=
68
+ literal?
69
+ _
70
+ )
48
71
  PATTERN
49
72
 
50
73
  def on_new_investigation
51
74
  return if processed_source.blank?
52
75
 
76
+ process_assignment_method_nodes
77
+ process_indexed_assignment_method_nodes
78
+ end
79
+
80
+ private
81
+
82
+ def process_assignment_method_nodes
53
83
  duplicated_assignment_method_nodes.each do |nodes|
54
84
  nodes[1..].each do |node|
55
85
  register_offense(node, node.method_name, nodes.first.first_line)
@@ -57,7 +87,14 @@ module RuboCop
57
87
  end
58
88
  end
59
89
 
60
- private
90
+ def process_indexed_assignment_method_nodes
91
+ duplicated_indexed_assignment_method_nodes.each do |nodes|
92
+ nodes[1..].each do |node|
93
+ assignment = "#{node.children.first.method_name}[#{node.first_argument.source}]="
94
+ register_offense(node, assignment, nodes.first.first_line)
95
+ end
96
+ end
97
+ end
61
98
 
62
99
  def match_block_variable_name?(receiver_name)
63
100
  gem_specification(processed_source.ast) do |block_variable_name|
@@ -73,6 +110,13 @@ module RuboCop
73
110
  .select { |nodes| nodes.size > 1 }
74
111
  end
75
112
 
113
+ def duplicated_indexed_assignment_method_nodes
114
+ indexed_assignment_method_declarations(processed_source.ast)
115
+ .group_by { |node| [node.children.first.method_name, node.first_argument] }
116
+ .values
117
+ .select { |nodes| nodes.size > 1 }
118
+ end
119
+
76
120
  def register_offense(node, assignment, line_of_first_occurrence)
77
121
  line_range = node.loc.column...node.loc.last_column
78
122
  offense_location = source_range(processed_source.buffer, node.first_line, line_range)
@@ -71,7 +71,7 @@ module RuboCop
71
71
 
72
72
  dependency_declarations(processed_source.ast)
73
73
  .each_cons(2) do |previous, current|
74
- next unless consecutive_lines(previous, current)
74
+ next unless consecutive_lines?(previous, current)
75
75
  next unless case_insensitive_out_of_order?(gem_name(current), gem_name(previous))
76
76
  next unless get_dependency_name(previous) == get_dependency_name(current)
77
77
 
@@ -29,6 +29,7 @@ module RuboCop
29
29
  NODE_GROUPS = {
30
30
  any_block: %i[block numblock itblock],
31
31
  any_def: %i[def defs],
32
+ any_match_pattern: %i[match_pattern match_pattern_p],
32
33
  argument: %i[arg optarg restarg kwarg kwoptarg kwrestarg blockarg forward_arg shadowarg],
33
34
  boolean: %i[true false],
34
35
  call: %i[send csend],
@@ -22,6 +22,11 @@ module RuboCop
22
22
  # * Private attribute macros (`attr_accessor`, `attr_writer`, `attr_reader`)
23
23
  # * Private instance methods
24
24
  #
25
+ # NOTE: Simply enabling the cop with `Enabled: true` will not use
26
+ # the example order shown below.
27
+ # To enforce the order of macros like `attr_reader`,
28
+ # you must define both `ExpectedOrder` *and* `Categories`.
29
+ #
25
30
  # You can configure the following order:
26
31
  #
27
32
  # [source,yaml]
@@ -68,6 +73,36 @@ module RuboCop
68
73
  # - extend
69
74
  # ----
70
75
  #
76
+ # If you only set `ExpectedOrder`
77
+ # without defining `Categories`,
78
+ # macros such as `attr_reader` or `has_many`
79
+ # will not be recognized as part of a category, and their order will not be validated.
80
+ # For example, the following will NOT raise any offenses, even if the order is incorrect:
81
+ #
82
+ # [source,yaml]
83
+ # ----
84
+ # Layout/ClassStructure:
85
+ # Enabled: true
86
+ # ExpectedOrder:
87
+ # - public_attribute_macros
88
+ # - initializer
89
+ # ----
90
+ #
91
+ # To make it work as expected, you must also specify `Categories` like this:
92
+ #
93
+ # [source,yaml]
94
+ # ----
95
+ # Layout/ClassStructure:
96
+ # ExpectedOrder:
97
+ # - public_attribute_macros
98
+ # - initializer
99
+ # Categories:
100
+ # attribute_macros:
101
+ # - attr_reader
102
+ # - attr_writer
103
+ # - attr_accessor
104
+ # ----
105
+ #
71
106
  # @safety
72
107
  # Autocorrection is unsafe because class methods and module inclusion
73
108
  # can behave differently, based on which methods or constants have
@@ -116,7 +116,7 @@ module RuboCop
116
116
  def allowed_only_before_style?(node)
117
117
  if node.special_modifier?
118
118
  return true if processed_source[node.last_line] == 'end'
119
- return false if next_line_empty?(node.last_line)
119
+ return false if next_line_empty_and_exists?(node.last_line)
120
120
  end
121
121
 
122
122
  previous_line_empty?(node.first_line)
@@ -129,7 +129,7 @@ module RuboCop
129
129
  when :around
130
130
  corrector.insert_after(line, "\n") unless next_line_empty?(node.last_line)
131
131
  when :only_before
132
- if next_line_empty?(node.last_line)
132
+ if next_line_empty_and_exists?(node.last_line)
133
133
  range = next_empty_line_range(node)
134
134
 
135
135
  corrector.remove(range)
@@ -138,7 +138,7 @@ module RuboCop
138
138
  end
139
139
 
140
140
  def previous_line_ignoring_comments(processed_source, send_line)
141
- processed_source[0..send_line - 2].reverse.find { |line| !comment_line?(line) }
141
+ processed_source[0..(send_line - 2)].reverse.find { |line| !comment_line?(line) }
142
142
  end
143
143
 
144
144
  def previous_line_empty?(send_line)
@@ -154,6 +154,10 @@ module RuboCop
154
154
  body_end?(last_send_line) || next_line.blank?
155
155
  end
156
156
 
157
+ def next_line_empty_and_exists?(last_send_line)
158
+ next_line_empty?(last_send_line) && last_send_line.next != processed_source.lines.size
159
+ end
160
+
157
161
  def empty_lines_around?(node)
158
162
  previous_line_empty?(node.first_line) && next_line_empty?(node.last_line)
159
163
  end
@@ -155,7 +155,7 @@ module RuboCop
155
155
  def on_send(node)
156
156
  return unless should_check?(node)
157
157
  return if same_line?(node, node.first_argument)
158
- return if style != :consistent && enforce_first_argument_with_fixed_indentation? &&
158
+ return if enforce_first_argument_with_fixed_indentation? &&
159
159
  !enable_layout_first_method_argument_line_break?
160
160
 
161
161
  indent = base_indentation(node) + configured_indentation_width
@@ -250,7 +250,7 @@ module RuboCop
250
250
  reset!
251
251
 
252
252
  alignment_for(first_pair).each do |alignment|
253
- delta = alignment.deltas_for_first_pair(first_pair, node)
253
+ delta = alignment.deltas_for_first_pair(first_pair)
254
254
  check_delta delta, node: first_pair, alignment: alignment
255
255
  end
256
256
 
@@ -58,6 +58,12 @@ module RuboCop
58
58
  # attr_reader :name #: String
59
59
  # attr_reader :age #: Integer?
60
60
  #
61
+ # #: (
62
+ # #| Integer,
63
+ # #| String
64
+ # #| ) -> void
65
+ # def foo; end
66
+ #
61
67
  # @example AllowRBSInlineAnnotation: true
62
68
  #
63
69
  # # good
@@ -67,6 +73,12 @@ module RuboCop
67
73
  # attr_reader :name #: String
68
74
  # attr_reader :age #: Integer?
69
75
  #
76
+ # #: (
77
+ # #| Integer,
78
+ # #| String
79
+ # #| ) -> void
80
+ # def foo; end
81
+ #
70
82
  # @example AllowSteepAnnotation: false (default)
71
83
  #
72
84
  # # bad
@@ -175,7 +187,7 @@ module RuboCop
175
187
  end
176
188
 
177
189
  def rbs_inline_annotation?(comment)
178
- allow_rbs_inline_annotation? && comment.text.start_with?(/#:|#\[.+\]/)
190
+ allow_rbs_inline_annotation? && comment.text.start_with?(/#:|#\[.+\]|#\|/)
179
191
  end
180
192
 
181
193
  def allow_steep_annotation?
@@ -23,6 +23,16 @@ module RuboCop
23
23
  def kind(token)
24
24
  'semicolon' if token.semicolon?
25
25
  end
26
+
27
+ def space_missing?(token1, token2)
28
+ super && !semicolon_sequence?(token1, token2)
29
+ end
30
+
31
+ private
32
+
33
+ def semicolon_sequence?(token, next_token)
34
+ token.semicolon? && next_token.semicolon?
35
+ end
26
36
  end
27
37
  end
28
38
  end
@@ -22,51 +22,25 @@ module RuboCop
22
22
  RESTRICT_ON_SEND = %i[[] []=].freeze
23
23
 
24
24
  def on_send(node)
25
- return unless (first_argument = node.first_argument)
26
-
27
- begin_pos = first_argument.source_range.begin_pos
28
- return unless (range = offense_range(node, begin_pos))
29
-
30
- register_offense(range)
31
- end
32
-
33
- private
34
-
35
- def offense_range(node, begin_pos)
36
25
  receiver_end_pos = node.receiver.source_range.end_pos
37
26
  selector_begin_pos = node.loc.selector.begin_pos
38
27
  return if receiver_end_pos >= selector_begin_pos
39
28
  return if dot_before_brackets?(node, receiver_end_pos, selector_begin_pos)
40
29
 
41
- if reference_variable_with_brackets?(node)
42
- range_between(receiver_end_pos, selector_begin_pos)
43
- elsif node.method?(:[]=)
44
- offense_range_for_assignment(node, begin_pos)
30
+ range = range_between(receiver_end_pos, selector_begin_pos)
31
+
32
+ add_offense(range) do |corrector|
33
+ corrector.remove(range)
45
34
  end
46
35
  end
47
36
 
37
+ private
38
+
48
39
  def dot_before_brackets?(node, receiver_end_pos, selector_begin_pos)
49
40
  return false unless node.loc.respond_to?(:dot) && (dot = node.loc.dot)
50
41
 
51
42
  dot.begin_pos == receiver_end_pos && dot.end_pos == selector_begin_pos
52
43
  end
53
-
54
- def offense_range_for_assignment(node, begin_pos)
55
- end_pos = node.receiver.source_range.end_pos
56
-
57
- return if begin_pos - end_pos == 1 ||
58
- (range = range_between(end_pos, begin_pos - 1)).source.start_with?('[')
59
-
60
- range
61
- end
62
-
63
- def register_offense(range)
64
- add_offense(range) { |corrector| corrector.remove(range) }
65
- end
66
-
67
- def reference_variable_with_brackets?(node)
68
- node.receiver&.variable? && node.method?(:[]) && node.arguments.size == 1
69
- end
70
44
  end
71
45
  end
72
46
  end