rubocop-sorbet 0.7.7 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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