Skip to content

Commit

Permalink
Merge pull request #263 from corsonknowles/main
Browse files Browse the repository at this point in the history
Add Sorbet/Refinement
  • Loading branch information
egiurleo authored Sep 25, 2024
2 parents f4ca5d4 + 79fc263 commit ba484cc
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 0 deletions.
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

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

0 comments on commit ba484cc

Please sign in to comment.