Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Sorbet/Refinement #263

Merged
merged 1 commit into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
Sorbet/Refinement:
Description: >-
Checks for the use of Ruby Refinements library. Refinements add
complexity and incur a performance penalty that can be significant
for large code bases. They are also not supported by Sorbet.
Enabled: pending
VersionAdded: '<<next>>'

inherit_mode:
merge:
- Exclude
Expand Down
52 changes: 52 additions & 0 deletions lib/rubocop/cop/sorbet/refinement.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Sorbet
# Checks for the use of Ruby Refinements library. Refinements add
# complexity and incur a performance penalty that can be significant
# for large code bases. Good examples are cases of unrelated
# methods that happen to have the same name as these module methods.
#
# @example
# # bad
# module Foo
# refine(Date) do
# end
# end
#
# # bad
# module Foo
# using(Date) do
# end
# end
#
# # good
# module Foo
# bar.refine(Date)
# end
#
# # good
# module Foo
# bar.using(Date)
# end

class Refinement < Base
MSG = "Do not use Ruby Refinements library as it is not supported by Sorbet."
RESTRICT_ON_SEND = [:refine, :using].freeze

def on_send(node)
return unless node.receiver.nil?
return unless node.first_argument&.const_type?

if node.method?(:refine)
return unless node.block_node
return unless node.parent.parent.module_type?
end

add_offense(node)
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/rubocop/cop/sorbet_cops.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
require_relative "sorbet/forbid_t_unsafe"
require_relative "sorbet/forbid_t_untyped"
require_relative "sorbet/redundant_extend_t_sig"
require_relative "sorbet/refinement"
require_relative "sorbet/type_alias_name"
require_relative "sorbet/obsolete_strict_memoization"
require_relative "sorbet/buggy_obsolete_strict_memoization"
Expand Down
1 change: 1 addition & 0 deletions manual/cops.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ In the following section you find all available cops:
* [Sorbet/MultipleTEnumValues](cops_sorbet.md#sorbetmultipletenumvalues)
* [Sorbet/ObsoleteStrictMemoization](cops_sorbet.md#sorbetobsoletestrictmemoization)
* [Sorbet/RedundantExtendTSig](cops_sorbet.md#sorbetredundantextendtsig)
* [Sorbet/Refinement](cops_sorbet.md#sorbetrefinement)
* [Sorbet/SignatureBuildOrder](cops_sorbet.md#sorbetsignaturebuildorder)
* [Sorbet/SingleLineRbiClassModuleDefinitions](cops_sorbet.md#sorbetsinglelinerbiclassmoduledefinitions)
* [Sorbet/StrictSigil](cops_sorbet.md#sorbetstrictsigil)
Expand Down
37 changes: 37 additions & 0 deletions manual/cops_sorbet.md
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,43 @@ class Example
end
```

## Sorbet/Refinement

Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
--- | --- | --- | --- | ---
Enabled | Yes | No | <<next>> | -

Checks for the use of Ruby Refinements library. Refinements add
complexity and incur a performance penalty that can be significant
for large code bases. Good examples are cases of unrelated
methods that happen to have the same name as these module methods.

### Examples

```ruby
# bad
module Foo
refine(Date) do
end
end

# bad
module Foo
using(Date) do
end
end

# good
module Foo
bar.refine(Date)
end

# good
module Foo
bar.using(Date)
end
```

## Sorbet/SignatureBuildOrder

Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
Expand Down
63 changes: 63 additions & 0 deletions spec/rubocop/cop/sorbet/refinement_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# frozen_string_literal: true
corsonknowles marked this conversation as resolved.
Show resolved Hide resolved

RSpec.describe(RuboCop::Cop::Sorbet::Refinement, :config) do
it "reports an offense for use of using" do
expect_offense(<<~RUBY, "my_class.rb")
using MyRefinement
^^^^^^^^^^^^^^^^^^ Do not use Ruby Refinements library as it is not supported by Sorbet.
RUBY
end

it "reports an offense for use of refine" do
expect_offense(<<~RUBY, "my_refinement.rb")
module MyRefinement
refine(String) do
^^^^^^^^^^^^^^ Do not use Ruby Refinements library as it is not supported by Sorbet.
def to_s
"foo"
end
end
end
RUBY
end

it "reports no offense for use of using with non-const argument" do
expect_no_offenses(<<~RUBY, "my_class.rb")
using "foo"
RUBY
end

it "reports no offense for use of refine with non-const argument" do
expect_no_offenses(<<~RUBY, "my_refinement.rb")
module MyRefinement
refine "foo" do
def to_s
"foo"
end
end
end
RUBY
end

it "reports no offense for use of refine with no block argument" do
expect_no_offenses(<<~RUBY, "my_refinement.rb")
module MyRefinement
refine(String)
end
RUBY
end

it "reports no offense for use of refine outside of module" do
expect_no_offenses(<<~RUBY, "my_refinement.rb")
module MyNamespace
class MyClass
refine(String) do
def to_s
"foo"
end
end
end
end
RUBY
end
end
Loading