Skip to content

Commit

Permalink
Extract SignatureBuildOrder Order config
Browse files Browse the repository at this point in the history
  • Loading branch information
sambostock committed Mar 18, 2024
1 parent 6b87977 commit 233cf9a
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 33 deletions.
11 changes: 11 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,17 @@ Sorbet/SignatureBuildOrder:
then params, then return and finally the modifier
such as: `abstract.params(...).returns(...).soft`.'
Enabled: true
Order:
- abstract
- override
- overridable
- type_parameters
- params
- returns
- void
- soft
- checked
- on_failure
VersionAdded: 0.3.0

Sorbet/SingleLineRbiClassModuleDefinitions:
Expand Down
49 changes: 22 additions & 27 deletions lib/rubocop/cop/sorbet/signatures/signature_build_order.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@
module RuboCop
module Cop
module Sorbet
# Checks for the correct order of sig builder methods:
# - abstract, override, or overridable
# - type_parameters
# - params
# - returns, or void
# - soft, checked, or on_failure
# Checks for the correct order of `sig` builder methods.
#
# Options:
#
# * `Order`: The order in which to enforce the builder methods are called.
#
# @example
# # bad
Expand All @@ -31,34 +30,22 @@ module Sorbet
class SignatureBuildOrder < ::RuboCop::Cop::Cop # rubocop:todo InternalAffairs/InheritDeprecatedCopClass
include SignatureHelp

ORDER =
[
:abstract,
:override,
:overridable,
:type_parameters,
:params,
:returns,
:void,
:soft,
:checked,
:on_failure,
].each_with_index.to_h.freeze

# @!method root_call(node)
def_node_search(:root_call, <<~PATTERN)
(send nil? {#{ORDER.keys.map(&:inspect).join(" ")}} ...)
(send nil? #builder? ...)
PATTERN

def on_signature(node)
calls = call_chain(node.children[2]).map(&:method_name)
return if calls.empty?

# While the developer is typing, we may have an incomplete call statement, which means `ORDER[call]` will
# return `nil`. In that case, invoking `sort_by` will raise
return if calls.any? { |call| ORDER[call].nil? }

expected_order = calls.sort_by { |call| ORDER[call] }
expected_order = calls.sort_by do |call|
builder_method_indexes.fetch(call) do
# Abort if we don't have a configured order for this call,
# likely because the method name is still being typed.
return nil
end
end
return if expected_order == calls

message = "Sig builders must be invoked in the following order: #{expected_order.join(", ")}."
Expand All @@ -79,7 +66,7 @@ def autocorrect(node)

lambda do |corrector|
tree = call_chain(node_reparsed_with_modern_features(node))
.sort_by { |call| ORDER[call.method_name] }
.sort_by { |call| builder_method_indexes[call.method_name] }
.reduce(nil) do |receiver, caller|
caller.updated(nil, [receiver] + caller.children.drop(1))
end
Expand Down Expand Up @@ -132,6 +119,14 @@ def call_chain(sig_child_node)

calls
end

def builder?(method_name)
builder_method_indexes.key?(method_name)
end

def builder_method_indexes
@configured_order ||= cop_config.fetch("Order").map(&:to_sym).each_with_index.to_h.freeze
end
end
end
end
Expand Down
17 changes: 11 additions & 6 deletions manual/cops_sorbet.md
Original file line number Diff line number Diff line change
Expand Up @@ -733,12 +733,11 @@ Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChan
--- | --- | --- | --- | ---
Enabled | Yes | Yes | 0.3.0 | -
Checks for the correct order of sig builder methods:
- abstract, override, or overridable
- type_parameters
- params
- returns, or void
- soft, checked, or on_failure
Checks for the correct order of `sig` builder methods.
Options:
* `Order`: The order in which to enforce the builder methods are called.
### Examples
Expand All @@ -756,6 +755,12 @@ sig { returns(Integer).params(x: Integer) }
sig { params(x: Integer).returns(Integer) }
```
### Configurable attributes
Name | Default value | Configurable values
--- | --- | ---
Order | `abstract`, `override`, `overridable`, `type_parameters`, `params`, `returns`, `void`, `soft`, `checked`, `on_failure` | Array
## Sorbet/SingleLineRbiClassModuleDefinitions
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
Expand Down
28 changes: 28 additions & 0 deletions spec/rubocop/cop/sorbet/signatures/signature_build_order_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,32 @@
Object.const_set(:Unparser, original_unparser)
end
end

describe("config") do
let :cop_config do
{
"Order" => [
"returns",
"override",
],
}
end

it("ignores chains including unknown methods") do
expect_no_offenses(<<~RUBY)
sig { override.params(x: Integer).returns(Integer) } # params not in Order
RUBY
end

it("allows customizing the order") do
expect_offense(<<~RUBY)
sig { override.returns(Integer) }
^^^^^^^^^^^^^^^^^^^^^^^^^ Sig builders must be invoked in the following order: returns, override.
RUBY

expect_correction(<<~RUBY)
sig { returns(Integer).override }
RUBY
end
end
end

0 comments on commit 233cf9a

Please sign in to comment.