rubocop-sorbet 0.7.7 → 0.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8a1efe9f67422bb3173e5d5f91b3ccfd9f3862218af34495ca45e37be474ce6d
4
- data.tar.gz: aeef803f94dd5084efa7d95cb1e899dd1e901b0a992acdb1e1bfe5b3613b78d7
3
+ metadata.gz: ddc9a9caf478aaa31d5b985064aadc77e9e2d199927b7db01eb5823ea2190c28
4
+ data.tar.gz: d440f179cff018d9ff54d68c01fafa9265d4020ca738f5311fe552b44c7d2074
5
5
  SHA512:
6
- metadata.gz: 607f488db395a271ab0fe3997f710f1f275d04c62b0fdd00bfb3bf4b3ffbd0274315d65d284440078fe92415b3ba7b66f364de415af004ab45f745238081de31
7
- data.tar.gz: d55d280a2c2404fe754bcb59445369b3c67c03329b4055700984e99effef6b6c53b7c44560925ee029f1a963649cbc65d30f49d30cb406fca7d92eafc83a6859
6
+ metadata.gz: b046329f103125db498e57acb613d387b780c6ec00c3d433cdb88e90ed1110091f5c6a1f59f0d3907abc98e1755ef332046b545b8dd54665939d997716ecf057
7
+ data.tar.gz: 68348b3e0ad4a8a6141526fb6c5406b42e7ea12661256f83258551b2a11c3a6c04ded7843b7398a4903fa9877cbd16c1acb4bf7f3ccce69c600fca5bc51b18bc
data/Gemfile CHANGED
@@ -7,6 +7,6 @@ gemspec
7
7
 
8
8
  gem "byebug"
9
9
  gem "rake", ">= 12.3.3"
10
- gem "rspec"
10
+ gem "rspec", "~> 3.7"
11
11
  gem "rubocop-shopify", require: false
12
12
  gem "yard", "~> 0.9"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rubocop-sorbet (0.7.7)
4
+ rubocop-sorbet (0.8.0)
5
5
  rubocop (>= 0.90.0)
6
6
 
7
7
  GEM
@@ -51,10 +51,7 @@ GEM
51
51
  rubocop (~> 1.51)
52
52
  ruby-progressbar (1.13.0)
53
53
  unicode-display_width (2.4.2)
54
- unparser (0.6.0)
55
- diff-lcs (~> 1.3)
56
- parser (>= 3.0.0)
57
- yard (0.9.25)
54
+ yard (0.9.36)
58
55
 
59
56
  PLATFORMS
60
57
  ruby
@@ -62,10 +59,9 @@ PLATFORMS
62
59
  DEPENDENCIES
63
60
  byebug
64
61
  rake (>= 12.3.3)
65
- rspec
62
+ rspec (~> 3.7)
66
63
  rubocop-shopify
67
64
  rubocop-sorbet!
68
- unparser (~> 0.6)
69
65
  yard (~> 0.9)
70
66
 
71
67
  BUNDLED WITH
data/README.md CHANGED
@@ -15,13 +15,6 @@ or, if you use `Bundler`, add this line your application's `Gemfile`:
15
15
  gem 'rubocop-sorbet', require: false
16
16
  ```
17
17
 
18
- Note: in order to use the [Sorbet/SignatureBuildOrder](https://github.com/Shopify/rubocop-sorbet/blob/main/manual/cops_sorbet.md#sorbetsignaturebuildorder) cop autocorrect feature, it is necessary
19
- to install `unparser` in addition to `rubocop-sorbet`.
20
-
21
- ```ruby
22
- gem "unparser", require: false
23
- ```
24
-
25
18
  ## Usage
26
19
 
27
20
  You need to tell RuboCop to load the Sorbet extension. There are three ways to do this:
data/config/default.yml CHANGED
@@ -188,12 +188,12 @@ Sorbet/BuggyObsoleteStrictMemoization:
188
188
  Checks for the a mistaken variant of the "obsolete memoization pattern" that used to be required
189
189
  for older Sorbet versions in `#typed: strict` files. The mistaken variant would overwrite the ivar with `nil`
190
190
  on every call, causing the memoized value to be discarded and recomputed on every call.
191
-
191
+
192
192
  This cop will correct it to read from the ivar instead of `nil`, which will memoize it correctly.
193
-
193
+
194
194
  The result of this correction will be the "obsolete memoization pattern", which can further be corrected by
195
195
  the `Sorbet/ObsoleteStrictMemoization` cop.
