rubocop 1.22.0 → 1.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +43 -7
  4. data/lib/rubocop/config_loader.rb +1 -1
  5. data/lib/rubocop/cop/bundler/gem_comment.rb +3 -3
  6. data/lib/rubocop/cop/gemspec/date_assignment.rb +2 -10
  7. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +1 -10
  8. data/lib/rubocop/cop/gemspec/require_mfa.rb +146 -0
  9. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +30 -23
  10. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -10
  11. data/lib/rubocop/cop/generator.rb +1 -1
  12. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +60 -0
  13. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  14. data/lib/rubocop/cop/layout/assignment_indentation.rb +1 -1
  15. data/lib/rubocop/cop/layout/block_alignment.rb +3 -3
  16. data/lib/rubocop/cop/layout/dot_position.rb +10 -4
  17. data/lib/rubocop/cop/layout/empty_comment.rb +1 -1
  18. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +22 -1
  19. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +7 -4
  20. data/lib/rubocop/cop/layout/end_alignment.rb +1 -2
  21. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +1 -1
  22. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +1 -1
  23. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
  24. data/lib/rubocop/cop/layout/hash_alignment.rb +1 -1
  25. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
  26. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  27. data/lib/rubocop/cop/layout/line_length.rb +1 -1
  28. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -1
  29. data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -2
  30. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +1 -1
  31. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -1
  32. data/lib/rubocop/cop/layout/space_before_comment.rb +1 -1
  33. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +11 -5
  34. data/lib/rubocop/cop/layout/space_inside_parens.rb +0 -4
  35. data/lib/rubocop/cop/lint/ambiguous_range.rb +3 -3
  36. data/lib/rubocop/cop/lint/deprecated_constants.rb +3 -2
  37. data/lib/rubocop/cop/lint/else_layout.rb +1 -1
  38. data/lib/rubocop/cop/lint/number_conversion.rb +5 -2
  39. data/lib/rubocop/cop/lint/require_relative_self_path.rb +1 -0
  40. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +117 -0
  41. data/lib/rubocop/cop/metrics/parameter_lists.rb +5 -2
  42. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -2
  43. data/lib/rubocop/cop/mixin/gemspec_help.rb +30 -0
  44. data/lib/rubocop/cop/mixin/hash_transform_method.rb +3 -3
  45. data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +1 -1
  46. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
  47. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +1 -1
  48. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  49. data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
  50. data/lib/rubocop/cop/mixin/trailing_body.rb +1 -1
  51. data/lib/rubocop/cop/naming/file_name.rb +37 -4
  52. data/lib/rubocop/cop/security/json_load.rb +1 -1
  53. data/lib/rubocop/cop/style/commented_keyword.rb +5 -3
  54. data/lib/rubocop/cop/style/documentation.rb +1 -1
  55. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  56. data/lib/rubocop/cop/style/format_string_token.rb +2 -1
  57. data/lib/rubocop/cop/style/line_end_concatenation.rb +1 -1
  58. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +1 -1
  59. data/lib/rubocop/cop/style/multiline_when_then.rb +1 -1
  60. data/lib/rubocop/cop/style/open_struct_use.rb +69 -0
  61. data/lib/rubocop/cop/style/parentheses_around_condition.rb +12 -2
  62. data/lib/rubocop/cop/style/quoted_symbols.rb +21 -7
  63. data/lib/rubocop/cop/style/redundant_argument.rb +5 -2
  64. data/lib/rubocop/cop/style/select_by_regexp.rb +39 -6
  65. data/lib/rubocop/cop/util.rb +13 -2
  66. data/lib/rubocop/formatter/html_formatter.rb +5 -2
  67. data/lib/rubocop/formatter/json_formatter.rb +4 -1
  68. data/lib/rubocop/rake_task.rb +1 -1
  69. data/lib/rubocop/remote_config.rb +1 -1
  70. data/lib/rubocop/result_cache.rb +2 -2
  71. data/lib/rubocop/rspec/parallel_formatter.rb +90 -0
  72. data/lib/rubocop/rspec/support.rb +1 -0
  73. data/lib/rubocop/runner.rb +1 -1
  74. data/lib/rubocop/target_finder.rb +1 -1
  75. data/lib/rubocop/version.rb +1 -1
  76. data/lib/rubocop/yaml_duplication_checker.rb +1 -1
  77. data/lib/rubocop.rb +4 -0
  78. metadata +10 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '08520f6dcf324dcb95ce7191673f070e86c3b5c2613942d19e4c951119a43476'
4
- data.tar.gz: 90fd28bb4692e3feb9973d0b711a181c90608b205741b4b68cc23df9a9d42142
3
+ metadata.gz: cf11fe2d8f8be8879d6afc63d6f3be50b970c7910c83f1d22847d1fc9654d4aa
4
+ data.tar.gz: fd4c7ef3838a286efe3d1729672cc7a0e72f47cfb4c8d815699d53d2a974a372
5
5
  SHA512:
6
- metadata.gz: 27a14f94b096be10618d7670ff591f4443ab4e09d8fce7f2604e686a1f7ab5544aaf53136c09a6ec62198f9f56950b5ab0c3cea64ee0ccd565e328b7ba131a5d
7
- data.tar.gz: 419cf3d94cf235cf31b09d26cbed550937dcb1930797e3e28c97cfb27116b160732aeec5434de8e0c3417f6900bf795c1826d7941e49cf8f12e9a15c245472b4
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
@@ -136,7 +136,7 @@ module RuboCop
136
136
  end
137
137
  end
138
138
 
139
- # Returns the path rubocop inferred as the root of the project. No file
139
+ # Returns the path RuboCop inferred as the root of the project. No file
140
140
  # searches will go past this directory.
141
141
  def project_root
142
142
  @project_root ||= find_project_root
@@ -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,12 +68,12 @@ module RuboCop
68
68
  end
69
69
 
70
70
  def proper_dot_position?(node)
71
- receiver_line = receiver_end_line(node.receiver)
72
- selector_line = selector_range(node).line
71
+ selector_range = selector_range(node)
73
72
 
74
- # receiver and selector are on the same line
75
- return true if selector_line == receiver_line
73
+ return true if same_line?(selector_range, end_range(node.receiver))
76
74
 
75
+ selector_line = selector_range.line
76
+ receiver_line = receiver_end_line(node.receiver)
77
77
  dot_line = node.loc.dot.line
78
78
 
79
79
  # don't register an offense if there is a line comment between the
@@ -119,7 +119,13 @@ module RuboCop
119
119
  (node.str_type? || node.dstr_type?) && node.heredoc?
120
120
  end
121
121
 
122
+ def end_range(node)
123
+ node.source_range.end
124
+ end
125
+
122
126
  def selector_range(node)
127
+ return node unless node.call_type?
128
+
123
129
  # l.(1) has no selector, so we use the opening parenthesis instead
124
130
  node.loc.selector || node.loc.begin
125
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)