Skip to content

Commit

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

Sorbet/ForbidExtendTSig:
Description: 'Forbid the usage of redundant `extend T::Sig`. For use in applications that monkey patch it into `Module` 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
49 changes: 49 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,49 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Sorbet
# Forbids the use of redundant `extend T::Sig`. Meant for use in
# applications that monkey patch `Module` to `extend T::Sig` globally,
# which would make it redundant.
#
# @safety
# This cop cannot know if the host application has monkey patched
# `Module` to `extend T::Sig`, or if the offense is itself the
# monkey-patch.
#
# @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 < Base
extend AutoCorrector

MSG = "Do not `extend T::Sig` when it is already extended into 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) 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
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 `extend T::Sig` when it is already extended into 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", label: "in a module", header: "module M"
include_examples "block form", label: "in a class", header: "class C"
include_examples "block form", label: "in an anonymous module", header: "Module.new do"
include_examples "block form", label: "in an anonymous class", header: "Class.new do"
include_examples "block form", label: "in `self`'s singleton class", header: "class << self"
include_examples "block form", label: "in an arbitrary singleton class", header: "class << object"
include_examples "block form", label: "in a module with other contents", header: <<~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 2460e0a

Please sign in to comment.