rubocop 1.22.1 → 1.23.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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +43 -7
  4. data/lib/rubocop/cop/bundler/gem_comment.rb +3 -3
  5. data/lib/rubocop/cop/gemspec/date_assignment.rb +2 -10
  6. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +1 -10
  7. data/lib/rubocop/cop/gemspec/require_mfa.rb +146 -0
  8. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +30 -23
  9. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -10
  10. data/lib/rubocop/cop/generator.rb +1 -1
  11. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +60 -0
  12. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  13. data/lib/rubocop/cop/layout/assignment_indentation.rb +1 -1
  14. data/lib/rubocop/cop/layout/block_alignment.rb +3 -3
  15. data/lib/rubocop/cop/layout/dot_position.rb +9 -7
  16. data/lib/rubocop/cop/layout/empty_comment.rb +1 -1
  17. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +22 -1
  18. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +7 -4
  19. data/lib/rubocop/cop/layout/end_alignment.rb +1 -2
  20. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +1 -1
  21. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +1 -1
  22. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
  23. data/lib/rubocop/cop/layout/hash_alignment.rb +1 -1
  24. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
  25. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  26. data/lib/rubocop/cop/layout/line_length.rb +1 -1
  27. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -1
  28. data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -2
  29. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +1 -1
  30. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -1
  31. data/lib/rubocop/cop/layout/space_before_comment.rb +1 -1
  32. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +11 -5
  33. data/lib/rubocop/cop/layout/space_inside_parens.rb +0 -4
  34. data/lib/rubocop/cop/lint/ambiguous_range.rb +3 -3
  35. data/lib/rubocop/cop/lint/deprecated_constants.rb +3 -2
  36. data/lib/rubocop/cop/lint/else_layout.rb +1 -1
  37. data/lib/rubocop/cop/lint/number_conversion.rb +5 -2
  38. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +117 -0
  39. data/lib/rubocop/cop/metrics/parameter_lists.rb +5 -2
  40. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -2
  41. data/lib/rubocop/cop/mixin/gemspec_help.rb +30 -0
  42. data/lib/rubocop/cop/mixin/hash_transform_method.rb +3 -3
  43. data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +1 -1
  44. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
  45. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +1 -1
  46. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  47. data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
  48. data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -5
  49. data/lib/rubocop/cop/mixin/trailing_body.rb +1 -1
  50. data/lib/rubocop/cop/naming/file_name.rb +37 -4
  51. data/lib/rubocop/cop/security/json_load.rb +1 -1
  52. data/lib/rubocop/cop/style/commented_keyword.rb +5 -3
  53. data/lib/rubocop/cop/style/documentation.rb +1 -1
  54. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  55. data/lib/rubocop/cop/style/format_string_token.rb +2 -1
  56. data/lib/rubocop/cop/style/line_end_concatenation.rb +1 -1
  57. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +1 -1
  58. data/lib/rubocop/cop/style/multiline_when_then.rb +1 -1
  59. data/lib/rubocop/cop/style/open_struct_use.rb +69 -0
  60. data/lib/rubocop/cop/style/parentheses_around_condition.rb +12 -2
  61. data/lib/rubocop/cop/style/quoted_symbols.rb +11 -1
  62. data/lib/rubocop/cop/style/select_by_regexp.rb +9 -3
  63. data/lib/rubocop/cop/util.rb +11 -1
  64. data/lib/rubocop/formatter/html_formatter.rb +5 -2
  65. data/lib/rubocop/formatter/json_formatter.rb +4 -1
  66. data/lib/rubocop/remote_config.rb +1 -1
  67. data/lib/rubocop/rspec/parallel_formatter.rb +90 -0
  68. data/lib/rubocop/rspec/support.rb +1 -0
  69. data/lib/rubocop/target_finder.rb +1 -1
  70. data/lib/rubocop/version.rb +1 -1
  71. data/lib/rubocop/yaml_duplication_checker.rb +1 -1
  72. data/lib/rubocop.rb +4 -0
  73. metadata +14 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc48ec8bad4f5af1a464cb2d8d512975a10efd1d9c73e74ce024f798a4b200cc
