Merge pull request #8781 from reitermarkus/rubocop-sorbet
Add and vendor `rubocop-sorbet` and `sorbet-runtime-stub`.
This commit is contained in:
commit
fb9aafbb08
3
.github/workflows/tests.yml
vendored
3
.github/workflows/tests.yml
vendored
@ -120,8 +120,7 @@ jobs:
|
||||
- name: Run brew audit --skip-style on all taps
|
||||
run: brew audit --skip-style
|
||||
|
||||
# TODO: remove --quiet when possible.
|
||||
- run: brew typecheck --quiet
|
||||
- run: brew typecheck
|
||||
|
||||
- name: Run vale for docs linting
|
||||
run: |
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -35,6 +35,7 @@
|
||||
!**/vendor/bundle/ruby/*/gems/*/lib
|
||||
!**/vendor/bundle/ruby/*/gems/rubocop-performance-*/config
|
||||
!**/vendor/bundle/ruby/*/gems/rubocop-rspec-*/config
|
||||
!**/vendor/bundle/ruby/*/gems/rubocop-sorbet-*/config
|
||||
|
||||
# Ignore partially included gems where we don't need all files
|
||||
**/vendor/bundle/ruby/*/gems/activesupport-*/lib/active_support.rb
|
||||
@ -134,6 +135,7 @@
|
||||
**/vendor/bundle/ruby/*/gems/simplecov-html-*/
|
||||
**/vendor/bundle/ruby/*/gems/sorbet-*/
|
||||
**/vendor/bundle/ruby/*/gems/sorbet-runtime-*/
|
||||
!**/vendor/bundle/ruby/*/gems/sorbet-runtime-stub-*/
|
||||
**/vendor/bundle/ruby/*/gems/spoom-*/
|
||||
**/vendor/bundle/ruby/*/gems/stackprof-*/
|
||||
**/vendor/bundle/ruby/*/gems/strscan-*/
|
||||
|
||||
@ -264,6 +264,14 @@ Layout/LineLength:
|
||||
' "~/Library/Application Support/', '"~/Library/Caches/', '"~/Application Support',
|
||||
' was verified as official when first introduced to the cask']
|
||||
|
||||
# Enable once we are using `sorbet-runtime`.
|
||||
Sorbet/FalseSigil:
|
||||
Enabled: false
|
||||
|
||||
# Try getting rid of these.
|
||||
Sorbet/ConstantsFromStrings:
|
||||
Enabled: false
|
||||
|
||||
# Avoid false positives on modifiers used on symbols of methods
|
||||
# See https://github.com/rubocop-hq/rubocop/issues/5953
|
||||
Style/AccessModifierDeclarations:
|
||||
|
||||
@ -25,4 +25,6 @@ gem "patchelf"
|
||||
gem "plist"
|
||||
gem "rubocop-performance"
|
||||
gem "rubocop-rspec"
|
||||
gem "rubocop-sorbet"
|
||||
gem "ruby-macho"
|
||||
gem "sorbet-runtime-stub"
|
||||
|
||||
@ -114,6 +114,8 @@ GEM
|
||||
rubocop-ast (>= 0.4.0)
|
||||
rubocop-rspec (1.43.2)
|
||||
rubocop (~> 0.87)
|
||||
rubocop-sorbet (0.5.1)
|
||||
rubocop
|
||||
ruby-macho (2.2.0)
|
||||
ruby-progressbar (1.10.1)
|
||||
simplecov (0.19.0)
|
||||
@ -123,6 +125,7 @@ GEM
|
||||
sorbet (0.5.5942)
|
||||
sorbet-static (= 0.5.5942)
|
||||
sorbet-runtime (0.5.5942)
|
||||
sorbet-runtime-stub (0.2.0)
|
||||
sorbet-static (0.5.5942-universal-darwin-14)
|
||||
spoom (1.0.4)
|
||||
colorize
|
||||
@ -167,10 +170,12 @@ DEPENDENCIES
|
||||
rubocop
|
||||
rubocop-performance
|
||||
rubocop-rspec
|
||||
rubocop-sorbet
|
||||
ruby-macho
|
||||
simplecov
|
||||
sorbet
|
||||
sorbet-runtime
|
||||
sorbet-runtime-stub
|
||||
tapioca
|
||||
|
||||
BUNDLED WITH
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "delegate"
|
||||
|
||||
require "extend/hash_validator"
|
||||
using HashValidator
|
||||
|
||||
@ -8,7 +10,7 @@ module Cask
|
||||
# Class corresponding to the `conflicts_with` stanza.
|
||||
#
|
||||
# @api private
|
||||
class ConflictsWith < DelegateClass(Hash)
|
||||
class ConflictsWith < SimpleDelegator
|
||||
VALID_KEYS = [
|
||||
:formula,
|
||||
:cask,
|
||||
@ -18,12 +20,13 @@ module Cask
|
||||
:java,
|
||||
].freeze
|
||||
|
||||
def initialize(**pairs)
|
||||
pairs.assert_valid_keys!(*VALID_KEYS)
|
||||
def initialize(**options)
|
||||
options.assert_valid_keys!(*VALID_KEYS)
|
||||
|
||||
super(pairs.transform_values { |v| Set.new(Array(v)) })
|
||||
conflicts = options.transform_values { |v| Set.new(Array(v)) }
|
||||
conflicts.default = Set.new
|
||||
|
||||
self.default = Set.new
|
||||
super(conflicts)
|
||||
end
|
||||
|
||||
def to_json(generator)
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "delegate"
|
||||
|
||||
require "requirements/macos_requirement"
|
||||
|
||||
module Cask
|
||||
@ -7,7 +9,7 @@ module Cask
|
||||
# Class corresponding to the `depends_on` stanza.
|
||||
#
|
||||
# @api private
|
||||
class DependsOn < DelegateClass(Hash)
|
||||
class DependsOn < SimpleDelegator
|
||||
VALID_KEYS = Set.new([
|
||||
:formula,
|
||||
:cask,
|
||||
|
||||
@ -1,32 +1,63 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Class corresponding to the `url` stanza.
|
||||
#
|
||||
# @api private
|
||||
class URL
|
||||
ATTRIBUTES = [
|
||||
:using,
|
||||
:tag, :branch, :revisions, :revision,
|
||||
:trust_cert, :cookies, :referer, :user_agent,
|
||||
:data
|
||||
].freeze
|
||||
private_constant :ATTRIBUTES
|
||||
extend T::Sig
|
||||
|
||||
attr_reader :uri, :specs, *ATTRIBUTES
|
||||
attr_reader :uri, :specs,
|
||||
:using,
|
||||
:tag, :branch, :revisions, :revision,
|
||||
:trust_cert, :cookies, :referer, :user_agent,
|
||||
:data
|
||||
|
||||
extend Forwardable
|
||||
def_delegators :uri, :path, :scheme, :to_s
|
||||
|
||||
def initialize(uri, **options)
|
||||
@uri = URI(uri)
|
||||
@user_agent = :default
|
||||
sig do
|
||||
params(
|
||||
uri: T.any(URI::Generic, String),
|
||||
using: T.nilable(Symbol),
|
||||
tag: T.nilable(String),
|
||||
branch: T.nilable(String),
|
||||
revisions: T.nilable(T::Array[String]),
|
||||
revision: T.nilable(String),
|
||||
trust_cert: T.nilable(T::Boolean),
|
||||
cookies: T.nilable(T::Hash[String, String]),
|
||||
referer: T.nilable(T.any(URI::Generic, String)),
|
||||
user_agent: T.nilable(T.any(Symbol, String)),
|
||||
data: T.nilable(T::Hash[String, String]),
|
||||
).returns(T.untyped)
|
||||
end
|
||||
def initialize(
|
||||
uri,
|
||||
using: nil,
|
||||
tag: nil,
|
||||
branch: nil,
|
||||
revisions: nil,
|
||||
revision: nil,
|
||||
trust_cert: nil,
|
||||
cookies: nil,
|
||||
referer: nil,
|
||||
user_agent: nil,
|
||||
data: nil
|
||||
)
|
||||
@uri = URI(uri)
|
||||
|
||||
ATTRIBUTES.each do |attribute|
|
||||
next unless options.key?(attribute)
|
||||
specs = {}
|
||||
specs[:using] = @using = using
|
||||
specs[:tag] = @tag = tag
|
||||
specs[:branch] = @branch = branch
|
||||
specs[:revisions] = @revisions = revisions
|
||||
specs[:revision] = @revision = revision
|
||||
specs[:trust_cert] = @trust_cert = trust_cert
|
||||
specs[:cookies] = @cookies = cookies
|
||||
specs[:referer] = @referer = referer
|
||||
specs[:user_agent] = @user_agent = user_agent || :default
|
||||
specs[:data] = @data = data
|
||||
|
||||
instance_variable_set("@#{attribute}", options[attribute])
|
||||
end
|
||||
|
||||
@specs = options
|
||||
@specs = specs.compact
|
||||
end
|
||||
end
|
||||
|
||||
@ -27,6 +27,7 @@ module Commands
|
||||
"environment" => "--env",
|
||||
"--config" => "config",
|
||||
"-v" => "--version",
|
||||
"tc" => "typecheck",
|
||||
}.freeze
|
||||
|
||||
def valid_internal_cmd?(cmd)
|
||||
|
||||
@ -4,34 +4,30 @@ require "irb"
|
||||
|
||||
# @private
|
||||
module IRB
|
||||
@setup_done = false
|
||||
def self.parse_opts(argv: nil); end
|
||||
|
||||
extend Module.new {
|
||||
def parse_opts; end
|
||||
|
||||
def start_within(binding)
|
||||
unless @setup_done
|
||||
setup(nil, argv: [])
|
||||
@setup_done = true
|
||||
end
|
||||
|
||||
workspace = WorkSpace.new(binding)
|
||||
irb = Irb.new(workspace)
|
||||
|
||||
@CONF[:IRB_RC]&.call(irb.context)
|
||||
@CONF[:MAIN_CONTEXT] = irb.context
|
||||
|
||||
trap("SIGINT") do
|
||||
irb.signal_handle
|
||||
end
|
||||
|
||||
begin
|
||||
catch(:IRB_EXIT) do
|
||||
irb.eval_input
|
||||
end
|
||||
ensure
|
||||
irb_at_exit
|
||||
end
|
||||
def self.start_within(binding)
|
||||
unless @setup_done
|
||||
setup(nil, argv: [])
|
||||
@setup_done = true
|
||||
end
|
||||
}
|
||||
|
||||
workspace = WorkSpace.new(binding)
|
||||
irb = Irb.new(workspace)
|
||||
|
||||
@CONF[:IRB_RC]&.call(irb.context)
|
||||
@CONF[:MAIN_CONTEXT] = irb.context
|
||||
|
||||
trap("SIGINT") do
|
||||
irb.signal_handle
|
||||
end
|
||||
|
||||
begin
|
||||
catch(:IRB_EXIT) do
|
||||
irb.eval_input
|
||||
end
|
||||
ensure
|
||||
irb_at_exit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -6,7 +6,7 @@ require "cask_dependent"
|
||||
# A collection of dependencies.
|
||||
#
|
||||
# @api private
|
||||
class Dependencies < DelegateClass(Array)
|
||||
class Dependencies < SimpleDelegator
|
||||
def initialize(*args)
|
||||
super(args)
|
||||
end
|
||||
@ -41,7 +41,7 @@ end
|
||||
# A collection of requirements.
|
||||
#
|
||||
# @api private
|
||||
class Requirements < DelegateClass(Set)
|
||||
class Requirements < SimpleDelegator
|
||||
def initialize(*args)
|
||||
super(Set.new(args))
|
||||
end
|
||||
|
||||
@ -58,6 +58,7 @@ module Homebrew
|
||||
ENV["HOMEBREW_NO_COMPAT"] = "1" if args.no_compat?
|
||||
ENV["HOMEBREW_TEST_GENERIC_OS"] = "1" if args.generic?
|
||||
ENV["HOMEBREW_TEST_ONLINE"] = "1" if args.online?
|
||||
ENV["HOMEBREW_SORBET_RUNTIME"] = "1"
|
||||
|
||||
ENV["USER"] ||= system_command!("id", args: ["-nu"]).stdout.chomp
|
||||
|
||||
|
||||
@ -251,6 +251,10 @@ module Homebrew
|
||||
"of macOS. This is useful in development on new macOS versions.",
|
||||
boolean: true,
|
||||
},
|
||||
HOMEBREW_SORBET_RUNTIME: {
|
||||
description: "Enable runtime typechecking using Sorbet.",
|
||||
boolean: true,
|
||||
},
|
||||
HOMEBREW_SVN: {
|
||||
description: "Use this as the `svn`(1) binary.",
|
||||
default_text: "A Homebrew-built Subversion (if installed), or the system-provided binary.",
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
module UnpackStrategy
|
||||
class Zip
|
||||
prepend Module.new {
|
||||
module MacOSZipExtension
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
if merge_xattrs && contains_extended_attributes?(path)
|
||||
# We use ditto directly, because dot_clean has issues if the __MACOSX
|
||||
@ -46,6 +46,9 @@ module UnpackStrategy
|
||||
end
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
private_constant :MacOSZipExtension
|
||||
|
||||
prepend MacOSZipExtension
|
||||
end
|
||||
end
|
||||
|
||||
@ -31,6 +31,8 @@ ActiveSupport::Inflector.inflections(:en) do |inflect|
|
||||
inflect.irregular "it", "they"
|
||||
end
|
||||
|
||||
require "utils/sorbet"
|
||||
|
||||
HOMEBREW_BOTTLE_DEFAULT_DOMAIN = ENV["HOMEBREW_BOTTLE_DEFAULT_DOMAIN"]
|
||||
HOMEBREW_BREW_DEFAULT_GIT_REMOTE = ENV["HOMEBREW_BREW_DEFAULT_GIT_REMOTE"]
|
||||
HOMEBREW_CORE_DEFAULT_GIT_REMOTE = ENV["HOMEBREW_CORE_DEFAULT_GIT_REMOTE"]
|
||||
|
||||
@ -4,6 +4,8 @@ require_relative "load_path"
|
||||
|
||||
require "rubocop-performance"
|
||||
require "rubocop-rspec"
|
||||
require "rubocop-sorbet"
|
||||
|
||||
require "rubocops/formula_desc"
|
||||
require "rubocops/components_order"
|
||||
require "rubocops/components_redundancy"
|
||||
|
||||
@ -3,3 +3,6 @@
|
||||
|
||||
--ignore
|
||||
/vendor
|
||||
|
||||
--ignore
|
||||
/test/.gem
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
# typed: strict
|
||||
|
||||
class User < String
|
||||
def gui?
|
||||
end
|
||||
class User < SimpleDelegator
|
||||
include Kernel
|
||||
|
||||
def self.current
|
||||
end
|
||||
sig { returns(T::Boolean) }
|
||||
def gui?; end
|
||||
|
||||
sig { returns(T.nilable(T.attached_class)) }
|
||||
def self.current; end
|
||||
end
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# frozen_string_literal: true
|
||||
# typed: false
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Add your extra requires here
|
||||
|
||||
10
Library/Homebrew/utils/sorbet.rb
Normal file
10
Library/Homebrew/utils/sorbet.rb
Normal file
@ -0,0 +1,10 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
if ENV["HOMEBREW_SORBET_RUNTIME"]
|
||||
require "utils/gems"
|
||||
Homebrew.install_bundler_gems!
|
||||
require "sorbet-runtime"
|
||||
else
|
||||
require "sorbet-runtime-stub"
|
||||
end
|
||||
@ -8,7 +8,7 @@ require "system_command"
|
||||
# A system user.
|
||||
#
|
||||
# @api private
|
||||
class User < DelegateClass(String)
|
||||
class User < SimpleDelegator
|
||||
# Return whether the user has an active GUI session.
|
||||
def gui?
|
||||
out, _, status = system_command "who"
|
||||
|
||||
@ -75,9 +75,11 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/unicode-display_width
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-0.92.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-performance-1.8.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rspec-1.43.2/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-sorbet-0.5.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ruby-macho-2.2.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-static-0.5.5942-universal-darwin-19/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-0.5.5942/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-runtime-stub-0.2.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/thor-1.0.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/spoom-1.0.4/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/tapioca-0.4.7/lib"
|
||||
|
||||
128
Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/config/default.yml
vendored
Normal file
128
Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/config/default.yml
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
inherit_mode:
|
||||
merge:
|
||||
- Exclude
|
||||
|
||||
Sorbet/AllowIncompatibleOverride:
|
||||
Description: 'Disallows using `.override(allow_incompatible: true)`.'
|
||||
Enabled: true
|
||||
VersionAdded: 0.2.0
|
||||
|
||||
Sorbet/BindingConstantWithoutTypeAlias:
|
||||
Description: >-
|
||||
Disallows binding the return value of `T.any`, `T.all`, `T.enum`
|
||||
to a constant directly. To bind the value, one must use `T.type_alias`.
|
||||
Enabled: true
|
||||
VersionAdded: 0.2.0
|
||||
|
||||
Sorbet/CheckedTrueInSignature:
|
||||
Description: 'Disallows the usage of `checked(true)` in signatures.'
|
||||
Enabled: true
|
||||
VersionAdded: 0.2.0
|
||||
|
||||
Sorbet/ConstantsFromStrings:
|
||||
Description: >-
|
||||
Forbids constant access through meta-programming.
|
||||
|
||||
For example, things like `constantize` or `const_get`
|
||||
are forbidden.
|
||||
Enabled: true
|
||||
VersionAdded: 0.2.0
|
||||
|
||||
Sorbet/EnforceSigilOrder:
|
||||
Description: 'Ensures that Sorbet sigil comes first in a file.'
|
||||
Enabled: true
|
||||
VersionAdded: 0.3.4
|
||||
|
||||
Sorbet/EnforceSignatures:
|
||||
Description: 'Ensures all methods have a valid signature.'
|
||||
Enabled: false
|
||||
VersionAdded: 0.3.4
|
||||
|
||||
Sorbet/FalseSigil:
|
||||
Description: 'All files must be at least at strictness `false`.'
|
||||
Enabled: true
|
||||
VersionAdded: 0.3.3
|
||||
SuggestedStrictness: true
|
||||
Include:
|
||||
- "**/*.rb"
|
||||
- "**/*.rbi"
|
||||
- "**/*.rake"
|
||||
- "**/*.ru"
|
||||
Exclude:
|
||||
- bin/**/*
|
||||
- db/**/*.rb
|
||||
- script/**/*
|
||||
|
||||
Sorbet/ForbidIncludeConstLiteral:
|
||||
Description: 'Forbids include of non-literal constants.'
|
||||
Enabled: false
|
||||
VersionAdded: 0.2.0
|
||||
VersionChanged: 0.5.0
|
||||
|
||||
Sorbet/ForbidSuperclassConstLiteral:
|
||||
Description: 'Forbid superclasses which are non-literal constants.'
|
||||
Enabled: false
|
||||
VersionAdded: 0.2.0
|
||||
VersionChanged: 0.5.0
|
||||
|
||||
Sorbet/ForbidUntypedStructProps:
|
||||
Description: >-
|
||||
Disallows use of `T.untyped` or `T.nilable(T.untyped)` as a
|
||||
prop type for `T::Struct` subclasses.
|
||||
Enabled: true
|
||||
VersionAdded: 0.4.0
|
||||
|
||||
Sorbet/HasSigil:
|
||||
Description: 'Makes the Sorbet typed sigil mandatory in all files.'
|
||||
Enabled: false
|
||||
VersionAdded: 0.3.3
|
||||
|
||||
Sorbet/IgnoreSigil:
|
||||
Description: 'All files must be at least at strictness `ignore`.'
|
||||
Enabled: false
|
||||
VersionAdded: 0.3.3
|
||||
|
||||
Sorbet/KeywordArgumentOrdering:
|
||||
Description: >-
|
||||
Enforces a compatible keyword arguments with Sorbet.
|
||||
|
||||
All keyword arguments must be at the end of the parameters
|
||||
list, and all keyword arguments with a default value must be
|
||||
after those without default values.
|
||||
Enabled: true
|
||||
VersionAdded: 0.2.0
|
||||
|
||||
Sorbet/ParametersOrderingInSignature:
|
||||
Description: 'Enforces same parameter order between a method and its signature.'
|
||||
Enabled: true
|
||||
VersionAdded: 0.2.0
|
||||
|
||||
Sorbet/SignatureBuildOrder:
|
||||
Description: >-
|
||||
Enforces the order of parts in a signature.
|
||||
|
||||
The order is first inheritance related builders,
|
||||
then params, then return and finally the modifier
|
||||
such as: `abstract.params(...).returns(...).soft`.'
|
||||
Enabled: true
|
||||
VersionAdded: 0.3.0
|
||||
|
||||
Sorbet/StrictSigil:
|
||||
Description: 'All files must be at least at strictness `strict`.'
|
||||
Enabled: false
|
||||
VersionAdded: 0.3.3
|
||||
|
||||
Sorbet/StrongSigil:
|
||||
Description: 'All files must be at least at strictness `strong`.'
|
||||
Enabled: false
|
||||
VersionAdded: 0.3.3
|
||||
|
||||
Sorbet/TrueSigil:
|
||||
Description: 'All files must be at least at strictness `true`.'
|
||||
Enabled: false
|
||||
VersionAdded: 0.3.3
|
||||
|
||||
Sorbet/ValidSigil:
|
||||
Description: 'All files must have a valid sigil.'
|
||||
Enabled: true
|
||||
VersionAdded: 0.3.3
|
||||
11
Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop-sorbet.rb
vendored
Normal file
11
Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop-sorbet.rb
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop'
|
||||
|
||||
require_relative 'rubocop/sorbet'
|
||||
require_relative 'rubocop/sorbet/version'
|
||||
require_relative 'rubocop/sorbet/inject'
|
||||
|
||||
RuboCop::Sorbet::Inject.defaults!
|
||||
|
||||
require_relative 'rubocop/cop/sorbet_cops'
|
||||
@ -0,0 +1,121 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop'
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Sorbet
|
||||
# This cop disallows binding the return value of `T.any`, `T.all`, `T.enum`
|
||||
# to a constant directly. To bind the value, one must use `T.type_alias`.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# FooOrBar = T.any(Foo, Bar)
|
||||
#
|
||||
# # good
|
||||
# FooOrBar = T.type_alias { T.any(Foo, Bar) }
|
||||
class BindingConstantWithoutTypeAlias < RuboCop::Cop::Cop
|
||||
def_node_matcher(:binding_unaliased_type?, <<-PATTERN)
|
||||
(casgn _ _ [#not_nil? #not_t_let? #not_dynamic_type_creation_with_block? #not_generic_parameter_decl? #method_needing_aliasing_on_t?])
|
||||
PATTERN
|
||||
|
||||
def_node_matcher(:using_type_alias?, <<-PATTERN)
|
||||
(block
|
||||
(send
|
||||
(const nil? :T) :type_alias)
|
||||
_
|
||||
_
|
||||
)
|
||||
PATTERN
|
||||
|
||||
def_node_matcher(:using_deprecated_type_alias_syntax?, <<-PATTERN)
|
||||
(
|
||||
send
|
||||
(const nil? :T)
|
||||
:type_alias
|
||||
_
|
||||
)
|
||||
PATTERN
|
||||
|
||||
def_node_matcher(:t_let?, <<-PATTERN)
|
||||
(
|
||||
send
|
||||
(const nil? :T)
|
||||
:let
|
||||
_
|
||||
_
|
||||
)
|
||||
PATTERN
|
||||
|
||||
def_node_matcher(:dynamic_type_creation_with_block?, <<-PATTERN)
|
||||
(block
|
||||
(send
|
||||
const :new ...)
|
||||
_
|
||||
_
|
||||
)
|
||||
PATTERN
|
||||
|
||||
def_node_matcher(:generic_parameter_decl?, <<-PATTERN)
|
||||
(
|
||||
send nil? {:type_template :type_member} ...
|
||||
)
|
||||
PATTERN
|
||||
|
||||
def_node_search(:method_needing_aliasing_on_t?, <<-PATTERN)
|
||||
(
|
||||
send
|
||||
(const nil? :T)
|
||||
{:any :all :noreturn :class_of :untyped :nilable :self_type :enum :proc}
|
||||
...
|
||||
)
|
||||
PATTERN
|
||||
|
||||
def not_t_let?(node)
|
||||
!t_let?(node)
|
||||
end
|
||||
|
||||
def not_dynamic_type_creation_with_block?(node)
|
||||
!dynamic_type_creation_with_block?(node)
|
||||
end
|
||||
|
||||
def not_generic_parameter_decl?(node)
|
||||
!generic_parameter_decl?(node)
|
||||
end
|
||||
|
||||
def not_nil?(node)
|
||||
!node.nil?
|
||||
end
|
||||
|
||||
def on_casgn(node)
|
||||
return unless binding_unaliased_type?(node) && !using_type_alias?(node.children[2])
|
||||
if using_deprecated_type_alias_syntax?(node.children[2])
|
||||
add_offense(
|
||||
node.children[2],
|
||||
message: "It looks like you're using the old `T.type_alias` syntax. " \
|
||||
'`T.type_alias` now expects a block.' \
|
||||
'Run Sorbet with the options "--autocorrect --error-white-list=5043" ' \
|
||||
'to automatically upgrade to the new syntax.'
|
||||
)
|
||||
return
|
||||
end
|
||||
add_offense(
|
||||
node.children[2],
|
||||
message: "It looks like you're trying to bind a type to a constant. " \
|
||||
'To do this, you must alias the type using `T.type_alias`.'
|
||||
)
|
||||
end
|
||||
|
||||
def autocorrect(node)
|
||||
lambda do |corrector|
|
||||
corrector.replace(
|
||||
node.source_range,
|
||||
"T.type_alias { #{node.source} }"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,53 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop'
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Sorbet
|
||||
# This cop disallows the calls that are used to get constants fom Strings
|
||||
# such as +constantize+, +const_get+, and +constants+.
|
||||
#
|
||||
# The goal of this cop is to make the code easier to statically analyze,
|
||||
# more IDE-friendly, and more predictable. It leads to code that clearly
|
||||
# expresses which values the constant can have.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# class_name.constantize
|
||||
#
|
||||
# # bad
|
||||
# constants.detect { |c| c.name == "User" }
|
||||
#
|
||||
# # bad
|
||||
# const_get(class_name)
|
||||
#
|
||||
# # good
|
||||
# case class_name
|
||||
# when "User"
|
||||
# User
|
||||
# else
|
||||
# raise ArgumentError
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# { "User" => User }.fetch(class_name)
|
||||
class ConstantsFromStrings < ::RuboCop::Cop::Cop
|
||||
def_node_matcher(:constant_from_string?, <<-PATTERN)
|
||||
(send _ {:constantize :constants :const_get} ...)
|
||||
PATTERN
|
||||
|
||||
def on_send(node)
|
||||
return unless constant_from_string?(node)
|
||||
add_offense(
|
||||
node,
|
||||
location: :selector,
|
||||
message: "Don't use `#{node.method_name}`, it makes the code harder to understand, less editor-friendly, " \
|
||||
"and impossible to analyze. Replace `#{node.method_name}` with a case/when or a hash."
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,58 @@
|
||||
# encoding: utf-8
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop'
|
||||
|
||||
# Correct `send` expressions in include statements by constant literals.
|
||||
#
|
||||
# Sorbet, the static checker, is not (yet) able to support constructs on the
|
||||
# following form:
|
||||
#
|
||||
# ```ruby
|
||||
# class MyClass
|
||||
# include send_expr
|
||||
# end
|
||||
# ```
|
||||
#
|
||||
# Multiple occurences of this can be found in Shopify's code base like:
|
||||
#
|
||||
# ```ruby
|
||||
# include Rails.application.routes.url_helpers
|
||||
# ```
|
||||
# or
|
||||
# ```ruby
|
||||
# include Polaris::Engine.helpers
|
||||
# ```
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Sorbet
|
||||
class ForbidIncludeConstLiteral < RuboCop::Cop::Cop
|
||||
MSG = 'Includes must only contain constant literals'
|
||||
|
||||
attr_accessor :used_names
|
||||
|
||||
def_node_matcher :not_lit_const_include?, <<-PATTERN
|
||||
(send nil? {:include :extend :prepend}
|
||||
$_
|
||||
)
|
||||
PATTERN
|
||||
|
||||
def initialize(*)
|
||||
super
|
||||
self.used_names = Set.new
|
||||
end
|
||||
|
||||
def on_send(node)
|
||||
return unless not_lit_const_include?(node) do |send_argument|
|
||||
![:const, :self].include?(send_argument.type)
|
||||
end
|
||||
parent = node.parent
|
||||
return unless parent
|
||||
parent = parent.parent if [:begin, :block].include?(parent.type)
|
||||
return unless [:module, :class, :sclass].include?(parent.type)
|
||||
add_offense(node)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,45 @@
|
||||
# encoding: utf-8
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop'
|
||||
|
||||
# Correct superclass `send` expressions by constant literals.
|
||||
#
|
||||
# Sorbet, the static checker, is not (yet) able to support constructs on the
|
||||
# following form:
|
||||
#
|
||||
# ```ruby
|
||||
# class Foo < send_expr; end
|
||||
# ```
|
||||
#
|
||||
# Multiple occurences of this can be found in Shopify's code base like:
|
||||
#
|
||||
# ```ruby
|
||||
# class ShopScope < Component::TrustedIdScope[ShopIdentity::ShopId]
|
||||
# ```
|
||||
# or
|
||||
# ```ruby
|
||||
# class ApiClientEligibility < Struct.new(:api_client, :match_results, :shop)
|
||||
# ```
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Sorbet
|
||||
class ForbidSuperclassConstLiteral < RuboCop::Cop::Cop
|
||||
MSG = 'Superclasses must only contain constant literals'
|
||||
|
||||
def_node_matcher :not_lit_const_superclass?, <<-PATTERN
|
||||
(class
|
||||
(const ...)
|
||||
(send ...)
|
||||
...
|
||||
)
|
||||
PATTERN
|
||||
|
||||
def on_class(node)
|
||||
return unless not_lit_const_superclass?(node)
|
||||
add_offense(node.child_nodes[1])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,58 @@
|
||||
# encoding: utf-8
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop'
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Sorbet
|
||||
# This cop disallows use of `T.untyped` or `T.nilable(T.untyped)`
|
||||
# as a prop type for `T::Struct`.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# class SomeClass
|
||||
# const :foo, T.untyped
|
||||
# prop :bar, T.nilable(T.untyped)
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# class SomeClass
|
||||
# const :foo, Integer
|
||||
# prop :bar, T.nilable(String)
|
||||
# end
|
||||
class ForbidUntypedStructProps < RuboCop::Cop::Cop
|
||||
MSG = 'Struct props cannot be T.untyped'
|
||||
|
||||
def_node_matcher :t_struct, <<~PATTERN
|
||||
(const (const nil? :T) :Struct)
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :t_untyped, <<~PATTERN
|
||||
(send (const nil? :T) :untyped)
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :t_nilable_untyped, <<~PATTERN
|
||||
(send (const nil? :T) :nilable {#t_untyped #t_nilable_untyped})
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :subclass_of_t_struct?, <<~PATTERN
|
||||
(class (const ...) #t_struct ...)
|
||||
PATTERN
|
||||
|
||||
def_node_search :untyped_props, <<~PATTERN
|
||||
(send nil? {:prop :const} _ {#t_untyped #t_nilable_untyped} ...)
|
||||
PATTERN
|
||||
|
||||
def on_class(node)
|
||||
return unless subclass_of_t_struct?(node)
|
||||
|
||||
untyped_props(node).each do |untyped_prop|
|
||||
add_offense(untyped_prop.child_nodes[1])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,115 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop'
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Sorbet
|
||||
# This cop checks that the Sorbet sigil comes as the first magic comment in the file.
|
||||
#
|
||||
# The expected order for magic comments is: typed, (en)?coding, warn_indent then frozen_string_literal.
|
||||
#
|
||||
# For example, the following bad ordering:
|
||||
#
|
||||
# ```ruby
|
||||
# # frozen_string_literal: true
|
||||
# # typed: true
|
||||
# class Foo; end
|
||||
# ```
|
||||
#
|
||||
# Will be corrected as:
|
||||
#
|
||||
# ```ruby
|
||||
# # typed: true
|
||||
# # frozen_string_literal: true
|
||||
# class Foo; end
|
||||
# ```
|
||||
#
|
||||
# Only `typed`, `(en)?coding`, `warn_indent` and `frozen_string_literal` magic comments are considered,
|
||||
# other comments or magic comments are left in the same place.
|
||||
class EnforceSigilOrder < ValidSigil
|
||||
include RangeHelp
|
||||
|
||||
def investigate(processed_source)
|
||||
return if processed_source.tokens.empty?
|
||||
|
||||
tokens = extract_magic_comments(processed_source)
|
||||
return if tokens.empty?
|
||||
|
||||
check_magic_comments_order(tokens)
|
||||
end
|
||||
|
||||
def autocorrect(_node)
|
||||
lambda do |corrector|
|
||||
tokens = extract_magic_comments(processed_source)
|
||||
|
||||
# Get the magic comments tokens in their expected order
|
||||
expected = PREFERRED_ORDER.keys.map do |re|
|
||||
tokens.select { |token| re.match?(token.text) }
|
||||
end.flatten
|
||||
|
||||
tokens.each_with_index do |token, index|
|
||||
corrector.replace(token.pos, expected[index].text)
|
||||
end
|
||||
|
||||
# Remove blank lines between the magic comments
|
||||
lines = tokens.map(&:line).to_set
|
||||
(lines.min...lines.max).each do |line|
|
||||
next if lines.include?(line)
|
||||
next unless processed_source[line - 1].empty?
|
||||
corrector.remove(source_range(processed_source.buffer, line, 0))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
CODING_REGEX = /#\s+(en)?coding:(?:\s+([\w]+))?/
|
||||
INDENT_REGEX = /#\s+warn_indent:(?:\s+([\w]+))?/
|
||||
FROZEN_REGEX = /#\s+frozen_string_literal:(?:\s+([\w]+))?/
|
||||
|
||||
PREFERRED_ORDER = {
|
||||
CODING_REGEX => 'encoding',
|
||||
SIGIL_REGEX => 'typed',
|
||||
INDENT_REGEX => 'warn_indent',
|
||||
FROZEN_REGEX => 'frozen_string_literal',
|
||||
}.freeze
|
||||
|
||||
MAGIC_REGEX = Regexp.union(*PREFERRED_ORDER.keys)
|
||||
|
||||
# extraction
|
||||
|
||||
# Get all the tokens in `processed_source` that match `MAGIC_REGEX`
|
||||
def extract_magic_comments(processed_source)
|
||||
processed_source.tokens
|
||||
.take_while { |token| token.type == :tCOMMENT }
|
||||
.select { |token| MAGIC_REGEX.match?(token.text) }
|
||||
end
|
||||
|
||||
# checks
|
||||
|
||||
def check_magic_comments_order(tokens)
|
||||
# Get the current magic comments order
|
||||
order = tokens.map do |token|
|
||||
PREFERRED_ORDER.keys.find { |re| re.match?(token.text) }
|
||||
end.compact.uniq
|
||||
|
||||
# Get the expected magic comments order based on the one used in the actual source
|
||||
expected = PREFERRED_ORDER.keys.select do |re|
|
||||
tokens.any? { |token| re.match?(token.text) }
|
||||
end.uniq
|
||||
|
||||
if order != expected
|
||||
tokens.each do |token|
|
||||
add_offense(
|
||||
token,
|
||||
location: token.pos,
|
||||
message: "Magic comments should be in the following order: #{PREFERRED_ORDER.values.join(', ')}."
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop'
|
||||
require_relative 'has_sigil'
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Sorbet
|
||||
# This cop makes the Sorbet `false` sigil mandatory in all files.
|
||||
class FalseSigil < HasSigil
|
||||
def minimum_strictness
|
||||
'false'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop'
|
||||
require_relative 'valid_sigil'
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Sorbet
|
||||
# This cop makes the Sorbet typed sigil mandatory in all files.
|
||||
#
|
||||
# Options:
|
||||
#
|
||||
# * `SuggestedStrictness`: Sorbet strictness level suggested in offense messages (default: 'false')
|
||||
# * `MinimumStrictness`: If set, make offense if the strictness level in the file is below this one
|
||||
#
|
||||
# If a `MinimumStrictness` level is specified, it will be used in offense messages and autocorrect.
|
||||
class HasSigil < ValidSigil
|
||||
@registry = Cop.registry # So we can properly subclass this cop
|
||||
|
||||
def require_sigil_on_all_files?
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop'
|
||||
require_relative 'has_sigil'
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Sorbet
|
||||
# This cop makes the Sorbet `ignore` sigil mandatory in all files.
|
||||
class IgnoreSigil < HasSigil
|
||||
def minimum_strictness
|
||||
'ignore'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop'
|
||||
require_relative 'has_sigil'
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Sorbet
|
||||
# This cop makes the Sorbet `strict` sigil mandatory in all files.
|
||||
class StrictSigil < HasSigil
|
||||
def minimum_strictness
|
||||
'strict'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop'
|
||||
require_relative 'has_sigil'
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Sorbet
|
||||
# This cop makes the Sorbet `strong` sigil mandatory in all files.
|
||||
class StrongSigil < HasSigil
|
||||
def minimum_strictness
|
||||
'strong'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop'
|
||||
require_relative 'has_sigil'
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Sorbet
|
||||
# This cop makes the Sorbet `true` sigil mandatory in all files.
|
||||
class TrueSigil < HasSigil
|
||||
def minimum_strictness
|
||||
'true'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,160 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop'
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Sorbet
|
||||
# This cop checks that every Ruby file contains a valid Sorbet sigil.
|
||||
# Adapted from: https://gist.github.com/clarkdave/85aca4e16f33fd52aceb6a0a29936e52
|
||||
#
|
||||
# Options:
|
||||
#
|
||||
# * `RequireSigilOnAllFiles`: make offense if the Sorbet typed is not found in the file (default: false)
|
||||
# * `SuggestedStrictness`: Sorbet strictness level suggested in offense messages (default: 'false')
|
||||
# * `MinimumStrictness`: If set, make offense if the strictness level in the file is below this one
|
||||
#
|
||||
# If a `MinimumStrictness` level is specified, it will be used in offense messages and autocorrect.
|
||||
class ValidSigil < RuboCop::Cop::Cop
|
||||
@registry = Cop.registry # So we can properly subclass this cop
|
||||
|
||||
def investigate(processed_source)
|
||||
return if processed_source.tokens.empty?
|
||||
|
||||
sigil = extract_sigil(processed_source)
|
||||
return unless check_sigil_present(sigil)
|
||||
|
||||
strictness = extract_strictness(sigil)
|
||||
return unless check_strictness_not_empty(sigil, strictness)
|
||||
return unless check_strictness_valid(sigil, strictness)
|
||||
return unless check_strictness_level(sigil, strictness)
|
||||
end
|
||||
|
||||
def autocorrect(_node)
|
||||
lambda do |corrector|
|
||||
return unless require_sigil_on_all_files?
|
||||
return unless extract_sigil(processed_source).nil?
|
||||
|
||||
token = processed_source.tokens.first
|
||||
replace_with = suggested_strictness_level(minimum_strictness, suggested_strictness)
|
||||
sigil = "# typed: #{replace_with}"
|
||||
if token.text.start_with?("#!") # shebang line
|
||||
corrector.insert_after(token.pos, "\n#{sigil}")
|
||||
else
|
||||
corrector.insert_before(token.pos, "#{sigil}\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
STRICTNESS_LEVELS = %w(ignore false true strict strong)
|
||||
SIGIL_REGEX = /#\s+typed:(?:\s+([\w]+))?/
|
||||
|
||||
# extraction
|
||||
|
||||
def extract_sigil(processed_source)
|
||||
processed_source.tokens
|
||||
.take_while { |token| token.type == :tCOMMENT }
|
||||
.find { |token| SIGIL_REGEX.match?(token.text) }
|
||||
end
|
||||
|
||||
def extract_strictness(sigil)
|
||||
sigil.text.match(SIGIL_REGEX)&.captures&.first
|
||||
end
|
||||
|
||||
# checks
|
||||
|
||||
def check_sigil_present(sigil)
|
||||
return true unless sigil.nil?
|
||||
|
||||
token = processed_source.tokens.first
|
||||
if require_sigil_on_all_files?
|
||||
strictness = suggested_strictness_level(minimum_strictness, suggested_strictness)
|
||||
add_offense(
|
||||
token,
|
||||
location: token.pos,
|
||||
message: 'No Sorbet sigil found in file. ' \
|
||||
"Try a `typed: #{strictness}` to start (you can also use `rubocop -a` to automatically add this)."
|
||||
)
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
def suggested_strictness_level(minimum_strictness, suggested_strictness)
|
||||
# if no minimum strictness is set (eg. using Sorbet/HasSigil without config) then
|
||||
# we always use the suggested strictness which defaults to `false`
|
||||
return suggested_strictness unless minimum_strictness
|
||||
|
||||
# special case: if you're using Sorbet/IgnoreSigil without config, we should recommend `ignore`
|
||||
return "ignore" if minimum_strictness == "ignore" && cop_config['SuggestedStrictness'].nil?
|
||||
|
||||
# if a minimum strictness is set (eg. you're using Sorbet/FalseSigil)
|
||||
# we want to compare the minimum strictness and suggested strictness. this is because
|
||||
# the suggested strictness might be higher than the minimum (eg. if you want all new files
|
||||
# at a higher strictness level, without having to migrate existing files at lower levels).
|
||||
|
||||
suggested_level = STRICTNESS_LEVELS.index(suggested_strictness)
|
||||
minimum_level = STRICTNESS_LEVELS.index(minimum_strictness)
|
||||
|
||||
suggested_level > minimum_level ? suggested_strictness : minimum_strictness
|
||||
end
|
||||
|
||||
def check_strictness_not_empty(sigil, strictness)
|
||||
return true if strictness
|
||||
|
||||
add_offense(
|
||||
sigil,
|
||||
location: sigil.pos,
|
||||
message: 'Sorbet sigil should not be empty.'
|
||||
)
|
||||
false
|
||||
end
|
||||
|
||||
def check_strictness_valid(sigil, strictness)
|
||||
return true if STRICTNESS_LEVELS.include?(strictness)
|
||||
|
||||
add_offense(
|
||||
sigil,
|
||||
location: sigil.pos,
|
||||
message: "Invalid Sorbet sigil `#{strictness}`."
|
||||
)
|
||||
false
|
||||
end
|
||||
|
||||
def check_strictness_level(sigil, strictness)
|
||||
return true unless minimum_strictness
|
||||
|
||||
minimum_level = STRICTNESS_LEVELS.index(minimum_strictness)
|
||||
current_level = STRICTNESS_LEVELS.index(strictness)
|
||||
if current_level < minimum_level
|
||||
add_offense(
|
||||
sigil,
|
||||
location: sigil.pos,
|
||||
message: "Sorbet sigil should be at least `#{minimum_strictness}` got `#{strictness}`."
|
||||
)
|
||||
return false
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
# options
|
||||
|
||||
# Default is `false`
|
||||
def require_sigil_on_all_files?
|
||||
!!cop_config['RequireSigilOnAllFiles']
|
||||
end
|
||||
|
||||
# Default is `'false'`
|
||||
def suggested_strictness
|
||||
STRICTNESS_LEVELS.include?(cop_config['SuggestedStrictness']) ? cop_config['SuggestedStrictness'] : 'false'
|
||||
end
|
||||
|
||||
# Default is `nil`
|
||||
def minimum_strictness
|
||||
cop_config['MinimumStrictness'] if STRICTNESS_LEVELS.include?(cop_config['MinimumStrictness'])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,59 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop'
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Sorbet
|
||||
# This cop disallows using `.override(allow_incompatible: true)`.
|
||||
# Using `allow_incompatible` suggests a violation of the Liskov
|
||||
# Substitution Principle, meaning that a subclass is not a valid
|
||||
# subtype of it's superclass. This Cop prevents these design smells
|
||||
# from occurring.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# sig.override(allow_incompatible: true)
|
||||
#
|
||||
# # good
|
||||
# sig.override
|
||||
class AllowIncompatibleOverride < RuboCop::Cop::Cop
|
||||
def_node_search(:sig?, <<-PATTERN)
|
||||
(
|
||||
send
|
||||
nil?
|
||||
:sig
|
||||
...
|
||||
)
|
||||
PATTERN
|
||||
|
||||
def not_nil?(node)
|
||||
!node.nil?
|
||||
end
|
||||
|
||||
def_node_search(:allow_incompatible?, <<-PATTERN)
|
||||
(pair (sym :allow_incompatible) (true))
|
||||
PATTERN
|
||||
|
||||
def_node_matcher(:allow_incompatible_override?, <<-PATTERN)
|
||||
(
|
||||
send
|
||||
[#not_nil? #sig?]
|
||||
:override
|
||||
[#not_nil? #allow_incompatible?]
|
||||
)
|
||||
PATTERN
|
||||
|
||||
def on_send(node)
|
||||
return unless allow_incompatible_override?(node)
|
||||
add_offense(
|
||||
node.children[2],
|
||||
message: 'Usage of `allow_incompatible` suggests a violation of the Liskov Substitution Principle. '\
|
||||
'Instead, strive to write interfaces which respect subtyping principles and remove `allow_incompatible`',
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,53 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop'
|
||||
require_relative 'signature_cop'
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Sorbet
|
||||
# This cop disallows the usage of `checked(true)`. This usage could cause
|
||||
# confusion; it could lead some people to believe that a method would be checked
|
||||
# even if runtime checks have not been enabled on the class or globally.
|
||||
# Additionally, in the event where checks are enabled, `checked(true)` would
|
||||
# be redundant; only `checked(false)` or `soft` would change the behaviour.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# sig { void.checked(true) }
|
||||
#
|
||||
# # good
|
||||
# sig { void }
|
||||
class CheckedTrueInSignature < SignatureCop
|
||||
include(RuboCop::Cop::RangeHelp)
|
||||
|
||||
def_node_search(:offending_node, <<~PATTERN)
|
||||
(send _ :checked (true))
|
||||
PATTERN
|
||||
|
||||
MESSAGE =
|
||||
'Using `checked(true)` in a method signature definition is not allowed. ' \
|
||||
'`checked(true)` is the default behavior for modules/classes with runtime checks enabled. ' \
|
||||
'To enable typechecking at runtime for this module, regardless of global settings, ' \
|
||||
'`include(WaffleCone::RuntimeChecks)` to this module and set other methods to `checked(false)`.'
|
||||
private_constant(:MESSAGE)
|
||||
|
||||
def on_signature(node)
|
||||
error = offending_node(node).first
|
||||
return unless error
|
||||
|
||||
add_offense(
|
||||
error,
|
||||
location: source_range(
|
||||
processed_source.buffer,
|
||||
error.location.line,
|
||||
(error.location.selector.begin_pos)..(error.location.end.begin_pos),
|
||||
),
|
||||
message: MESSAGE
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,135 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop'
|
||||
require 'stringio'
|
||||
require_relative 'signature_cop'
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Sorbet
|
||||
# This cop checks that every method definition and attribute accessor has a Sorbet signature.
|
||||
#
|
||||
# It also suggest an autocorrect with placeholders so the following code:
|
||||
#
|
||||
# ```
|
||||
# def foo(a, b, c); end
|
||||
# ```
|
||||
#
|
||||
# Will be corrected as:
|
||||
#
|
||||
# ```
|
||||
# sig { params(a: T.untyped, b: T.untyped, c: T.untyped).returns(T.untyped)
|
||||
# def foo(a, b, c); end
|
||||
# ```
|
||||
#
|
||||
# You can configure the placeholders used by changing the following options:
|
||||
#
|
||||
# * `ParameterTypePlaceholder`: placeholders used for parameter types (default: 'T.untyped')
|
||||
# * `ReturnTypePlaceholder`: placeholders used for return types (default: 'T.untyped')
|
||||
class EnforceSignatures < SignatureCop
|
||||
def_node_matcher(:accessor?, <<-PATTERN)
|
||||
(send nil? {:attr_reader :attr_writer :attr_accessor} ...)
|
||||
PATTERN
|
||||
|
||||
def on_def(node)
|
||||
check_node(node)
|
||||
end
|
||||
|
||||
def on_defs(node)
|
||||
check_node(node)
|
||||
end
|
||||
|
||||
def on_send(node)
|
||||
return unless accessor?(node)
|
||||
check_node(node)
|
||||
end
|
||||
|
||||
def autocorrect(node)
|
||||
lambda do |corrector|
|
||||
suggest = SigSuggestion.new(node.loc.column, param_type_placeholder, return_type_placeholder)
|
||||
|
||||
if node.is_a?(RuboCop::AST::DefNode) # def something
|
||||
node.arguments.each do |arg|
|
||||
suggest.params << arg.children.first
|
||||
end
|
||||
elsif accessor?(node) # attr reader, writer, accessor
|
||||
method = node.children[1]
|
||||
symbol = node.children[2]
|
||||
suggest.params << symbol.value if symbol && (method == :attr_writer || method == :attr_accessor)
|
||||
suggest.returns = 'void' if method == :attr_writer
|
||||
end
|
||||
|
||||
corrector.insert_before(node.loc.expression, suggest.to_autocorrect)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_node(node)
|
||||
prev = previous_node(node)
|
||||
unless signature?(prev)
|
||||
add_offense(
|
||||
node,
|
||||
message: "Each method is required to have a signature."
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def previous_node(node)
|
||||
parent = node.parent
|
||||
return nil unless parent
|
||||
parent.children[node.sibling_index - 1]
|
||||
end
|
||||
|
||||
def param_type_placeholder
|
||||
cop_config['ParameterTypePlaceholder'] || 'T.untyped'
|
||||
end
|
||||
|
||||
def return_type_placeholder
|
||||
cop_config['ReturnTypePlaceholder'] || 'T.untyped'
|
||||
end
|
||||
|
||||
class SigSuggestion
|
||||
attr_accessor :params, :returns
|
||||
|
||||
def initialize(indent, param_placeholder, return_placeholder)
|
||||
@params = []
|
||||
@returns = nil
|
||||
@indent = indent
|
||||
@param_placeholder = param_placeholder
|
||||
@return_placeholder = return_placeholder
|
||||
end
|
||||
|
||||
def to_autocorrect
|
||||
out = StringIO.new
|
||||
out << 'sig { '
|
||||
out << generate_params
|
||||
out << generate_return
|
||||
out << " }\n"
|
||||
out << ' ' * @indent # preserve indent for the next line
|
||||
out.string
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def generate_params
|
||||
return if @params.empty?
|
||||
out = StringIO.new
|
||||
out << 'params('
|
||||
out << @params.map do |param|
|
||||
"#{param}: #{@param_placeholder}"
|
||||
end.join(", ")
|
||||
out << ').'
|
||||
out.string
|
||||
end
|
||||
|
||||
def generate_return
|
||||
return "returns(#{@return_placeholder})" if @returns.nil?
|
||||
return @returns if @returns == 'void'
|
||||
"returns(#{@returns})"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,51 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop'
|
||||
require_relative 'signature_cop'
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Sorbet
|
||||
# This cop checks for the ordering of keyword arguments required by
|
||||
# sorbet-runtime. The ordering requires that all keyword arguments
|
||||
# are at the end of the parameters list, and all keyword arguments
|
||||
# with a default value must be after those without default values.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# sig { params(a: Integer, b: String).void }
|
||||
# def foo(a: 1, b:); end
|
||||
#
|
||||
# # good
|
||||
# sig { params(b: String, a: Integer).void }
|
||||
# def foo(b:, a: 1); end
|
||||
class KeywordArgumentOrdering < SignatureCop
|
||||
def on_signature(node)
|
||||
method_node = node.parent.children[node.sibling_index + 1]
|
||||
return if method_node.nil?
|
||||
method_parameters = method_node.arguments
|
||||
|
||||
check_order_for_kwoptargs(method_parameters)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_order_for_kwoptargs(parameters)
|
||||
out_of_kwoptarg = false
|
||||
|
||||
parameters.reverse.each do |param|
|
||||
out_of_kwoptarg = true unless param.type == :kwoptarg || param.type == :blockarg || param.type == :kwrestarg
|
||||
|
||||
next unless param.type == :kwoptarg && out_of_kwoptarg
|
||||
|
||||
add_offense(
|
||||
param,
|
||||
message: 'Optional keyword arguments must be at the end of the parameter list.'
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,70 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop'
|
||||
require_relative 'signature_cop'
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Sorbet
|
||||
# This cop checks for inconsistent ordering of parameters between the
|
||||
# signature and the method definition. The sorbet-runtime gem raises
|
||||
# when such inconsistency occurs.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# sig { params(a: Integer, b: String).void }
|
||||
# def foo(b:, a:); end
|
||||
#
|
||||
# # good
|
||||
# sig { params(a: Integer, b: String).void }
|
||||
# def foo(a:, b:); end
|
||||
class ParametersOrderingInSignature < SignatureCop
|
||||
def_node_search(:signature_params, <<-PATTERN)
|
||||
(send _ :params ...)
|
||||
PATTERN
|
||||
|
||||
def on_signature(node)
|
||||
sig_params = signature_params(node).first
|
||||
|
||||
sig_params_order = extract_parameters(sig_params)
|
||||
return if sig_params_order.nil?
|
||||
method_node = node.parent.children[node.sibling_index + 1]
|
||||
return if method_node.nil? || method_node.type != :def
|
||||
method_parameters = method_node.arguments
|
||||
|
||||
check_for_inconsistent_param_ordering(sig_params_order, method_parameters)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def extract_parameters(sig_params)
|
||||
return [] if sig_params.nil?
|
||||
|
||||
arguments = sig_params.arguments.first
|
||||
return arguments.keys.map(&:value) if RuboCop::AST::HashNode === arguments
|
||||
|
||||
add_offense(
|
||||
sig_params,
|
||||
message: "Invalid signature."
|
||||
)
|
||||
end
|
||||
|
||||
def check_for_inconsistent_param_ordering(sig_params_order, parameters)
|
||||
parameters.each_with_index do |param, index|
|
||||
param_name = param.children[0]
|
||||
sig_param_name = sig_params_order[index]
|
||||
|
||||
next if param_name == sig_param_name
|
||||
|
||||
add_offense(
|
||||
param,
|
||||
message: "Inconsistent ordering of arguments at index #{index}. " \
|
||||
"Expected `#{sig_param_name}` from sig above."
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,104 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop'
|
||||
require_relative 'signature_cop'
|
||||
|
||||
begin
|
||||
require 'unparser'
|
||||
rescue LoadError
|
||||
nil
|
||||
end
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Sorbet
|
||||
class SignatureBuildOrder < SignatureCop
|
||||
ORDER =
|
||||
[
|
||||
:abstract,
|
||||
:override,
|
||||
:overridable,
|
||||
:type_parameters,
|
||||
:params,
|
||||
:returns,
|
||||
:void,
|
||||
:soft,
|
||||
:checked,
|
||||
:on_failure,
|
||||
].each_with_index.to_h.freeze
|
||||
|
||||
def_node_search(:root_call, <<~PATTERN)
|
||||
(send nil? {#{ORDER.keys.map(&:inspect).join(' ')}} ...)
|
||||
PATTERN
|
||||
|
||||
def on_signature(node)
|
||||
calls = call_chain(node.children[2]).map(&:method_name)
|
||||
return unless calls.any?
|
||||
|
||||
expected_order = calls.sort_by { |call| ORDER[call] }
|
||||
return if expected_order == calls
|
||||
|
||||
message = "Sig builders must be invoked in the following order: #{expected_order.join(', ')}."
|
||||
|
||||
unless can_autocorrect?
|
||||
message += ' For autocorrection, add the `unparser` gem to your project.'
|
||||
end
|
||||
|
||||
add_offense(
|
||||
node.children[2],
|
||||
message: message,
|
||||
)
|
||||
node
|
||||
end
|
||||
|
||||
def autocorrect(node)
|
||||
return nil unless can_autocorrect?
|
||||
|
||||
lambda do |corrector|
|
||||
tree = call_chain(node_with_index_sends(node))
|
||||
.sort_by { |call| ORDER[call.method_name] }
|
||||
.reduce(nil) do |receiver, caller|
|
||||
caller.updated(nil, [receiver] + caller.children.drop(1))
|
||||
end
|
||||
|
||||
corrector.replace(
|
||||
node.source_range,
|
||||
Unparser.unparse(tree),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def node_with_index_sends(node)
|
||||
# This is really dirty hack to reparse the current node with index send
|
||||
# emitting enabled, which is necessary to unparse them back as index accessors.
|
||||
emit_index_value = RuboCop::AST::Builder.emit_index
|
||||
RuboCop::AST::Builder.emit_index = true
|
||||
RuboCop::AST::ProcessedSource.new(node.source, target_ruby_version, processed_source.path).ast
|
||||
ensure
|
||||
RuboCop::AST::Builder.emit_index = emit_index_value
|
||||
end
|
||||
|
||||
def can_autocorrect?
|
||||
defined?(::Unparser)
|
||||
end
|
||||
|
||||
def call_chain(sig_child_node)
|
||||
call_node = root_call(sig_child_node).first
|
||||
return [] unless call_node
|
||||
|
||||
calls = []
|
||||
while call_node != sig_child_node
|
||||
calls << call_node
|
||||
call_node = call_node.parent
|
||||
end
|
||||
|
||||
calls << sig_child_node
|
||||
|
||||
calls
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,28 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop'
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Sorbet
|
||||
# Abstract cop specific to Sorbet signatures
|
||||
#
|
||||
# You can subclass it to use the `on_signature` trigger and the `signature?` node matcher.
|
||||
class SignatureCop < RuboCop::Cop::Cop
|
||||
@registry = Cop.registry # So we can properly subclass this cop
|
||||
|
||||
def_node_matcher(:signature?, <<~PATTERN)
|
||||
(block (send nil? :sig) (args) ...)
|
||||
PATTERN
|
||||
|
||||
def on_block(node)
|
||||
on_signature(node) if signature?(node)
|
||||
end
|
||||
|
||||
def on_signature(_)
|
||||
# To be defined in subclasses
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
require_relative 'sorbet/binding_constants_without_type_alias'
|
||||
require_relative 'sorbet/constants_from_strings'
|
||||
require_relative 'sorbet/forbid_superclass_const_literal'
|
||||
require_relative 'sorbet/forbid_include_const_literal'
|
||||
require_relative 'sorbet/forbid_untyped_struct_props'
|
||||
|
||||
require_relative 'sorbet/signatures/allow_incompatible_override'
|
||||
require_relative 'sorbet/signatures/checked_true_in_signature'
|
||||
require_relative 'sorbet/signatures/keyword_argument_ordering'
|
||||
require_relative 'sorbet/signatures/parameters_ordering_in_signature'
|
||||
require_relative 'sorbet/signatures/signature_build_order'
|
||||
require_relative 'sorbet/signatures/enforce_signatures'
|
||||
|
||||
require_relative 'sorbet/sigils/valid_sigil'
|
||||
require_relative 'sorbet/sigils/has_sigil'
|
||||
require_relative 'sorbet/sigils/ignore_sigil'
|
||||
require_relative 'sorbet/sigils/false_sigil'
|
||||
require_relative 'sorbet/sigils/true_sigil'
|
||||
require_relative 'sorbet/sigils/strict_sigil'
|
||||
require_relative 'sorbet/sigils/strong_sigil'
|
||||
require_relative 'sorbet/sigils/enforce_sigil_order'
|
||||
15
Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/sorbet.rb
vendored
Normal file
15
Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/sorbet.rb
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
require "rubocop/sorbet/version"
|
||||
require "yaml"
|
||||
|
||||
module RuboCop
|
||||
module Sorbet
|
||||
class Error < StandardError; end
|
||||
|
||||
PROJECT_ROOT = Pathname.new(__dir__).parent.parent.expand_path.freeze
|
||||
CONFIG_DEFAULT = PROJECT_ROOT.join('config', 'default.yml').freeze
|
||||
CONFIG = YAML.safe_load(CONFIG_DEFAULT.read).freeze
|
||||
|
||||
private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT)
|
||||
end
|
||||
end
|
||||
20
Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/sorbet/inject.rb
vendored
Normal file
20
Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/sorbet/inject.rb
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# The original code is from https://github.com/rubocop-hq/rubocop-rspec/blob/master/lib/rubocop/rspec/inject.rb
|
||||
# See https://github.com/rubocop-hq/rubocop-rspec/blob/master/MIT-LICENSE.md
|
||||
module RuboCop
|
||||
module Sorbet
|
||||
# Because RuboCop doesn't yet support plugins, we have to monkey patch in a
|
||||
# bit of our configuration.
|
||||
module Inject
|
||||
def self.defaults!
|
||||
path = CONFIG_DEFAULT.to_s
|
||||
hash = ConfigLoader.send(:load_yaml_configuration, path)
|
||||
config = Config.new(hash, path).tap(&:make_excludes_absolute)
|
||||
puts "configuration from #{path}" if ConfigLoader.debug?
|
||||
config = ConfigLoader.merge_with_default(config, path)
|
||||
ConfigLoader.instance_variable_set(:@default_configuration, config)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
module RuboCop
|
||||
module Sorbet
|
||||
VERSION = "0.5.1"
|
||||
end
|
||||
end
|
||||
146
Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/sorbet-runtime-stub-0.2.0/lib/sorbet-runtime-stub.rb
vendored
Normal file
146
Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/sorbet-runtime-stub-0.2.0/lib/sorbet-runtime-stub.rb
vendored
Normal file
@ -0,0 +1,146 @@
|
||||
# typed: ignore
|
||||
|
||||
begin
|
||||
gem "sorbet-runtime"
|
||||
return
|
||||
rescue Gem::LoadError
|
||||
end
|
||||
|
||||
module T
|
||||
class << self
|
||||
def absurd(value); end
|
||||
def all(type_a, type_b, *types); end
|
||||
def any(type_a, type_b, *types); end
|
||||
def attached_class; end
|
||||
def class_of(klass); end
|
||||
def enum(values); end
|
||||
def nilable(type); end
|
||||
def noreturn; end
|
||||
def self_type; end
|
||||
def type_alias(type=nil, &_blk); end
|
||||
def type_parameter(name); end
|
||||
def untyped; end
|
||||
|
||||
def assert_type!(value, _type, _checked: true)
|
||||
value
|
||||
end
|
||||
|
||||
def cast(value, _type, _checked: true)
|
||||
value
|
||||
end
|
||||
|
||||
def let(value, _type, _checked: true)
|
||||
value
|
||||
end
|
||||
|
||||
def must(arg, _msg = nil)
|
||||
arg
|
||||
end
|
||||
|
||||
def proc
|
||||
T::Proc.new
|
||||
end
|
||||
|
||||
def reveal_type(value)
|
||||
value
|
||||
end
|
||||
|
||||
def unsafe(value)
|
||||
value
|
||||
end
|
||||
end
|
||||
|
||||
module Sig
|
||||
def sig(arg0=nil, &blk); end
|
||||
end
|
||||
|
||||
module Helpers
|
||||
def abstract!; end
|
||||
def interface!; end
|
||||
def final!; end
|
||||
def sealed!; end
|
||||
def mixes_in_class_methods(mod); end
|
||||
end
|
||||
|
||||
module Generic
|
||||
include T::Helpers
|
||||
|
||||
def type_parameters(*params); end
|
||||
def type_member(variance=:invariant, fixed: nil, lower: nil, upper: BasicObject); end
|
||||
def type_template(variance=:invariant, fixed: nil, lower: nil, upper: BasicObject); end
|
||||
|
||||
def [](*types)
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
module Array
|
||||
def self.[](type); end
|
||||
end
|
||||
|
||||
Boolean = Object.new.freeze
|
||||
|
||||
module Configuration
|
||||
def self.call_validation_error_handler(signature, opts); end
|
||||
def self.call_validation_error_handler=(value); end
|
||||
def self.default_checked_level=(default_checked_level); end
|
||||
def self.enable_checking_for_sigs_marked_checked_tests; end
|
||||
def self.enable_final_checks_on_hooks; end
|
||||
def self.enable_legacy_t_enum_migration_mode; end
|
||||
def self.reset_final_checks_on_hooks; end
|
||||
def self.hard_assert_handler(str, extra); end
|
||||
def self.hard_assert_handler=(value); end
|
||||
def self.inline_type_error_handler(error); end
|
||||
def self.inline_type_error_handler=(value); end
|
||||
def self.log_info_handler(str, extra); end
|
||||
def self.log_info_handler=(value); end
|
||||
def self.scalar_types; end
|
||||
def self.scalar_types=(values); end
|
||||
def self.sealed_violation_whitelist; end
|
||||
def self.sealed_violation_whitelist=(sealed_violation_whitelist); end
|
||||
def self.sig_builder_error_handler(error, location); end
|
||||
def self.sig_builder_error_handler=(value); end
|
||||
def self.sig_validation_error_handler(error, opts); end
|
||||
def self.sig_validation_error_handler=(value); end
|
||||
def self.soft_assert_handler(str, extra); end
|
||||
def self.soft_assert_handler=(value); end
|
||||
end
|
||||
|
||||
module Enumerable
|
||||
def self.[](type); end
|
||||
end
|
||||
|
||||
module Enumerator
|
||||
def self.[](type); end
|
||||
end
|
||||
|
||||
module Hash
|
||||
def self.[](keys, values); end
|
||||
end
|
||||
|
||||
class Proc
|
||||
def bind(*_)
|
||||
self
|
||||
end
|
||||
|
||||
def params(*_param)
|
||||
self
|
||||
end
|
||||
|
||||
def void
|
||||
self
|
||||
end
|
||||
|
||||
def returns(_type)
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
module Range
|
||||
def self.[](type); end
|
||||
end
|
||||
|
||||
module Set
|
||||
def self.[](type); end
|
||||
end
|
||||
end
|
||||
@ -81,6 +81,7 @@ switch
|
||||
tap
|
||||
tap-info
|
||||
tap-new
|
||||
tc
|
||||
test
|
||||
tests
|
||||
typecheck
|
||||
|
||||
@ -1770,6 +1770,9 @@ For example, you might add something like the following to your ~/.profile, ~/.b
|
||||
* `HOMEBREW_SKIP_OR_LATER_BOTTLES`:
|
||||
If set with `HOMEBREW_DEVELOPER`, do not use bottles from older versions of macOS. This is useful in development on new macOS versions.
|
||||
|
||||
* `HOMEBREW_SORBET_RUNTIME`:
|
||||
Enable runtime typechecking using Sorbet.
|
||||
|
||||
* `HOMEBREW_SVN`:
|
||||
Use this as the `svn`(1) binary.
|
||||
|
||||
|
||||
@ -2446,6 +2446,10 @@ If set, use Pry for the \fBbrew irb\fR command\.
|
||||
If set with \fBHOMEBREW_DEVELOPER\fR, do not use bottles from older versions of macOS\. This is useful in development on new macOS versions\.
|
||||
.
|
||||
.TP
|
||||
\fBHOMEBREW_SORBET_RUNTIME\fR
|
||||
Enable runtime typechecking using Sorbet\.
|
||||
.
|
||||
.TP
|
||||
\fBHOMEBREW_SVN\fR
|
||||
Use this as the \fBsvn\fR(1) binary\.
|
||||
.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user