196
-
196
+
197
197
  See `Sorbet/ObsoleteStrictMemoization` for more details.
198
198
  Enabled: true
199
199
  VersionAdded: '0.7.3'
@@ -223,6 +223,20 @@ Sorbet/SignatureBuildOrder:
223
223
  then params, then return and finally the modifier
224
224
  such as: `abstract.params(...).returns(...).soft`.'
225
225
  Enabled: true
226
+ Order:
227
+ - final
228
+ - abstract
229
+ - implementation
230
+ - override
231
+ - overridable
232
+ - type_parameters
233
+ - params
234
+ - bind
235
+ - returns
236
+ - void
237
+ - soft
238
+ - checked
239
+ - on_failure
226
240
  VersionAdded: 0.3.0
227
241
 
228
242
  Sorbet/SingleLineRbiClassModuleDefinitions:
@@ -102,7 +102,7 @@ module RuboCop
102
102
  end
103
103
 
104
104
  class Property
105
- attr_reader :node, :kind, :name, :type, :default, :factory
105
+ attr_reader :node, :kind, :name, :default, :factory
106
106
 
107
107
  def initialize(node, kind, name, type, default:, factory:)
108
108
  @node = node
@@ -151,6 +151,11 @@ module RuboCop
151
151
  def nilable?
152
152
  type.start_with?("T.nilable(")
153
153
  end
154
+
155
+ def type
156
+ copy = @type.gsub(/[[:space:]]+/, "").strip # Remove newlines and spaces
157
+ copy.gsub(",", ", ") # Add a space after each comma
158
+ end
154
159
  end
155
160
 
156
161
  # @!method t_struct?(node)
@@ -215,8 +220,35 @@ module RuboCop
215
220
  sorted_props = props.sort_by { |prop| prop.default || prop.factory || prop.nilable? ? 1 : 0 }
216
221
 
217
222
  string = +"\n"
218
- string << "#{indent}sig { params(#{sorted_props.map(&:initialize_sig_param).join(", ")}).void }\n"
219
- string << "#{indent}def initialize(#{sorted_props.map(&:initialize_param).join(", ")})\n"
223
+
224
+ line = "#{indent}sig { params(#{sorted_props.map(&:initialize_sig_param).join(", ")}).void }\n"
225
+ if line.length <= max_line_length
226
+ string << line
227
+ else
228
+ string << "#{indent}sig do\n"
229
+ string << "#{indent} params(\n"
230
+ sorted_props.each do |prop|
231
+ string << "#{indent} #{prop.initialize_sig_param}"
232
+ string << "," if prop != sorted_props.last
233
+ string << "\n"
234
+ end
235
+ string << "#{indent} ).void\n"
236
+ string << "#{indent}end\n"
237
+ end
238
+
239
+ line = "#{indent}def initialize(#{sorted_props.map(&:initialize_param).join(", ")})\n"
240
+ if line.length <= max_line_length
241
+ string << line
242
+ else
243
+ string << "#{indent}def initialize(\n"
244
+ sorted_props.each do |prop|
245
+ string << "#{indent} #{prop.initialize_param}"
246
+ string << "," if prop != sorted_props.last
247
+ string << "\n"
248
+ end
249
+ string << "#{indent})\n"
250
+ end
251
+
220
252
  props.each do |prop|
221
253
  string << "#{indent} #{prop.initialize_assign}\n"
222
254
  end
@@ -1,20 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- begin
4
- require "unparser"
5
- rescue LoadError
6
- nil
7
- end
8
-
9
3
  module RuboCop
10
4
  module Cop
11
5
  module Sorbet
12
- # Checks for the correct order of sig builder methods:
13
- # - abstract, override, or overridable
14
- # - type_parameters
15
- # - params
16
- # - returns, or void
17
- # - soft, checked, or on_failure
6
+ # Checks for the correct order of `sig` builder methods.
7
+ #
8
+ # Options:
9
+ #
10
+ # * `Order`: The order in which to enforce the builder methods are called.
18
11
  #
19
12
  # @example
20
13
  # # bad
@@ -28,109 +21,80 @@ module RuboCop
28
21
  #
29
22
  # # good
30
23
  # sig { params(x: Integer).returns(Integer) }
31
- class SignatureBuildOrder < ::RuboCop::Cop::Cop # rubocop:todo InternalAffairs/InheritDeprecatedCopClass
24
+ class SignatureBuildOrder < ::RuboCop::Cop::Base
25
+ extend AutoCorrector
32
26
  include SignatureHelp