4
- data.tar.gz: 65ed09a920ce2211ab2d29a26780d1b198c08d29b3af92ef783c46024e056607
3
+ metadata.gz: cf11fe2d8f8be8879d6afc63d6f3be50b970c7910c83f1d22847d1fc9654d4aa
4
+ data.tar.gz: fd4c7ef3838a286efe3d1729672cc7a0e72f47cfb4c8d815699d53d2a974a372
5
5
  SHA512:
6
- metadata.gz: 9fff6f773ae50140975bfac3f671f1657cd7da4c1df10623a7e36096e21032630fa8c41eb5a5d4eb8f07b92a48e7d99fc2b40d20da5811fc4bb0060dfc410e3b
7
- data.tar.gz: 0aa4fa740168feb2643edbeecedc723c3da44c0f2c4843a39b9593c7dda07db21d05552ae52731b1b1ddbeeb2ffe0816d2c5e7d0423caec2dc40888cbd52758f
6
+ metadata.gz: 4014183525b58378a0e628ae5136d17c148a41c6c25dd51eddc810ca08fbd89d652f6c715b58eef149005af1145469936416864869b6e63a4c9e521627ab1cb4
7
+ data.tar.gz: 6e0e2a9a9d6fae829b73bd49814965a28d447207aaf7d1dda073f34130343557df965c77cc998d6ddb0d59eca09f1307d6ca56cbd3fcade575ca52950d1a56d3
data/README.md CHANGED
@@ -54,7 +54,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi
54
54
  in your `Gemfile`:
55
55
 
56
56
  ```rb
57
- gem 'rubocop', '~> 1.22', require: false
57
+ gem 'rubocop', '~> 1.23', require: false
58
58
  ```
59
59
 
60
60
  See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
data/config/default.yml CHANGED
@@ -150,6 +150,7 @@ AllCops:
150
150
  rubocop-minitest: [minitest]
151
151
  rubocop-sequel: [sequel]
152
152
  rubocop-rake: [rake]
153
+ rubocop-graphql: [graphql]
153
154
 
154
155
  #################### Bundler ###############################
155
156
 
@@ -258,11 +259,20 @@ Gemspec/OrderedDependencies:
258
259
  Include:
259
260
  - '**/*.gemspec'
260
261
 
262
+ Gemspec/RequireMFA:
263
+ Description: 'Checks that the gemspec has metadata to require MFA from RubyGems.'
264
+ Enabled: pending
265
+ VersionAdded: '1.23'
266
+ Reference:
267
+ - https://guides.rubygems.org/mfa-requirement-opt-in/
268
+ Include:
269
+ - '**/*.gemspec'
270
+
261
271
  Gemspec/RequiredRubyVersion:
262
272
  Description: 'Checks that `required_ruby_version` of gemspec is specified and equal to `TargetRubyVersion` of .rubocop.yml.'
263
273
  Enabled: true
264
274
  VersionAdded: '0.52'
265
- VersionChanged: '0.89'
275
+ VersionChanged: '1.22'
266
276
  Include:
267
277
  - '**/*.gemspec'
268
278
 
@@ -511,13 +521,13 @@ Layout/EmptyLineBetweenDefs:
511
521
  StyleGuide: '#empty-lines-between-methods'
512
522
  Enabled: true
513
523
  VersionAdded: '0.49'
514
- VersionChanged: '1.7'
524
+ VersionChanged: '1.23'
515
525
  EmptyLineBetweenMethodDefs: true
516
526
  EmptyLineBetweenClassDefs: true
517
527
  EmptyLineBetweenModuleDefs: true
518
- # If `true`, this parameter means that single line method definitions don't
519
- # need an empty line between them.
520
- AllowAdjacentOneLineDefs: false
528
+ # `AllowAdjacentOneLineDefs` means that single line method definitions don't
529
+ # need an empty line between them. `true` by default.
530
+ AllowAdjacentOneLineDefs: true
521
531
  # Can be array to specify minimum and maximum number of empty lines, e.g. [1, 2]
