Skip to content

Commit

Permalink
Add new Sorbet/RedundantExtendTSig cop
Browse files Browse the repository at this point in the history
  • Loading branch information
sambostock committed Feb 3, 2023
1 parent 0d1d5c9 commit 0931663
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 0 deletions.
10 changes: 10 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,16 @@ Sorbet/OneAncestorPerLine:
Enabled: false
VersionAdded: '0.6.0'

Sorbet/ForbidExtendTSig:
Description: >-
Forbid the usage of redundant `extend T::Sig`.
Only for use in applications that monkey patch `Module.include(T::Sig)` directly,
where it is useful to reduce noise.
Enabled: false
Safe: false
VersionAdded: '<<next>>'

Sorbet/SignatureBuildOrder:
Description: >-
Enforces the order of parts in a signature.
Expand Down
50 changes: 50 additions & 0 deletions lib/rubocop/cop/sorbet/redundant_extend_t_sig.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Sorbet
# Forbids the use of redundant `extend T::Sig`. Only for use in
# applications that monkey patch `Module.include(T::Sig)` globally,
# which would make it redundant.
#
# @safety
# This cop should not be enabled in applications that have not monkey
# patched `Module`.
#
# @example
# # bad
# class Greeter
# extend T::Sig
# sig { void }
# def say_hello = puts "hello"
# end
#
# # good
# class Greeter
# sig { void }
# def say_hello = puts "hello"
# end
#
class RedundantExtendTSig < RuboCop::Cop::Cop
MSG = "Do not redundantly `extend T::Sig` when it is already included in all modules."
RESTRICT_ON_SEND = [:extend].freeze

def_node_matcher :extend_t_sig?, <<~PATTERN
(send _ :extend (const (const {nil? | cbase} :T) :Sig))
PATTERN

def on_send(node)
return unless extend_t_sig?(node)

add_offense(node)
end

def autocorrect(node)
lambda do |corrector|
corrector.remove(node)
end
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 @@ -8,6 +8,7 @@
require_relative "sorbet/callback_conditionals_binding"
require_relative "sorbet/forbid_t_unsafe"
require_relative "sorbet/forbid_t_untyped"
require_relative "sorbet/redundant_extend_t_sig"
require_relative "sorbet/type_alias_name"

require_relative "sorbet/rbi/forbid_extend_t_sig_helpers_in_shims"
Expand Down
1 change: 1 addition & 0 deletions manual/cops.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ In the following section you find all available cops:
* [Sorbet/IgnoreSigil](cops_sorbet.md#sorbetignoresigil)
* [Sorbet/KeywordArgumentOrdering](cops_sorbet.md#sorbetkeywordargumentordering)
* [Sorbet/OneAncestorPerLine](cops_sorbet.md#sorbetoneancestorperline)
* [Sorbet/RedundantExtendTSig](cops_sorbet.md#sorbetredundantextendtsig)
* [Sorbet/SignatureBuildOrder](cops_sorbet.md#sorbetsignaturebuildorder)
* [Sorbet/SignatureCop](cops_sorbet.md#sorbetsignaturecop)
* [Sorbet/SingleLineRbiClassModuleDefinitions](cops_sorbet.md#sorbetsinglelinerbiclassmoduledefinitions)
Expand Down
27 changes: 27 additions & 0 deletions manual/cops_sorbet.md
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,33 @@ module SomeModule
end
```
## Sorbet/RedundantExtendTSig
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
--- | --- | --- | --- | ---
Enabled | Yes | Yes | - | -
Forbids the use of redundant `extend T::Sig`. Meant for use in
applications that monkey patch `Module.include(T::Sig)` globally,
which would make it redundant.
### Examples
```ruby
# bad
class Greeter
extend T::Sig
sig { void }
def say_hello = puts "hello"
end

# good
class Greeter
sig { void }
def say_hello = puts "hello"
end
```
## Sorbet/SignatureBuildOrder
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
Expand Down
81 changes: 81 additions & 0 deletions spec/rubocop/cop/sorbet/redundant_extend_t_sig_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# frozen_string_literal: true

RSpec.describe(RuboCop::Cop::Sorbet::RedundantExtendTSig, :config) do
let(:config) { RuboCop::Config.new }
let(:message) { "Do not redundantly `extend T::Sig` when it is already included in all modules." }

shared_examples "block form" do |label, header| # rubocop:disable Lint/UnusedBlockArgument
it 'registers an offense when using `extend T::Sig` #{label}' do
expect_offense(<<~RUBY)
#{header}
extend T::Sig
^^^^^^^^^^^^^ #{message}
end
RUBY

expect_correction(<<~RUBY)
#{header}
#{trailing_whitespace}
end
RUBY
end

private

def trailing_whitespace
""
end
end

include_examples "block form", "in a module", "module M"
include_examples "block form", "in a class", "class C"
include_examples "block form", "in an anonymous module", "Module.new do"
include_examples "block form", "in an anonymous class", "Class.new do"
include_examples "block form", "in `self`'s singleton class", "class << self"
include_examples "block form", "in an arbitrary singleton class", "class << object"
include_examples "block form", "in a module with other contents", <<~RUBY.chomp
module M
extend SomethingElse
RUBY

it "registers an offense when using `extend T::Sig` on its own" do
expect_offense(<<~RUBY)
extend T::Sig
^^^^^^^^^^^^^ #{message}
RUBY

expect_correction(a_blank_line)
end

it "registers an offense when using `extend ::T::Sig` (fully qualified)" do
expect_offense(<<~RUBY)
extend ::T::Sig
^^^^^^^^^^^^^^^ #{message}
RUBY

expect_correction(a_blank_line)
end

it "registers an offense when using `extend T::Sig` with an explicit receiver" do
expect_offense(<<~RUBY)
some_module.extend T::Sig
^^^^^^^^^^^^^^^^^^^^^^^^^ #{message}
RUBY

expect_correction(a_blank_line)
end

it "does not register an offense when extending other modules in the T namespace" do
expect_no_offenses(<<~RUBY)
module M
extend T::Helpers
end
RUBY
end

private

def a_blank_line
"\n"
end
end

0 comments on commit 0931663

Please sign in to comment.