33
27
 
34
- ORDER =
35
- [
36
- :abstract,
37
- :override,
38
- :overridable,
39
- :type_parameters,
40
- :params,
41
- :returns,
42
- :void,
43
- :soft,
44
- :checked,
45
- :on_failure,
46
- ].each_with_index.to_h.freeze
47
-
48
28
  # @!method root_call(node)
49
29
  def_node_search(:root_call, <<~PATTERN)
50
- (send nil? {#{ORDER.keys.map(&:inspect).join(" ")}} ...)
30
+ (send nil? #builder? ...)
51
31
  PATTERN
52
32
 
53
33
  def on_signature(node)
54
- calls = call_chain(node.children[2]).map(&:method_name)
55
- return if calls.empty?
34
+ body = node.body
56
35
 
57
- # While the developer is typing, we may have an incomplete call statement, which means `ORDER[call]` will
58
- # return `nil`. In that case, invoking `sort_by` will raise
59
- return if calls.any? { |call| ORDER[call].nil? }
36
+ actual_calls_and_indexes = call_chain(body).map.with_index do |send_node, actual_index|
37
+ # The index this method call appears at in the configured Order.
38
+ expected_index = builder_method_indexes[send_node.method_name]
60
39
 
61
- expected_order = calls.sort_by { |call| ORDER[call] }
62
- return if expected_order == calls
63
-
64
- message = "Sig builders must be invoked in the following order: #{expected_order.join(", ")}."
65
-
66
- unless can_autocorrect?
67
- message += " For autocorrection, add the `unparser` gem to your project."
40
+ [send_node, actual_index, expected_index]
68
41
  end
69
42
 
70
- add_offense(
71
- node.children[2],
72
- message: message,
73
- )
74
- node
75
- end
43
+ # Temporarily extract unknown method calls
44
+ expected_calls_and_indexes, unknown_calls_and_indexes = actual_calls_and_indexes
45
+ .partition { |_, _, expected_index| expected_index }
76
46
 
77
- def autocorrect(node)
78
- return unless can_autocorrect?
47
+ # Sort known method calls by expected index
48
+ expected_calls_and_indexes.sort_by! { |_, _, expected_index| expected_index }
79
49
 
80
- lambda do |corrector|
81
- tree = call_chain(node_reparsed_with_modern_features(node))
82
- .sort_by { |call| ORDER[call.method_name] }
83
- .reduce(nil) do |receiver, caller|
84
- caller.updated(nil, [receiver] + caller.children.drop(1))
85
- end
50
+ # Re-insert unknown method calls in their positions
51
+ unknown_calls_and_indexes.each do |entry|
52
+ _, original_index, _ = entry
86
53
 
87
- corrector.replace(
88
- node,
89
- Unparser.unparse(tree),
90
- )
54
+ expected_calls_and_indexes.insert(original_index, entry)
91
55
  end
92
- end
93
56
 
94
- # Create a subclass of AST Builder that has modern features turned on
95
- class ModernBuilder < RuboCop::AST::Builder
96
- modernize
57
+ # Compare expected and actual ordering
58
+ expected_method_names = expected_calls_and_indexes.map { |send_node, _, _| send_node.method_name }
59
+ actual_method_names = actual_calls_and_indexes.map { |send_node, _, _| send_node.method_name }
60
+ return if expected_method_names == actual_method_names
61
+
62
+ add_offense(
63
+ body,
64
+ message: "Sig builders must be invoked in the following order: #{expected_method_names.join(", ")}.",
65
+ ) { |corrector| corrector.replace(body, expected_source(expected_calls_and_indexes)) }
97
66
  end
98
- private_constant :ModernBuilder
99
67
 
100
68
  private
101
69
 
102
- # This method exists to reparse the current node with modern features enabled.
103
- # Modern features include "index send" emitting, which is necessary to unparse
104
- # "index sends" (i.e. `[]` calls) back to index accessors (i.e. as `foo[bar]``).
105
- # Otherwise, we would get the unparsed node as `foo.[](bar)`.
106
- def node_reparsed_with_modern_features(node)
107
- # Create a new parser with a modern builder class instance
108
- parser = Parser::CurrentRuby.new(ModernBuilder.new)
109
- # Create a new source buffer with the node source
110
- buffer = Parser::Source::Buffer.new(processed_source.path, source: node.source)
111
- # Re-parse the buffer
112
- parser.parse(buffer)
113
- end
70
+ def expected_source(expected_calls_and_indexes)
71
+ expected_calls_and_indexes.reduce(nil) do |receiver_source, (send_node, _, _)|
72
+ send_source = if send_node.arguments?
73
+ "#{send_node.method_name}(#{send_node.arguments.map(&:source).join(", ")})"
74
+ else
75
+ send_node.method_name.to_s
76
+ end
114
77
 
115
- def can_autocorrect?
116
- defined?(::Unparser)
78
+ receiver_source ? "#{receiver_source}.#{send_source}" : send_source
79
+ end
117
80
  end
118
81
 
119
- def call_chain(sig_child_node)
120
- return [] if sig_child_node.nil?
121
-
122
- call_node = root_call(sig_child_node).first
123
- return [] unless call_node
82
+ # Split foo.bar.baz into [foo, foo.bar, foo.bar.baz]
83
+ def call_chain(node)
84
+ chain = []
124
85
 
125
- calls = []
126
- while call_node != sig_child_node
127
- calls << call_node
128
- call_node = call_node.parent
86
+ while node&.send_type?
87
+ chain << node
88
+ node = node.receiver
129
89
  end
130
90
 
131
- calls << sig_child_node
91
+ chain.reverse!
92
+
93
+ chain
94
+ end
132
95
 
133
- calls
96
+ def builder_method_indexes
97
+ @configured_order ||= cop_config.fetch("Order").map(&:to_sym).each_with_index.to_h.freeze
134
98
  end
135
99
  end
136
100
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  module Sorbet
5
- VERSION = "0.7.7"
5
+ VERSION = "0.8.0"
6
6
  end
7
7
  end
@@ -733,12 +733,11 @@ Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChan
733
733
  --- | --- | --- | --- | ---
734
734
  Enabled | Yes | Yes | 0.3.0 | -
735
735
 
736
- Checks for the correct order of sig builder methods:
737
- - abstract, override, or overridable
738
- - type_parameters
739
- - params
740
- - returns, or void
741
- - soft, checked, or on_failure
736
+ Checks for the correct order of `sig` builder methods.
737
+
738
+ Options:
739
+
740
+ * `Order`: The order in which to enforce the builder methods are called.
742
741
 
743
742
  ### Examples
744
743
 
@@ -756,6 +755,12 @@ sig { returns(Integer).params(x: Integer) }
756
755
  sig { params(x: Integer).returns(Integer) }
757
756
  ```
758
757
 
758
+ ### Configurable attributes
759
+
760
+ Name | Default value | Configurable values
761
+ --- | --- | ---
762
+ Order | `final`, `abstract`, `implementation`, `override`, `overridable`, `type_parameters`, `params`, `bind`, `returns`, `void`, `soft`, `checked`, `on_failure` | Array
763
+
759
764
  ## Sorbet/SingleLineRbiClassModuleDefinitions
760
765
 
761
766
  Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
@@ -25,8 +25,5 @@ Gem::Specification.new do |spec|
25
25
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
26
  spec.require_paths = ["lib"]
27
27
 
28
- spec.add_development_dependency("rspec", "~> 3.7")
29
- spec.add_development_dependency("unparser", "~> 0.6")
30
-
31
28
  spec.add_runtime_dependency("rubocop", ">= 0.90.0")
32
29
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-sorbet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.7
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ufuk Kayserilioglu
@@ -11,36 +11,8 @@ authors:
11
11
  autorequire:
12
12
  bindir: exe
13
13
  cert_chain: []
14
- date: 2024-02-09 00:00:00.000000000 Z
14
+ date: 2024-03-18 00:00:00.000000000 Z
15
15
  dependencies:
16
- - !ruby/object:Gem::Dependency
17
- name: rspec
18
- requirement: !ruby/object:Gem::Requirement
19
- requirements:
20
- - - "~>"
21
- - !ruby/object:Gem::Version
22
- version: '3.7'
23
- type: :development
24
- prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
- requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: '3.7'
30
- - !ruby/object:Gem::Dependency
31
- name: unparser
32
- requirement: !ruby/object:Gem::Requirement
33
- requirements:
34
- - - "~>"
35
- - !ruby/object:Gem::Version
36
- version: '0.6'
37
- type: :development
38
- prerelease: false
39
- version_requirements: !ruby/object:Gem::Requirement
40
- requirements:
41
- - - "~>"
42
- - !ruby/object:Gem::Version
43
- version: '0.6'
44
16
  - !ruby/object:Gem::Dependency
45
17
  name: rubocop
46
18
  requirement: !ruby/object:Gem::Requirement