522
532
  NumberOfEmptyLines: 1
523
533
 
@@ -1568,6 +1578,7 @@ Lint/DeprecatedConstants:
1568
1578
  Description: 'Checks for deprecated constants.'
1569
1579
  Enabled: pending
1570
1580
  VersionAdded: '1.8'
1581
+ VersionChanged: '1.22'
1571
1582
  # You can configure deprecated constants.
1572
1583
  # If there is an alternative method, you can set alternative value as `Alternative`.
1573
1584
  # And you can set the deprecated version as `DeprecatedVersion`.
@@ -1588,6 +1599,9 @@ Lint/DeprecatedConstants:
1588
1599
  'FALSE':
1589
1600
  Alternative: 'false'
1590
1601
  DeprecatedVersion: '2.4'
1602
+ 'Net::HTTPServerException':
1603
+ Alternative: 'Net::HTTPClientException'
1604
+ DeprecatedVersion: '2.6'
1591
1605
  'Random::DEFAULT':
1592
1606
  Alternative: 'Random.new'
1593
1607
  DeprecatedVersion: '3.0'
@@ -2304,6 +2318,11 @@ Lint/UselessMethodDefinition:
2304
2318
  Safe: false
2305
2319
  AllowComments: true
2306
2320
 
2321
+ Lint/UselessRuby2Keywords:
2322
+ Description: 'Finds unnecessary uses of `ruby2_keywords`.'
2323
+ Enabled: pending
2324
+ VersionAdded: '1.23'
2325
+
2307
2326
  Lint/UselessSetterCall:
2308
2327
  Description: 'Checks for useless setter call to a local variable.'
2309
2328
  Enabled: true
@@ -2497,6 +2516,7 @@ Naming/FileName:
2497
2516
  StyleGuide: '#snake-case-files'
2498
2517
  Enabled: true
2499
2518
  VersionAdded: '0.50'
2519
+ VersionChanged: '1.23'
2500
2520
  # Camel case file names listed in `AllCops:Include` and all file names listed
2501
2521
  # in `AllCops:Exclude` are excluded by default. Add extra excludes here.
2502
2522
  Exclude: []
@@ -2509,6 +2529,13 @@ Naming/FileName:
2509
2529
  # whether each source file's class or module name matches the file name --
2510
2530
  # not whether the nested module hierarchy matches the subdirectory path.
2511
2531
  CheckDefinitionPathHierarchy: true
2532
+ # paths that are considered root directories, for example "lib" in most ruby projects
2533
+ # or "app/models" in rails projects
2534
+ CheckDefinitionPathHierarchyRoots:
2535
+ - lib
2536
+ - spec
2537
+ - test
2538
+ - src
2512
2539
  # If non-`nil`, expect all source file names to match the following regex.
2513
2540
  # Only the file name itself is matched, not the entire file path.
2514
2541
  # Use anchors as necessary if you want to match the entire name rather than
@@ -2754,10 +2781,9 @@ Security/JSONLoad:
2754
2781
  Reference: 'https://ruby-doc.org/stdlib-2.7.0/libdoc/json/rdoc/JSON.html#method-i-load'
2755
2782
  Enabled: true
2756
2783
  VersionAdded: '0.43'
2757
- VersionChanged: '0.44'
2784
+ VersionChanged: '1.22'
2758
2785
  # Autocorrect here will change to a method that may cause crashes depending
2759
2786
  # on the value of the argument.
2760
- AutoCorrect: false
2761
2787
  SafeAutoCorrect: false
2762
2788
 
2763
2789
  Security/MarshalLoad:
@@ -4213,6 +4239,16 @@ Style/OneLineConditional:
4213
4239
  VersionAdded: '0.9'
4214
4240
  VersionChanged: '0.90'
4215
4241
 
4242
+ Style/OpenStructUse:
4243
+ Description: >-
4244
+ Avoid using OpenStruct. As of Ruby 3.0, use is officially discouraged due to performance,
4245
+ version compatibility, and potential security issues.
4246
+ Reference:
4247
+ - https://docs.ruby-lang.org/en/3.0.0/OpenStruct.html#class-OpenStruct-label-Caveats
4248
+
4249
+ Enabled: pending
4250
+ VersionAdded: '1.23'
4251
+
4216
4252
  Style/OptionHash:
4217
4253
  Description: "Don't use option hashes when you can use keyword arguments."
4218
4254
  Enabled: false
@@ -88,7 +88,7 @@ module RuboCop
88
88
  CHECKED_OPTIONS_CONFIG = 'OnlyFor'
89
89
  VERSION_SPECIFIERS_OPTION = 'version_specifiers'
90
90
  RESTRICTIVE_VERSION_SPECIFIERS_OPTION = 'restrictive_version_specifiers'
91
- RESTRICTIVE_VERSION_PATTERN = /<|~>/.freeze
91
+ RESTRICTIVE_VERSION_PATTERN = /\A\s*(?:<|~>|\d|=)/.freeze
92
92
  RESTRICT_ON_SEND = %i[gem].freeze
93
93
 
94
94
  def on_send(node)
@@ -152,8 +152,8 @@ module RuboCop
152
152
  def restrictive_version_specified_gem?(node)
153
153
  return unless version_specified_gem?(node)
154
154
 
155
- node.arguments
156
- .any? { |arg| arg&.str_type? && RESTRICTIVE_VERSION_PATTERN.match?(arg.to_s) }
155
+ node.arguments[1..-1]
156
+ .any? { |arg| arg&.str_type? && RESTRICTIVE_VERSION_PATTERN.match?(arg.value) }
157
157
  end
158
158
 
159
159
  def contains_checked_options?(node)
@@ -21,21 +21,13 @@ module RuboCop
21
21
  #
22
22
  class DateAssignment < Base
23
23
  include RangeHelp
24
+ include GemspecHelp
24
25
  extend AutoCorrector
25
26
 
26
27
  MSG = 'Do not use `date =` in gemspec, it is set automatically when the gem is packaged.'
27
28
 
28
- # @!method gem_specification(node)
29
- def_node_matcher :gem_specification, <<~PATTERN
30
- (block
31
- (send
32
- (const
33
- (const {cbase nil?} :Gem) :Specification) :new)
34
- ...)
35
- PATTERN
36
-
37
29
  def on_block(block_node)
38
- return unless gem_specification(block_node)
30
+ return unless gem_specification?(block_node)
39
31
 
40
32
  block_parameter = block_node.arguments.first.source
41
33
 
@@ -36,20 +36,11 @@ module RuboCop
36
36
  # end
37
37
  class DuplicatedAssignment < Base
38
38
  include RangeHelp
39
+ include GemspecHelp
39
40
 
40
41
  MSG = '`%<assignment>s` method calls already given on line '\
41
42
  '%<line_of_first_occurrence>d of the gemspec.'
42
43
 
43
- # @!method gem_specification(node)
44
- def_node_search :gem_specification, <<~PATTERN
45
- (block
46
- (send
47
- (const
48
- (const {cbase nil?} :Gem) :Specification) :new)
49
- (args
50
- (arg $_)) ...)
51
- PATTERN
52
-
53
44
  # @!method assignment_method_declarations(node)
54
45
  def_node_search :assignment_method_declarations, <<~PATTERN
55
46
  (send
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Gemspec
6
+ # Requires a gemspec to have `rubygems_mfa_required` metadata set.
7
+ #
8
+ # This setting tells RubyGems that MFA is required for accounts to
9
+ # be able perform any of these privileged operations:
10
+ #
11
+ # * gem push
12
+ # * gem yank
13
+ # * gem owner --add/remove
14
+ # * adding or removing owners using gem ownership page
15
+ #
16
+ # This helps make your gem more secure, as users can be more
17
+ # confident that gem updates were pushed by maintainers.
18
+ #
19
+ # @example
20
+ #
21
+ # # bad
22
+ # Gem::Specification.new do |spec|
23
+ # # no `rubygems_mfa_required` metadata specified
24
+ # end
25
+ #
26
+ # # good
27
+ # Gem::Specification.new do |spec|
28
+ # spec.metadata = {
29
+ # 'rubygems_mfa_required' => 'true'
30
+ # }
31
+ # end
32
+ #
33
+ # # good
34
+ # Gem::Specification.new do |spec|
35
+ # spec.metadata['rubygems_mfa_required'] = 'true'
36
+ # end
37
+ #
38
+ # # bad
39
+ # Gem::Specification.new do |spec|
40
+ # spec.metadata = {
41
+ # 'rubygems_mfa_required' => 'false'
42
+ # }
43
+ # end
44
+ #
45
+ # # good
46
+ # Gem::Specification.new do |spec|
47
+ # spec.metadata = {
48
+ # 'rubygems_mfa_required' => 'true'
49
+ # }
50
+ # end
51
+ #
52
+ # # bad
53
+ # Gem::Specification.new do |spec|
54
+ # spec.metadata['rubygems_mfa_required'] = 'false'
55
+ # end
56
+ #
57
+ # # good
58
+ # Gem::Specification.new do |spec|
59
+ # spec.metadata['rubygems_mfa_required'] = 'true'
60
+ # end
61
+ #
62
+ class RequireMFA < Base
63
+ include GemspecHelp
64
+ extend AutoCorrector
65
+
66
+ MSG = "`metadata['rubygems_mfa_required']` must be set to `'true'`."
67
+
68
+ # @!method metadata(node)
69
+ def_node_matcher :metadata, <<~PATTERN
70
+ `{
71
+ (send _ :metadata= $_)
72
+ (send (send _ :metadata) :[]= (str "rubygems_mfa_required") $_)
73
+ }
74
+ PATTERN
75
+
76
+ # @!method rubygems_mfa_required(node)
77
+ def_node_search :rubygems_mfa_required, <<~PATTERN
78
+ (pair (str "rubygems_mfa_required") $_)
79
+ PATTERN
80
+
81
+ # @!method true_string?(node)
82
+ def_node_matcher :true_string?, <<~PATTERN
83
+ (str "true")
84
+ PATTERN
85
+
86
+ def on_block(node) # rubocop:disable Metrics/MethodLength
87
+ gem_specification(node) do |block_var|
88
+ metadata_value = metadata(node)
89
+ mfa_value = mfa_value(metadata_value)
90
+
91
+ if mfa_value
92
+ unless true_string?(mfa_value)
93
+ add_offense(mfa_value) do |corrector|
94
+ change_value(corrector, mfa_value)
95
+ end
96
+ end
97
+ else
98
+ add_offense(node) do |corrector|
99
+ autocorrect(corrector, node, block_var, metadata_value)
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ private
106
+
107
+ def mfa_value(metadata_value)
108
+ return unless metadata_value
109
+ return metadata_value if metadata_value.str_type?
110
+
111
+ rubygems_mfa_required(metadata_value).first
112
+ end
113
+
114
+ def autocorrect(corrector, node, block_var, metadata)
115
+ if metadata
116
+ return unless metadata.hash_type?
117
+
118
+ correct_metadata(corrector, metadata)
119
+ else
120
+ correct_missing_metadata(corrector, node, block_var)
121
+ end
122
+ end
123
+
124
+ def correct_metadata(corrector, metadata)
125
+ if metadata.pairs.any?
126
+ corrector.insert_after(metadata.pairs.last, ",\n'rubygems_mfa_required' => 'true'")
127
+ else
128
+ corrector.insert_before(metadata.loc.end, "'rubygems_mfa_required' => 'true'")
129
+ end
130
+ end
131
+
132
+ def correct_missing_metadata(corrector, node, block_var)
133
+ corrector.insert_before(node.loc.end, <<~RUBY)
134
+ #{block_var}.metadata = {
135
+ 'rubygems_mfa_required' => 'true'
136
+ }
137
+ RUBY
138
+ end
139
+
140
+ def change_value(corrector, value)
141
+ corrector.replace(value, "'true'")
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -3,10 +3,11 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Gemspec
6
- # Checks that `required_ruby_version` of gemspec is specified and
7
- # equal to `TargetRubyVersion` of .rubocop.yml.
8
- # Thereby, RuboCop to perform static analysis working on the version
9
- # required by gemspec.
6
+ # Checks that `required_ruby_version` in a gemspec file is set to a valid
7
+ # value (non-blank) and matches `TargetRubyVersion` as set in RuboCop's
8
+ # configuration for the gem.
9
+ #
10
+ # This ensures that RuboCop is using the same Ruby version as the gem.
10
11
  #
11
12
  # @example
12
13
  # # When `TargetRubyVersion` of .rubocop.yml is `2.5`.
@@ -26,6 +27,11 @@ module RuboCop
26
27
  # spec.required_ruby_version = '>= 2.6.0'
27
28
  # end
28
29
  #
30
+ # # bad
31
+ # Gem::Specification.new do |spec|
32
+ # spec.required_ruby_version = ''
33
+ # end
34
+ #
29
35
  # # good
30
36
  # Gem::Specification.new do |spec|
31
37
  # spec.required_ruby_version = '>= 2.5.0'
@@ -49,15 +55,15 @@ module RuboCop
49
55
  class RequiredRubyVersion < Base
50
56
  include RangeHelp
51
57
 
52
- NOT_EQUAL_MSG = '`required_ruby_version` (%<required_ruby_version>s, ' \
53
- 'declared in %<gemspec_filename>s) and `TargetRubyVersion` ' \
58
+ RESTRICT_ON_SEND = %i[required_ruby_version=].freeze
59
+ NOT_EQUAL_MSG = '`required_ruby_version` and `TargetRubyVersion` ' \
54
60
  '(%<target_ruby_version>s, which may be specified in ' \
55
61
  '.rubocop.yml) should be equal.'
56
62
  MISSING_MSG = '`required_ruby_version` should be specified.'
57
63
 
58
- # @!method required_ruby_version(node)
59
- def_node_search :required_ruby_version, <<~PATTERN
60
- (send _ :required_ruby_version= $_)
64
+ # @!method required_ruby_version?(node)
65
+ def_node_search :required_ruby_version?, <<~PATTERN
66
+ (send _ :required_ruby_version= _)
61
67
  PATTERN
62
68
 
63
69
  # @!method defined_ruby_version(node)
@@ -66,27 +72,28 @@ module RuboCop
66
72
  (send (const (const nil? :Gem) :Requirement) :new $(str _))}
67
73
  PATTERN
68
74
 
69
- # rubocop:disable Metrics/AbcSize
70
75
  def on_new_investigation
71
- version_def = required_ruby_version(processed_source.ast).first
76
+ add_global_offense(MISSING_MSG) unless required_ruby_version?(processed_source.ast)
77
+ end
72
78
 
73
- if version_def
74
- ruby_version = extract_ruby_version(defined_ruby_version(version_def))
75
- return if !ruby_version || ruby_version == target_ruby_version.to_s
79
+ def on_send(node)
80
+ version_def = node.first_argument
81
+ return if dynamic_version?(version_def)
76
82
 
77
- add_offense(
78
- version_def.loc.expression,
79
- message: not_equal_message(ruby_version, target_ruby_version)
80
- )
81
- else
82
- range = source_range(processed_source.buffer, 1, 0)
83
- add_offense(range, message: MISSING_MSG)
84
- end
83
+ ruby_version = extract_ruby_version(defined_ruby_version(version_def))
84
+ return if ruby_version == target_ruby_version.to_s
85
+
86
+ add_offense(version_def, message: not_equal_message(ruby_version, target_ruby_version))
85
87
  end
86
- # rubocop:enable Metrics/AbcSize
87
88
 
88
89
  private
89
90
 
91
+ def dynamic_version?(node)
92
+ (node.send_type? && !node.receiver) ||
93
+ node.variable? ||
94
+ node.each_descendant(:send, *RuboCop::AST::Node::VARIABLES).any?
95
+ end
96
+
90
97
  def extract_ruby_version(required_ruby_version)
91
98
  return unless required_ruby_version
92
99
 
@@ -26,20 +26,13 @@ module RuboCop
26
26
  # end
27
27
  #
28
28
  class RubyVersionGlobalsUsage < Base
29
+ include GemspecHelp
30
+
29
31
  MSG = 'Do not use `RUBY_VERSION` in gemspec file.'
30
32
 
31
33
  # @!method ruby_version?(node)
32
34
  def_node_matcher :ruby_version?, '(const {cbase nil?} :RUBY_VERSION)'
33
35
 
34
- # @!method gem_specification?(node)
35
- def_node_search :gem_specification?, <<~PATTERN
36
- (block
37
- (send
38
- (const
39
- (const {cbase nil?} :Gem) :Specification) :new)
40
- ...)
41
- PATTERN
42
-
43
36
  def on_const(node)
44
37
  return unless gem_spec_with_ruby_version?(node)
45
38
 
@@ -49,7 +42,7 @@ module RuboCop
49
42
  private
50
43
 
51
44
  def gem_spec_with_ruby_version?(node)
52
- gem_specification?(processed_source.ast) && ruby_version?(node)
45
+ gem_specification(processed_source.ast) && ruby_version?(node)
53
46
  end
54
47
  end
55
48
  end
@@ -150,7 +150,7 @@ module RuboCop
150
150
  1. Modify the description of #{badge} in config/default.yml
151
151
  2. Implement your new cop in the generated file!
152
152
  3. Commit your new cop with a message such as
153
- e.g. "Add new `#{badge}` cop."
153
+ e.g. "Add new `#{badge}` cop"
154
154
  4. Run `bundle exec rake changelog:new` to generate a changelog entry
155
155
  for your new cop.
156
156
  TODO
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # This cop enforces the use of `same_line?` instead of location line comparison for equality.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # node.loc.line == node.parent.loc.line
11
+ #
12
+ # # bad
13
+ # node.loc.first_line == node.parent.loc.first_line
14
+ #
15
+ # # good
16
+ # same_line?(node, node.parent)
17
+ #
18
+ class LocationLineEqualityComparison < Base
19
+ extend AutoCorrector
20
+
21
+ MSG = 'Use `%<preferred>s`.'
22
+
23
+ # @!method line_send(node)
24
+ def_node_matcher :line_send, <<~PATTERN
25
+ {
26
+ (send (send _ {:loc :source_range}) {:line :first_line})
27
+ (send _ :first_line)
28
+ }
29
+ PATTERN
30
+
31
+ # @!method location_line_equality_comparison?(node)
32
+ def_node_matcher :location_line_equality_comparison?, <<~PATTERN
33
+ (send #line_send :== #line_send)
34
+ PATTERN
35
+
36
+ def on_send(node)
37
+ return unless location_line_equality_comparison?(node)
38
+
39
+ lhs, _op, rhs = *node
40
+
41
+ lhs_receiver = extract_receiver(lhs)
42
+ rhs_receiver = extract_receiver(rhs)
43
+ preferred = "same_line?(#{lhs_receiver}, #{rhs_receiver})"
44
+
45
+ add_offense(node, message: format(MSG, preferred: preferred)) do |corrector|
46
+ corrector.replace(node, preferred)
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def extract_receiver(node)
53
+ receiver = node.receiver
54
+ receiver = receiver.receiver if receiver.method?(:loc) || receiver.method?(:source_range)
55
+ receiver.source
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -3,6 +3,7 @@
3
3
  require_relative 'internal_affairs/empty_line_between_expect_offense_and_correction'
4
4
  require_relative 'internal_affairs/example_description'
5
5
  require_relative 'internal_affairs/inherit_deprecated_cop_class'
6
+ require_relative 'internal_affairs/location_line_equality_comparison'
6
7
  require_relative 'internal_affairs/method_name_equal'
7
8
  require_relative 'internal_affairs/node_destructuring'
8
9
  require_relative 'internal_affairs/node_matcher_directive'
@@ -33,7 +33,7 @@ module RuboCop
33
33
  def check_assignment(node, rhs)
34
34
  return unless rhs
35
35
  return unless node.loc.operator
36
- return if node.loc.operator.line == rhs.first_line
36
+ return if same_line?(node.loc.operator, rhs)
37
37
 
38
38
  base = display_column(leftmost_multiple_assignment(node).source_range)
39
39
  check_alignment([rhs], base + configured_indentation_width)
@@ -101,11 +101,11 @@ module RuboCop
101
101
  def block_end_align_target(node)
102
102
  lineage = [node, *node.ancestors]
103
103
 
104
- target = lineage.each_cons(2) do |current, parent|
105
- break current if end_align_target?(current, parent)
104
+ lineage.each_cons(2) do |current, parent|
105
+ return current if end_align_target?(current, parent)
106
106
  end
107
107
 
108
- target || lineage.last
108
+ lineage.last
109
109
  end
110
110
 
111
111
  def end_align_target?(node, parent)
@@ -68,18 +68,14 @@ module RuboCop
68
68
  end
69
69
 
70
70
  def proper_dot_position?(node)
71
- selector_line = selector_range(node).line
71
+ selector_range = selector_range(node)
72
72
 
73
- # If the receiver is a HEREDOC and the selector is on the same line
74
- # then there is nothing to do
75
- return true if heredoc?(node.receiver) && node.receiver.loc.first_line == selector_line
73
+ return true if same_line?(selector_range, end_range(node.receiver))
76
74
 
75
+ selector_line = selector_range.line
77
76
  receiver_line = receiver_end_line(node.receiver)
78
77
  dot_line = node.loc.dot.line
79
78
 
80
- # receiver and selector are on the same line
81
- return true if selector_line == receiver_line
82
-
83
79
  # don't register an offense if there is a line comment between the
84
80
  # dot and the selector otherwise, we might break the code while
85
81
  # "correcting" it (even if there is just an extra blank line, treat
@@ -123,7 +119,13 @@ module RuboCop
123
119
  (node.str_type? || node.dstr_type?) && node.heredoc?
124
120
  end
125
121
 
122
+ def end_range(node)
123
+ node.source_range.end
124
+ end
125
+
126
126
  def selector_range(node)
127
+ return node unless node.call_type?
128
+
127
129
  # l.(1) has no selector, so we use the opening parenthesis instead
128
130
  node.loc.selector || node.loc.begin
129
131
  end
@@ -96,7 +96,7 @@ module RuboCop
96
96
 
97
97
  def autocorrect(corrector, node)
98
98
  previous_token = previous_token(node)
99
- range = if previous_token && node.loc.line == previous_token.line
99
+ range = if previous_token && same_line?(node, previous_token)
100
100
  range_with_surrounding_space(range: node.loc.expression, newlines: false)
101
101
  else
102
102
  range_by_whole_lines(node.loc.expression, include_final_newline: true)
@@ -77,13 +77,34 @@ module RuboCop
77
77
  # def b
78
78
  # end
79
79
  #
80
- # @example AllowAdjacentOneLineDefs: true
80
+ # @example AllowAdjacentOneLineDefs: true (default)
81
81
  #
82
82
  # # good
83
83
  # class ErrorA < BaseError; end
84
84
  # class ErrorB < BaseError; end
85
85
  # class ErrorC < BaseError; end
86
86
  #
87
+ # # good
88
+ # class ErrorA < BaseError; end
89
+ #
90
+ # class ErrorB < BaseError; end
91
+ #
92
+ # class ErrorC < BaseError; end
93
+ #
94
+ # @example AllowAdjacentOneLineDefs: false
95
+ #
96
+ # # bad
97
+ # class ErrorA < BaseError; end
98
+ # class ErrorB < BaseError; end
99
+ # class ErrorC < BaseError; end
100
+ #
101
+ # # good
102
+ # class ErrorA < BaseError; end
103
+ #
104
+ # class ErrorB < BaseError; end
105
+ #
106
+ # class ErrorC < BaseError; end
107
+ #
87
108
  class EmptyLineBetweenDefs < Base
88
109
  include RangeHelp
89
110
  extend AutoCorrector