Merge branch 'Homebrew:master' into mohammad

This commit is contained in:
Mohammad Zain Abbas 2022-07-24 13:39:59 +02:00 committed by GitHub
commit 96a7ff1019
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
230 changed files with 11949 additions and 323 deletions

View File

@ -37,6 +37,7 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 21 days-before-stale: 21
days-before-close: 7 days-before-close: 7
close-issue-reason: "not_planned"
stale-issue-message: > stale-issue-message: >
This issue has been automatically marked as stale because it has not had This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. recent activity. It will be closed if no further activity occurs.

3
.gitignore vendored
View File

@ -150,8 +150,7 @@
**/vendor/bundle/ruby/*/gems/simplecov-*/ **/vendor/bundle/ruby/*/gems/simplecov-*/
**/vendor/bundle/ruby/*/gems/simplecov-html-*/ **/vendor/bundle/ruby/*/gems/simplecov-html-*/
**/vendor/bundle/ruby/*/gems/sorbet-*/ **/vendor/bundle/ruby/*/gems/sorbet-*/
**/vendor/bundle/ruby/*/gems/sorbet-runtime-*/ !**/vendor/bundle/ruby/*/gems/sorbet-runtime-*/
!**/vendor/bundle/ruby/*/gems/sorbet-runtime-stub-*/
**/vendor/bundle/ruby/*/gems/spoom-*/ **/vendor/bundle/ruby/*/gems/spoom-*/
**/vendor/bundle/ruby/*/gems/stackprof-*/ **/vendor/bundle/ruby/*/gems/stackprof-*/
**/vendor/bundle/ruby/*/gems/strscan-*/ **/vendor/bundle/ruby/*/gems/strscan-*/

View File

@ -2,6 +2,8 @@
source "https://rubygems.org" source "https://rubygems.org"
ruby ">= 2.6.0"
# disallowed gems (should not be used) # disallowed gems (should not be used)
# * nokogiri - use rexml instead for XML parsing # * nokogiri - use rexml instead for XML parsing
@ -17,6 +19,7 @@ gem "rspec-github", require: false
gem "rspec-its", require: false gem "rspec-its", require: false
gem "rspec_junit_formatter", require: false gem "rspec_junit_formatter", require: false
gem "rspec-retry", require: false gem "rspec-retry", require: false
gem "rspec-sorbet", require: false
gem "rspec-wait", require: false gem "rspec-wait", require: false
gem "rubocop", require: false gem "rubocop", require: false
gem "rubocop-ast", require: false gem "rubocop-ast", require: false
@ -26,13 +29,12 @@ gem "warning", require: false
group :sorbet, optional: true do group :sorbet, optional: true do
gem "parlour", require: false gem "parlour", require: false
gem "rspec-sorbet", require: false
gem "sorbet-static-and-runtime", require: false gem "sorbet-static-and-runtime", require: false
gem "tapioca", require: false gem "tapioca", require: false
end end
# vendored gems # vendored gems
gem "activesupport", "< 7" # 7 requires Ruby 2.7 gem "activesupport"
gem "addressable" gem "addressable"
gem "concurrent-ruby" gem "concurrent-ruby"
gem "did_you_mean" # remove when HOMEBREW_REQUIRED_RUBY_VERSION >= 2.7 gem "did_you_mean" # remove when HOMEBREW_REQUIRED_RUBY_VERSION >= 2.7
@ -44,4 +46,4 @@ gem "rubocop-rails"
gem "rubocop-rspec" gem "rubocop-rspec"
gem "rubocop-sorbet" gem "rubocop-sorbet"
gem "ruby-macho" gem "ruby-macho"
gem "sorbet-runtime-stub" gem "sorbet-runtime"

View File

@ -124,19 +124,19 @@ GEM
rspec (>= 3, < 4) rspec (>= 3, < 4)
rspec_junit_formatter (0.5.1) rspec_junit_formatter (0.5.1)
rspec-core (>= 2, < 4, != 2.12.0) rspec-core (>= 2, < 4, != 2.12.0)
rubocop (1.31.2) rubocop (1.32.0)
json (~> 2.3) json (~> 2.3)
parallel (~> 1.10) parallel (~> 1.10)
parser (>= 3.1.0.0) parser (>= 3.1.0.0)
rainbow (>= 2.2.2, < 4.0) rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0) regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0) rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.18.0, < 2.0) rubocop-ast (>= 1.19.1, < 2.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 3.0) unicode-display_width (>= 1.4.0, < 3.0)
rubocop-ast (1.19.1) rubocop-ast (1.19.1)
parser (>= 3.1.1.0) parser (>= 3.1.1.0)
rubocop-performance (1.14.2) rubocop-performance (1.14.3)
rubocop (>= 1.7.0, < 2.0) rubocop (>= 1.7.0, < 2.0)
rubocop-ast (>= 0.4.0) rubocop-ast (>= 0.4.0)
rubocop-rails (2.15.2) rubocop-rails (2.15.2)
@ -159,14 +159,13 @@ GEM
simplecov (~> 0.19) simplecov (~> 0.19)
simplecov-html (0.12.3) simplecov-html (0.12.3)
simplecov_json_formatter (0.1.4) simplecov_json_formatter (0.1.4)
sorbet (0.5.10160) sorbet (0.5.10175)
sorbet-static (= 0.5.10160) sorbet-static (= 0.5.10175)
sorbet-runtime (0.5.10160) sorbet-runtime (0.5.10175)
sorbet-runtime-stub (0.2.0) sorbet-static (0.5.10175-universal-darwin-14)
sorbet-static (0.5.10160-universal-darwin-14) sorbet-static-and-runtime (0.5.10175)
sorbet-static-and-runtime (0.5.10160) sorbet (= 0.5.10175)
sorbet (= 0.5.10160) sorbet-runtime (= 0.5.10175)
sorbet-runtime (= 0.5.10160)
spoom (1.1.11) spoom (1.1.11)
sorbet (>= 0.5.9204) sorbet (>= 0.5.9204)
sorbet-runtime (>= 0.5.9204) sorbet-runtime (>= 0.5.9204)
@ -181,7 +180,7 @@ GEM
thor (>= 1.2.0) thor (>= 1.2.0)
yard-sorbet yard-sorbet
thor (1.2.1) thor (1.2.1)
tzinfo (2.0.4) tzinfo (2.0.5)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
@ -205,7 +204,7 @@ PLATFORMS
ruby ruby
DEPENDENCIES DEPENDENCIES
activesupport (< 7) activesupport
addressable addressable
bootsnap bootsnap
byebug byebug
@ -235,10 +234,13 @@ DEPENDENCIES
ruby-macho ruby-macho
simplecov simplecov
simplecov-cobertura simplecov-cobertura
sorbet-runtime-stub sorbet-runtime
sorbet-static-and-runtime sorbet-static-and-runtime
tapioca tapioca
warning warning
RUBY VERSION
ruby 2.6.8p205
BUNDLED WITH BUNDLED WITH
1.17.3 1.17.3

View File

@ -30,10 +30,8 @@ module Homebrew
def all_formulae def all_formulae
@all_formulae ||= begin @all_formulae ||= begin
curl_args = %w[--compressed --silent https://formulae.brew.sh/api/formula.json] curl_args = %w[--compressed --silent https://formulae.brew.sh/api/formula.json]
if cached_formula_json_file.exist? if cached_formula_json_file.exist? && !cached_formula_json_file.empty?
last_modified = cached_formula_json_file.mtime.utc curl_args.prepend("--time-cond", cached_formula_json_file)
last_modified = last_modified.strftime("%a, %d %b %Y %H:%M:%S GMT")
curl_args = ["--header", "If-Modified-Since: #{last_modified}", *curl_args]
end end
curl_download(*curl_args, to: HOMEBREW_CACHE_API/"#{formula_api_path}.json", max_time: 5) curl_download(*curl_args, to: HOMEBREW_CACHE_API/"#{formula_api_path}.json", max_time: 5)

View File

@ -103,7 +103,15 @@ begin
possible_tap = OFFICIAL_CMD_TAPS.find { |_, cmds| cmds.include?(cmd) } possible_tap = OFFICIAL_CMD_TAPS.find { |_, cmds| cmds.include?(cmd) }
possible_tap = Tap.fetch(possible_tap.first) if possible_tap possible_tap = Tap.fetch(possible_tap.first) if possible_tap
if !possible_tap || possible_tap.installed? || Tap.untapped_official_taps.include?(possible_tap.name) if !possible_tap ||
possible_tap.installed? ||
(blocked_tap = Tap.untapped_official_taps.include?(possible_tap.name))
if blocked_tap
onoe <<~EOS
`brew #{cmd}` is unavailable because #{possible_tap.name} was manually untapped.
Run `brew tap #{possible_tap.name}` to reenable `brew #{cmd}`.
EOS
end
# Check for cask explicitly because it's very common in old guides # Check for cask explicitly because it's very common in old guides
odie "`brew cask` is no longer a `brew` command. Use `brew <command> --cask` instead." if cmd == "cask" odie "`brew cask` is no longer a `brew` command. Use `brew <command> --cask` instead." if cmd == "cask"
odie "Unknown command: #{cmd}" odie "Unknown command: #{cmd}"

View File

@ -219,7 +219,7 @@ module Cask
end end
alias == eql? alias == eql?
def to_hash def to_h
{ {
"token" => token, "token" => token,
"full_token" => full_name, "full_token" => full_name,
@ -243,8 +243,8 @@ module Cask
} }
end end
def to_h def to_hash_with_variations
hash = to_hash hash = to_h
variations = {} variations = {}
hash_keys_to_skip = %w[outdated installed versions] hash_keys_to_skip = %w[outdated installed versions]
@ -252,21 +252,20 @@ module Cask
if @dsl.on_system_blocks_exist? if @dsl.on_system_blocks_exist?
[:arm, :intel].each do |arch| [:arm, :intel].each do |arch|
MacOSVersions::SYMBOLS.each_key do |os_name| MacOSVersions::SYMBOLS.each_key do |os_name|
# Big Sur is the first version of macOS that supports arm bottle_tag = ::Utils::Bottles::Tag.new(system: os_name, arch: arch)
next if arch == :arm && MacOS::Version.from_symbol(os_name) < MacOS::Version.from_symbol(:big_sur) next unless bottle_tag.valid_combination?
Homebrew::SimulateSystem.os = os_name Homebrew::SimulateSystem.os = os_name
Homebrew::SimulateSystem.arch = arch Homebrew::SimulateSystem.arch = arch
refresh refresh
bottle_tag = ::Utils::Bottles::Tag.new(system: os_name, arch: arch).to_sym to_h.each do |key, value|
to_hash.each do |key, value|
next if hash_keys_to_skip.include? key next if hash_keys_to_skip.include? key
next if value.to_s == hash[key].to_s next if value.to_s == hash[key].to_s
variations[bottle_tag] ||= {} variations[bottle_tag.to_sym] ||= {}
variations[bottle_tag][key] = value variations[bottle_tag.to_sym][key] = value
end end
end end
end end

View File

@ -61,6 +61,9 @@ module Homebrew
switch "--all", switch "--all",
depends_on: "--json", depends_on: "--json",
description: "Print JSON of all available formulae." description: "Print JSON of all available formulae."
switch "--variations",
depends_on: "--json",
description: "Include the variations hash in each formula's JSON output."
switch "-v", "--verbose", switch "-v", "--verbose",
description: "Show more verbose analytics data for <formula>." description: "Show more verbose analytics data for <formula>."
switch "--formula", "--formulae", switch "--formula", "--formulae",
@ -202,6 +205,8 @@ module Homebrew
if args.bottle? if args.bottle?
formulae.map(&:to_recursive_bottle_hash) formulae.map(&:to_recursive_bottle_hash)
elsif args.variations?
formulae.map(&:to_hash_with_variations)
else else
formulae.map(&:to_hash) formulae.map(&:to_hash)
end end
@ -216,6 +221,11 @@ module Homebrew
if args.bottle? if args.bottle?
{ "formulae" => formulae.map(&:to_recursive_bottle_hash) } { "formulae" => formulae.map(&:to_recursive_bottle_hash) }
elsif args.variations?
{
"formulae" => formulae.map(&:to_hash_with_variations),
"casks" => casks.map(&:to_hash_with_variations),
}
else else
{ {
"formulae" => formulae.map(&:to_hash), "formulae" => formulae.map(&:to_hash),

View File

@ -88,7 +88,7 @@ module Homebrew
def tests def tests
args = tests_args.parse args = tests_args.parse
Homebrew.install_bundler_gems!(groups: ["sorbet"]) Homebrew.install_bundler_gems!
require "byebug" if args.byebug? require "byebug" if args.byebug?

View File

@ -37,6 +37,17 @@ module Superenv
paths paths
end end
def homebrew_extra_isystem_paths
paths = []
# Add paths for GCC headers when building against glibc@2.13 because we have to use -nostdinc.
if deps.any? { |d| d.name == "glibc@2.13" }
gcc_include_dir = Utils.safe_popen_read(cc, "--print-file-name=include").chomp
gcc_include_fixed_dir = Utils.safe_popen_read(cc, "--print-file-name=include-fixed").chomp
paths << gcc_include_dir << gcc_include_fixed_dir
end
paths
end
def determine_rpath_paths(formula) def determine_rpath_paths(formula)
PATH.new( PATH.new(
*formula&.lib, *formula&.lib,

View File

@ -2005,6 +2005,45 @@ class Formula
hsh hsh
end end
# @private
def to_hash_with_variations
hash = to_hash
variations = {}
os_versions = [*MacOSVersions::SYMBOLS.keys, :linux]
if path.exist? && self.class.on_system_blocks_exist?
formula_contents = path.read
[:arm, :intel].each do |arch|
os_versions.each do |os_name|
bottle_tag = Utils::Bottles::Tag.new(system: os_name, arch: arch)
next unless bottle_tag.valid_combination?
Homebrew::SimulateSystem.os = os_name
Homebrew::SimulateSystem.arch = arch
variations_namespace = Formulary.class_s("Variations#{bottle_tag.to_sym.capitalize}")
variations_formula_class = Formulary.load_formula(name, path, formula_contents, variations_namespace,
flags: self.class.build_flags, ignore_errors: true)
variations_formula = variations_formula_class.new(name, path, :stable,
alias_path: alias_path, force_bottle: force_bottle)
variations_formula.to_hash.each do |key, value|
next if value.to_s == hash[key].to_s
variations[bottle_tag.to_sym] ||= {}
variations[bottle_tag.to_sym][key] = value
end
end
end
end
Homebrew::SimulateSystem.clear
hash["variations"] = variations
hash
end
# @api private # @api private
# Generate a hash to be used to install a formula from a JSON file # Generate a hash to be used to install a formula from a JSON file
def to_recursive_bottle_hash(top_level: true) def to_recursive_bottle_hash(top_level: true)
@ -2469,6 +2508,7 @@ class Formula
# The methods below define the formula DSL. # The methods below define the formula DSL.
class << self class << self
extend Predicable
include BuildEnvironment::DSL include BuildEnvironment::DSL
include OnSystem::MacOSAndLinux include OnSystem::MacOSAndLinux
@ -2483,6 +2523,11 @@ class Formula
end end
end end
# Whether this formula contains OS/arch-specific blocks
# (e.g. `on_macos`, `on_arm`, `on_monterey :or_older`, `on_system :linux, macos: :big_sur_or_newer`).
# @private
attr_predicate :on_system_blocks_exist?
# The reason for why this software is not linked (by default) to # The reason for why this software is not linked (by default) to
# {::HOMEBREW_PREFIX}. # {::HOMEBREW_PREFIX}.
# @private # @private
@ -3170,6 +3215,8 @@ class Formula
# Permit links to certain libraries that don't exist. Available on Linux only. # Permit links to certain libraries that don't exist. Available on Linux only.
def ignore_missing_libraries(*) def ignore_missing_libraries(*)
return if Homebrew::SimulateSystem.linux?
raise FormulaSpecificationError, "#{__method__} is available on Linux only" raise FormulaSpecificationError, "#{__method__} is available on Linux only"
end end

View File

@ -49,6 +49,7 @@ class Formula
def env; end def env; end
def conflicts; end def conflicts; end
def self.on_system_blocks_exist?; end
# This method is included by `OnSystem` # This method is included by `OnSystem`
def self.on_macos(&block); end def self.on_macos(&block); end
end end

View File

@ -264,6 +264,7 @@ module Formulary
end end
def self.convert_to_deprecate_disable_reason_string_or_symbol(string) def self.convert_to_deprecate_disable_reason_string_or_symbol(string)
require "deprecate_disable"
return string unless DeprecateDisable::DEPRECATE_DISABLE_REASONS.keys.map(&:to_s).include?(string) return string unless DeprecateDisable::DEPRECATE_DISABLE_REASONS.keys.map(&:to_s).include?(string)
string.to_sym string.to_sym

View File

@ -198,7 +198,7 @@ class LinkageChecker
# In macOS Big Sur and later, system libraries do not exist on-disk and instead exist in a cache. # In macOS Big Sur and later, system libraries do not exist on-disk and instead exist in a cache.
# If dlopen finds the dylib, then the linkage is not broken. # If dlopen finds the dylib, then the linkage is not broken.
@system_dylibs << dylib @system_dylibs << dylib
else elsif !system_framework?(dylib)
@broken_dylibs << dylib @broken_dylibs << dylib
end end
else else
@ -306,11 +306,13 @@ class LinkageChecker
def harmless_broken_link?(dylib) def harmless_broken_link?(dylib)
# libgcc_s_* is referenced by programs that use the Java Service Wrapper, # libgcc_s_* is referenced by programs that use the Java Service Wrapper,
# and is harmless on x86(_64) machines # and is harmless on x86(_64) machines
return true if [ [
"/usr/lib/libgcc_s_ppc64.1.dylib", "/usr/lib/libgcc_s_ppc64.1.dylib",
"/opt/local/lib/libgcc/libgcc_s.1.dylib", "/opt/local/lib/libgcc/libgcc_s.1.dylib",
].include?(dylib) ].include?(dylib)
end
def system_framework?(dylib)
dylib.start_with?("/System/Library/Frameworks/") dylib.start_with?("/System/Library/Frameworks/")
end end

View File

@ -376,6 +376,14 @@ module Homebrew
base[:StartCalendarInterval] = @cron.reject { |_, value| value == "*" } base[:StartCalendarInterval] = @cron.reject { |_, value| value == "*" }
end end
# Adding all session types has as the primary effect that if you initialise it through e.g. a Background session
# and you later "physically" sign in to the owning account (Aqua session), things shouldn't flip out.
# Also, we're not checking @process_type here because that is used to indicate process priority and not
# necessarily if it should run in a specific session type. Like database services could run with ProcessType
# Interactive so they have no resource limitations enforced upon them, but they aren't really interactive in the
# general sense.
base[:LimitLoadToSessionType] = %w[Aqua Background LoginWindow StandardIO System]
base.to_plist base.to_plist
end end

View File

@ -304,7 +304,11 @@ class Cmd
end end
def cppflags def cppflags
path_flags("-isystem", isystem_paths) + path_flags("-I", include_paths) args = []
args += path_flags("-isystem", isystem_paths) + path_flags("-I", include_paths)
# Add -nostdinc when building against glibc@2.13 to avoid mixing system and brewed glibc headers.
args << "-nostdinc" if @deps.include?("glibc@2.13")
args
end end
def ldflags_mac(args) def ldflags_mac(args)
@ -322,10 +326,17 @@ class Cmd
def ldflags_linux(args) def ldflags_linux(args)
unless mode == :ld unless mode == :ld
wl = "-Wl," wl = "-Wl,"
args << "-B#{@opt}/glibc/lib" if @deps.include?("glibc@2.13")
args << "-B#{@opt}/glibc@2.13/lib"
else
args << "-B#{@opt}/glibc/lib"
end
end end
args += rpath_flags("#{wl}-rpath=", rpath_paths) args += rpath_flags("#{wl}-rpath=", rpath_paths)
args += ["#{wl}--dynamic-linker=#{dynamic_linker_path}"] if dynamic_linker_path args += ["#{wl}--dynamic-linker=#{dynamic_linker_path}"] if dynamic_linker_path
# Use -rpath-link to make sure linker uses glibc@2.13 rather than the system glibc for indirect
# dependencies because -L will only handle direct dependencies.
args << "#{wl}-rpath-link=#{@opt}/glibc@2.13/lib" if @deps.include?("glibc@2.13")
args args
end end

View File

@ -205,6 +205,8 @@ class SoftwareSpec
def patch(strip = :p1, src = nil, &block) def patch(strip = :p1, src = nil, &block)
p = Patch.create(strip, src, &block) p = Patch.create(strip, src, &block)
return if p.is_a?(ExternalPatch) && p.url.blank?
dependency_collector.add(p.resource) if p.is_a? ExternalPatch dependency_collector.add(p.resource) if p.is_a? ExternalPatch
patches << p patches << p
end end

View File

@ -6,6 +6,7 @@
module RuboCop; end module RuboCop; end
module RuboCop::Cop; end module RuboCop::Cop; end
RuboCop::Cop::IgnoredPattern = RuboCop::Cop::AllowedPattern
module RuboCop::Cop::Performance; end module RuboCop::Cop::Performance; end
class RuboCop::Cop::Performance::AncestorsInclude < ::RuboCop::Cop::Base class RuboCop::Cop::Performance::AncestorsInclude < ::RuboCop::Cop::Base

View File

@ -3157,13 +3157,15 @@ class RuboCop::Cop::Layout::LineContinuationLeadingSpace < ::RuboCop::Cop::Base
private private
def continuation?(line); end def continuation?(line); end
def investigate(first_line, second_line, range_start); end def enforced_style_leading?; end
def offense_range(range_start, matches); end def investigate_leading_style(first_line, end_of_first_line); end
def investigate_trailing_style(second_line, end_of_first_line); end
def leading_offense_range(end_of_first_line, matches); end
def message(_range); end
def raw_lines(node); end def raw_lines(node); end
def trailing_offense_range(end_of_first_line, matches); end
end end
RuboCop::Cop::Layout::LineContinuationLeadingSpace::MSG = T.let(T.unsafe(nil), String)
class RuboCop::Cop::Layout::LineContinuationSpacing < ::RuboCop::Cop::Base class RuboCop::Cop::Layout::LineContinuationSpacing < ::RuboCop::Cop::Base
include ::RuboCop::Cop::RangeHelp include ::RuboCop::Cop::RangeHelp
extend ::RuboCop::Cop::AutoCorrector extend ::RuboCop::Cop::AutoCorrector
@ -3216,6 +3218,7 @@ class RuboCop::Cop::Layout::LineLength < ::RuboCop::Cop::Base
def max=(value); end def max=(value); end
def on_array(node); end def on_array(node); end
def on_block(node); end def on_block(node); end
def on_def(node); end
def on_hash(node); end def on_hash(node); end
def on_investigation_end; end def on_investigation_end; end
def on_new_investigation; end def on_new_investigation; end
@ -3417,6 +3420,15 @@ RuboCop::Cop::Layout::MultilineMethodDefinitionBraceLayout::ALWAYS_SAME_LINE_MES
RuboCop::Cop::Layout::MultilineMethodDefinitionBraceLayout::NEW_LINE_MESSAGE = T.let(T.unsafe(nil), String) RuboCop::Cop::Layout::MultilineMethodDefinitionBraceLayout::NEW_LINE_MESSAGE = T.let(T.unsafe(nil), String)
RuboCop::Cop::Layout::MultilineMethodDefinitionBraceLayout::SAME_LINE_MESSAGE = T.let(T.unsafe(nil), String) RuboCop::Cop::Layout::MultilineMethodDefinitionBraceLayout::SAME_LINE_MESSAGE = T.let(T.unsafe(nil), String)
class RuboCop::Cop::Layout::MultilineMethodParameterLineBreaks < ::RuboCop::Cop::Base
include ::RuboCop::Cop::MultilineElementLineBreaks
extend ::RuboCop::Cop::AutoCorrector
def on_def(node); end
end
RuboCop::Cop::Layout::MultilineMethodParameterLineBreaks::MSG = T.let(T.unsafe(nil), String)
class RuboCop::Cop::Layout::MultilineOperationIndentation < ::RuboCop::Cop::Base class RuboCop::Cop::Layout::MultilineOperationIndentation < ::RuboCop::Cop::Base
include ::RuboCop::Cop::ConfigurableEnforcedStyle include ::RuboCop::Cop::ConfigurableEnforcedStyle
include ::RuboCop::Cop::Alignment include ::RuboCop::Cop::Alignment
@ -5128,14 +5140,21 @@ class RuboCop::Cop::Lint::NonAtomicFileOperation < ::RuboCop::Cop::Base
def allowable_use_with_if?(if_node); end def allowable_use_with_if?(if_node); end
def autocorrect(corrector, node, range); end def autocorrect(corrector, node, range); end
def autocorrect_replace_method(corrector, node); end
def force_method?(node); end
def force_method_name?(node); end
def force_option?(node); end def force_option?(node); end
def message(node); end def if_node_child?(node); end
def message_remove_file_exist_check(node); end
def register_offense(node, exist_node); end def register_offense(node, exist_node); end
def replacement_method(node); end def replacement_method(node); end
end end
RuboCop::Cop::Lint::NonAtomicFileOperation::MAKE_FORCE_METHODS = T.let(T.unsafe(nil), Array)
RuboCop::Cop::Lint::NonAtomicFileOperation::MAKE_METHODS = T.let(T.unsafe(nil), Array) RuboCop::Cop::Lint::NonAtomicFileOperation::MAKE_METHODS = T.let(T.unsafe(nil), Array)
RuboCop::Cop::Lint::NonAtomicFileOperation::MSG = T.let(T.unsafe(nil), String) RuboCop::Cop::Lint::NonAtomicFileOperation::MSG_CHANGE_FORCE_METHOD = T.let(T.unsafe(nil), String)
RuboCop::Cop::Lint::NonAtomicFileOperation::MSG_REMOVE_FILE_EXIST_CHECK = T.let(T.unsafe(nil), String)
RuboCop::Cop::Lint::NonAtomicFileOperation::REMOVE_FORCE_METHODS = T.let(T.unsafe(nil), Array)
RuboCop::Cop::Lint::NonAtomicFileOperation::REMOVE_METHODS = T.let(T.unsafe(nil), Array) RuboCop::Cop::Lint::NonAtomicFileOperation::REMOVE_METHODS = T.let(T.unsafe(nil), Array)
RuboCop::Cop::Lint::NonAtomicFileOperation::RESTRICT_ON_SEND = T.let(T.unsafe(nil), Array) RuboCop::Cop::Lint::NonAtomicFileOperation::RESTRICT_ON_SEND = T.let(T.unsafe(nil), Array)
@ -5554,6 +5573,13 @@ end
RuboCop::Cop::Lint::RequireParentheses::MSG = T.let(T.unsafe(nil), String) RuboCop::Cop::Lint::RequireParentheses::MSG = T.let(T.unsafe(nil), String)
class RuboCop::Cop::Lint::RequireRangeParentheses < ::RuboCop::Cop::Base
def on_erange(node); end
def on_irange(node); end
end
RuboCop::Cop::Lint::RequireRangeParentheses::MSG = T.let(T.unsafe(nil), String)
class RuboCop::Cop::Lint::RequireRelativeSelfPath < ::RuboCop::Cop::Base class RuboCop::Cop::Lint::RequireRelativeSelfPath < ::RuboCop::Cop::Base
include ::RuboCop::Cop::RangeHelp include ::RuboCop::Cop::RangeHelp
extend ::RuboCop::Cop::AutoCorrector extend ::RuboCop::Cop::AutoCorrector
@ -5605,6 +5631,7 @@ RuboCop::Cop::Lint::ReturnInVoidContext::MSG = T.let(T.unsafe(nil), String)
class RuboCop::Cop::Lint::SafeNavigationChain < ::RuboCop::Cop::Base class RuboCop::Cop::Lint::SafeNavigationChain < ::RuboCop::Cop::Base
include ::RuboCop::Cop::AllowedMethods include ::RuboCop::Cop::AllowedMethods
include ::RuboCop::Cop::NilMethods include ::RuboCop::Cop::NilMethods
extend ::RuboCop::Cop::AutoCorrector
extend ::RuboCop::Cop::TargetRubyVersion extend ::RuboCop::Cop::TargetRubyVersion
def bad_method?(param0 = T.unsafe(nil)); end def bad_method?(param0 = T.unsafe(nil)); end
@ -5612,6 +5639,8 @@ class RuboCop::Cop::Lint::SafeNavigationChain < ::RuboCop::Cop::Base
private private
def add_safe_navigation_operator(offense_range:, send_node:); end
def autocorrect(corrector, offense_range:, send_node:); end
def method_chain(node); end def method_chain(node); end
end end
@ -7127,11 +7156,16 @@ module RuboCop::Cop::PercentArray
private private
def allowed_bracket_array?(node); end def allowed_bracket_array?(node); end
def build_bracketed_array_with_appropriate_whitespace(elements:, node:); end
def build_message_for_bracketed_array(preferred_array_code); end
def check_bracketed_array(node, literal_prefix); end def check_bracketed_array(node, literal_prefix); end
def check_percent_array(node); end def check_percent_array(node); end
def comments_in_array?(node); end def comments_in_array?(node); end
def invalid_percent_array_contents?(_node); end def invalid_percent_array_contents?(_node); end
def invalid_percent_array_context?(node); end def invalid_percent_array_context?(node); end
def whitespace_between(node); end
def whitespace_leading(node); end
def whitespace_trailing(node); end
end end
module RuboCop::Cop::PercentLiteral module RuboCop::Cop::PercentLiteral
@ -8551,6 +8585,16 @@ end
RuboCop::Cop::Style::EmptyElse::MSG = T.let(T.unsafe(nil), String) RuboCop::Cop::Style::EmptyElse::MSG = T.let(T.unsafe(nil), String)
class RuboCop::Cop::Style::EmptyHeredoc < ::RuboCop::Cop::Base
include ::RuboCop::Cop::Heredoc
include ::RuboCop::Cop::RangeHelp
extend ::RuboCop::Cop::AutoCorrector
def on_heredoc(node); end
end
RuboCop::Cop::Style::EmptyHeredoc::MSG = T.let(T.unsafe(nil), String)
class RuboCop::Cop::Style::EmptyLambdaParameter < ::RuboCop::Cop::Base class RuboCop::Cop::Style::EmptyLambdaParameter < ::RuboCop::Cop::Base
include ::RuboCop::Cop::EmptyParameter include ::RuboCop::Cop::EmptyParameter
include ::RuboCop::Cop::RangeHelp include ::RuboCop::Cop::RangeHelp
@ -8791,47 +8835,25 @@ RuboCop::Cop::Style::ExponentialNotation::MESSAGES = T.let(T.unsafe(nil), Hash)
class RuboCop::Cop::Style::FetchEnvVar < ::RuboCop::Cop::Base class RuboCop::Cop::Style::FetchEnvVar < ::RuboCop::Cop::Base
extend ::RuboCop::Cop::AutoCorrector extend ::RuboCop::Cop::AutoCorrector
def block_control?(param0 = T.unsafe(nil)); end
def env_with_bracket?(param0 = T.unsafe(nil)); end def env_with_bracket?(param0 = T.unsafe(nil)); end
def offensive_nodes(param0); end
def on_send(node); end def on_send(node); end
def operand_of_or?(param0 = T.unsafe(nil)); end
private private
def allowable_use?(node); end def allowable_use?(node); end
def allowed_var?(node); end def allowed_var?(node); end
def assigned?(node); end def assigned?(node); end
def configured_indentation; end
def conterpart_rhs_of(node); end
def default_nil(node, name_node); end
def default_rhs(node, name_node); end
def default_rhs_in_outer_or(node, name_node); end
def default_rhs_in_same_or(node, name_node); end
def default_to_rhs?(node); end
def first_line_of(source); end
def left_end_of_or_chains?(node); end
def message_chained_with_dot?(node); end def message_chained_with_dot?(node); end
def message_template_for(rhs); end def new_code(name_node); end
def new_code_default_nil(name_node); end
def new_code_default_rhs(node, name_node); end
def new_code_default_rhs_multiline(node, name_node); end
def new_code_default_rhs_single_line(node, name_node); end
def offensive?(node); end def offensive?(node); end
def or_chain_root(node); end def or_lhs?(node); end
def partial_matched?(node, condition); end def partial_matched?(node, condition); end
def rhs_can_be_default_value?(node); end
def rhs_is_block_control?(node); end
def right_end_of_or_chains?(node); end
def used_as_flag?(node); end def used_as_flag?(node); end
def used_if_condition_in_body(node); end def used_if_condition_in_body(node); end
def used_in_condition?(node, condition); end def used_in_condition?(node, condition); end
end end
RuboCop::Cop::Style::FetchEnvVar::MSG_DEFAULT_NIL = T.let(T.unsafe(nil), String) RuboCop::Cop::Style::FetchEnvVar::MSG = T.let(T.unsafe(nil), String)
RuboCop::Cop::Style::FetchEnvVar::MSG_DEFAULT_RHS_MULTILINE_BLOCK = T.let(T.unsafe(nil), String)
RuboCop::Cop::Style::FetchEnvVar::MSG_DEFAULT_RHS_SECOND_ARG_OF_FETCH = T.let(T.unsafe(nil), String)
RuboCop::Cop::Style::FetchEnvVar::MSG_DEFAULT_RHS_SINGLE_LINE_BLOCK = T.let(T.unsafe(nil), String)
class RuboCop::Cop::Style::FileRead < ::RuboCop::Cop::Base class RuboCop::Cop::Style::FileRead < ::RuboCop::Cop::Base
include ::RuboCop::Cop::RangeHelp include ::RuboCop::Cop::RangeHelp
@ -11453,8 +11475,10 @@ class RuboCop::Cop::Style::Semicolon < ::RuboCop::Cop::Base
def check_for_line_terminator_or_opener; end def check_for_line_terminator_or_opener; end
def each_semicolon; end def each_semicolon; end
def expressions_per_line(exprs); end def expressions_per_line(exprs); end
def find_range_node(token_before_semicolon); end
def find_semicolon_positions(line); end def find_semicolon_positions(line); end
def register_semicolon(line, column, after_expression); end def range_nodes; end
def register_semicolon(line, column, after_expression, token_before_semicolon = T.unsafe(nil)); end
def tokens_for_lines; end def tokens_for_lines; end
class << self class << self
@ -11971,6 +11995,7 @@ class RuboCop::Cop::Style::TopLevelMethodDefinition < ::RuboCop::Cop::Base
end end
RuboCop::Cop::Style::TopLevelMethodDefinition::MSG = T.let(T.unsafe(nil), String) RuboCop::Cop::Style::TopLevelMethodDefinition::MSG = T.let(T.unsafe(nil), String)
RuboCop::Cop::Style::TopLevelMethodDefinition::RESTRICT_ON_SEND = T.let(T.unsafe(nil), Array)
class RuboCop::Cop::Style::TrailingBodyOnClass < ::RuboCop::Cop::Base class RuboCop::Cop::Style::TrailingBodyOnClass < ::RuboCop::Cop::Base
include ::RuboCop::Cop::Alignment include ::RuboCop::Cop::Alignment

View File

@ -4,7 +4,11 @@
# This is an autogenerated file for types exported from the `tzinfo` gem. # This is an autogenerated file for types exported from the `tzinfo` gem.
# Please instead update this file by running `bin/tapioca gem tzinfo`. # Please instead update this file by running `bin/tapioca gem tzinfo`.
module TZInfo; end module TZInfo
class << self
def eager_load!; end
end
end
class TZInfo::AbsoluteDayOfYearTransitionRule < ::TZInfo::DayOfYearTransitionRule class TZInfo::AbsoluteDayOfYearTransitionRule < ::TZInfo::DayOfYearTransitionRule
def initialize(day, transition_at = T.unsafe(nil)); end def initialize(day, transition_at = T.unsafe(nil)); end
@ -94,6 +98,7 @@ class TZInfo::DataSource
def country_codes; end def country_codes; end
def data_timezone_identifiers; end def data_timezone_identifiers; end
def eager_load!; end
def get_country_info(code); end def get_country_info(code); end
def get_timezone_info(identifier); end def get_timezone_info(identifier); end
def inspect; end def inspect; end
@ -275,6 +280,7 @@ end
TZInfo::DataSources::ZoneinfoDataSource::DEFAULT_ALTERNATE_ISO3166_TAB_SEARCH_PATH = T.let(T.unsafe(nil), Array) TZInfo::DataSources::ZoneinfoDataSource::DEFAULT_ALTERNATE_ISO3166_TAB_SEARCH_PATH = T.let(T.unsafe(nil), Array)
TZInfo::DataSources::ZoneinfoDataSource::DEFAULT_SEARCH_PATH = T.let(T.unsafe(nil), Array) TZInfo::DataSources::ZoneinfoDataSource::DEFAULT_SEARCH_PATH = T.let(T.unsafe(nil), Array)
TZInfo::DataSources::ZoneinfoDataSource::EXCLUDED_FILENAMES = T.let(T.unsafe(nil), Array)
class TZInfo::DataSources::ZoneinfoDirectoryNotFound < ::StandardError; end class TZInfo::DataSources::ZoneinfoDirectoryNotFound < ::StandardError; end
class TZInfo::DataSources::ZoneinfoReader class TZInfo::DataSources::ZoneinfoReader

View File

@ -1,11 +1,47 @@
# typed: true # typed: true
# frozen_string_literal: true # frozen_string_literal: true
# Explicitly prevent `sorbet-runtime` from being loaded. require "sorbet-runtime"
def gem(name, *)
raise Gem::LoadError if name == "sorbet-runtime"
super # Disable runtime checking unless enabled.
# In the future we should consider not doing this monkey patch,
# if assured that there is no performance hit from removing this.
# There are mechanisms to achieve a middle ground (`default_checked_level`).
unless ENV["HOMEBREW_SORBET_RUNTIME"]
# Redefine T.let etc to make the `checked` parameter default to false rather than true.
# @private
module TNoChecks
def cast(value, type, checked: false)
super(value, type, checked: checked)
end
def let(value, type, checked: false)
super(value, type, checked: checked)
end
def bind(value, type, checked: false)
super(value, type, checked: checked)
end
def assert_type!(value, type, checked: false)
super(value, type, checked: checked)
end
end
# @private
module T
class << self
prepend TNoChecks
end
# Redefine T.sig to be noop.
# @private
module Sig
def sig(arg0 = nil, &blk); end
end
end
# For any cases the above doesn't handle: make sure we don't let TypeError slip through.
T::Configuration.call_validation_error_handler = ->(signature, opts) do end
T::Configuration.inline_type_error_handler = ->(error, opts) do end
end end
require "sorbet-runtime-stub"

View File

@ -7,4 +7,4 @@ require_relative "standalone/load_path"
require_relative "startup/ruby_path" require_relative "startup/ruby_path"
require "startup/config" require "startup/config"
require_relative "startup/bootsnap" require_relative "startup/bootsnap"
require_relative "startup/sorbet" require_relative "standalone/sorbet"

View File

@ -1,10 +0,0 @@
# typed: strict
# frozen_string_literal: true
if ENV["HOMEBREW_SORBET_RUNTIME"]
# This is only supported under the brew environment.
Homebrew.install_bundler_gems!(groups: ["sorbet"])
require "sorbet-runtime"
else
require "standalone/sorbet"
end

View File

@ -211,4 +211,64 @@ describe Cask::Cask, :cask do
end end
end end
end end
describe "#to_hash_with_variations" do
let!(:original_macos_version) { MacOS.full_version.to_s }
let(:expected_variations) {
<<~JSON
{
"arm64_big_sur": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine/1.2.0/arm.zip",
"version": "1.2.0",
"sha256": "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"
},
"monterey": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine/1.2.3/intel.zip"
},
"big_sur": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine/1.2.0/intel.zip",
"version": "1.2.0",
"sha256": "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"
},
"catalina": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine/1.0.0/intel.zip",
"version": "1.0.0",
"sha256": "1866dfa833b123bb8fe7fa7185ebf24d28d300d0643d75798bc23730af734216"
},
"mojave": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine/1.0.0/intel.zip",
"version": "1.0.0",
"sha256": "1866dfa833b123bb8fe7fa7185ebf24d28d300d0643d75798bc23730af734216"
}
}
JSON
}
before do
# Use a more limited symbols list to shorten the variations hash
symbols = {
monterey: "12",
big_sur: "11",
catalina: "10.15",
mojave: "10.14",
}
stub_const("MacOSVersions::SYMBOLS", symbols)
# For consistency, always run on Monterey and ARM
MacOS.full_version = "12"
allow(Hardware::CPU).to receive(:type).and_return(:arm)
end
after do
MacOS.full_version = original_macos_version
end
it "returns the correct variations hash" do
c = Cask::CaskLoader.load("multiple-versions")
h = c.to_hash_with_variations
expect(h).to be_a(Hash)
expect(JSON.pretty_generate(h["variations"])).to eq expected_variations.strip
end
end
end end

View File

@ -120,9 +120,7 @@ describe Cask::Cmd::List, :cask do
}, },
"conflicts_with": null, "conflicts_with": null,
"container": null, "container": null,
"auto_updates": null, "auto_updates": null
"variations": {
}
}, },
{ {
"token": "local-transmission", "token": "local-transmission",
@ -151,9 +149,7 @@ describe Cask::Cmd::List, :cask do
}, },
"conflicts_with": null, "conflicts_with": null,
"container": null, "container": null,
"auto_updates": null, "auto_updates": null
"variations": {
}
}, },
{ {
"token": "multiple-versions", "token": "multiple-versions",
@ -185,32 +181,7 @@ describe Cask::Cmd::List, :cask do
}, },
"conflicts_with": null, "conflicts_with": null,
"container": null, "container": null,
"auto_updates": null, "auto_updates": null
"variations": {
"arm_big_sur": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine/1.2.0/arm.zip",
"version": "1.2.0",
"sha256": "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"
},
"intel_monterey": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine/1.2.3/intel.zip"
},
"intel_big_sur": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine/1.2.0/intel.zip",
"version": "1.2.0",
"sha256": "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"
},
"intel_catalina": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine/1.0.0/intel.zip",
"version": "1.0.0",
"sha256": "1866dfa833b123bb8fe7fa7185ebf24d28d300d0643d75798bc23730af734216"
},
"intel_mojave": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine/1.0.0/intel.zip",
"version": "1.0.0",
"sha256": "1866dfa833b123bb8fe7fa7185ebf24d28d300d0643d75798bc23730af734216"
}
}
}, },
{ {
"token": "third-party-cask", "token": "third-party-cask",
@ -239,9 +210,7 @@ describe Cask::Cmd::List, :cask do
}, },
"conflicts_with": null, "conflicts_with": null,
"container": null, "container": null,
"auto_updates": null, "auto_updates": null
"variations": {
}
} }
] ]
EOS EOS

View File

@ -905,6 +905,99 @@ describe Formula do
expect(h["versions"]["bottle"]).to be_truthy expect(h["versions"]["bottle"]).to be_truthy
end end
describe "#to_hash_with_variations", :needs_macos do
let(:formula_path) { CoreTap.new.formula_dir/"foo-variations.rb" }
let(:formula_content) do
<<~RUBY
class FooVariations < Formula
url "file://#{TEST_FIXTURE_DIR}/tarballs/testball-0.1.tbz"
sha256 TESTBALL_SHA256
on_intel do
depends_on "intel-formula"
end
on_big_sur do
depends_on "big-sur-formula"
end
on_catalina :or_older do
depends_on "catalina-or-older-formula"
end
on_linux do
depends_on "linux-formula"
end
end
RUBY
end
let(:expected_variations) {
<<~JSON
{
"arm64_big_sur": {
"dependencies": [
"big-sur-formula"
]
},
"monterey": {
"dependencies": [
"intel-formula"
]
},
"big_sur": {
"dependencies": [
"intel-formula",
"big-sur-formula"
]
},
"catalina": {
"dependencies": [
"intel-formula",
"catalina-or-older-formula"
]
},
"mojave": {
"dependencies": [
"intel-formula",
"catalina-or-older-formula"
]
},
"x86_64_linux": {
"dependencies": [
"intel-formula",
"linux-formula"
]
}
}
JSON
}
before do
# Use a more limited symbols list to shorten the variations hash
symbols = {
monterey: "12",
big_sur: "11",
catalina: "10.15",
mojave: "10.14",
}
stub_const("MacOSVersions::SYMBOLS", symbols)
# For consistency, always run on Monterey and ARM
allow(MacOS).to receive(:version).and_return(MacOS::Version.new("12"))
allow(Hardware::CPU).to receive(:type).and_return(:arm)
formula_path.dirname.mkpath
formula_path.write formula_content
end
it "returns the correct variations hash" do
h = Formulary.factory("foo-variations").to_hash_with_variations
expect(h).to be_a(Hash)
expect(JSON.pretty_generate(h["variations"])).to eq expected_variations.strip
end
end
specify "#to_recursive_bottle_hash" do specify "#to_recursive_bottle_hash" do
f1 = formula "foo" do f1 = formula "foo" do
url "foo-1.0" url "foo-1.0"

View File

@ -180,6 +180,14 @@ describe Homebrew::Service do
\t<true/> \t<true/>
\t<key>LegacyTimers</key> \t<key>LegacyTimers</key>
\t<true/> \t<true/>
\t<key>LimitLoadToSessionType</key>
\t<array>
\t\t<string>Aqua</string>
\t\t<string>Background</string>
\t\t<string>LoginWindow</string>
\t\t<string>StandardIO</string>
\t\t<string>System</string>
\t</array>
\t<key>ProcessType</key> \t<key>ProcessType</key>
\t<string>Interactive</string> \t<string>Interactive</string>
\t<key>ProgramArguments</key> \t<key>ProgramArguments</key>
@ -221,6 +229,14 @@ describe Homebrew::Service do
<dict> <dict>
\t<key>Label</key> \t<key>Label</key>
\t<string>homebrew.mxcl.formula_name</string> \t<string>homebrew.mxcl.formula_name</string>
\t<key>LimitLoadToSessionType</key>
\t<array>
\t\t<string>Aqua</string>
\t\t<string>Background</string>
\t\t<string>LoginWindow</string>
\t\t<string>StandardIO</string>
\t\t<string>System</string>
\t</array>
\t<key>ProgramArguments</key> \t<key>ProgramArguments</key>
\t<array> \t<array>
\t\t<string>#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd</string> \t\t<string>#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd</string>
@ -262,6 +278,14 @@ describe Homebrew::Service do
<dict> <dict>
\t<key>Label</key> \t<key>Label</key>
\t<string>homebrew.mxcl.formula_name</string> \t<string>homebrew.mxcl.formula_name</string>
\t<key>LimitLoadToSessionType</key>
\t<array>
\t\t<string>Aqua</string>
\t\t<string>Background</string>
\t\t<string>LoginWindow</string>
\t\t<string>StandardIO</string>
\t\t<string>System</string>
\t</array>
\t<key>ProgramArguments</key> \t<key>ProgramArguments</key>
\t<array> \t<array>
\t\t<string>#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd</string> \t\t<string>#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd</string>
@ -289,6 +313,14 @@ describe Homebrew::Service do
<dict> <dict>
\t<key>Label</key> \t<key>Label</key>
\t<string>homebrew.mxcl.formula_name</string> \t<string>homebrew.mxcl.formula_name</string>
\t<key>LimitLoadToSessionType</key>
\t<array>
\t\t<string>Aqua</string>
\t\t<string>Background</string>
\t\t<string>LoginWindow</string>
\t\t<string>StandardIO</string>
\t\t<string>System</string>
\t</array>
\t<key>ProgramArguments</key> \t<key>ProgramArguments</key>
\t<array> \t<array>
\t\t<string>#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd</string> \t\t<string>#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd</string>
@ -318,6 +350,14 @@ describe Homebrew::Service do
<dict> <dict>
\t<key>Label</key> \t<key>Label</key>
\t<string>homebrew.mxcl.formula_name</string> \t<string>homebrew.mxcl.formula_name</string>
\t<key>LimitLoadToSessionType</key>
\t<array>
\t\t<string>Aqua</string>
\t\t<string>Background</string>
\t\t<string>LoginWindow</string>
\t\t<string>StandardIO</string>
\t\t<string>System</string>
\t</array>
\t<key>ProgramArguments</key> \t<key>ProgramArguments</key>
\t<array> \t<array>
\t\t<string>#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd</string> \t\t<string>#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd</string>
@ -356,6 +396,14 @@ describe Homebrew::Service do
\t</dict> \t</dict>
\t<key>Label</key> \t<key>Label</key>
\t<string>homebrew.mxcl.formula_name</string> \t<string>homebrew.mxcl.formula_name</string>
\t<key>LimitLoadToSessionType</key>
\t<array>
\t\t<string>Aqua</string>
\t\t<string>Background</string>
\t\t<string>LoginWindow</string>
\t\t<string>StandardIO</string>
\t\t<string>System</string>
\t</array>
\t<key>ProgramArguments</key> \t<key>ProgramArguments</key>
\t<array> \t<array>
\t\t<string>#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd</string> \t\t<string>#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd</string>
@ -387,6 +435,14 @@ describe Homebrew::Service do
\t</dict> \t</dict>
\t<key>Label</key> \t<key>Label</key>
\t<string>homebrew.mxcl.formula_name</string> \t<string>homebrew.mxcl.formula_name</string>
\t<key>LimitLoadToSessionType</key>
\t<array>
\t\t<string>Aqua</string>
\t\t<string>Background</string>
\t\t<string>LoginWindow</string>
\t\t<string>StandardIO</string>
\t\t<string>System</string>
\t</array>
\t<key>ProgramArguments</key> \t<key>ProgramArguments</key>
\t<array> \t<array>
\t\t<string>#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd</string> \t\t<string>#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd</string>
@ -418,6 +474,14 @@ describe Homebrew::Service do
\t</dict> \t</dict>
\t<key>Label</key> \t<key>Label</key>
\t<string>homebrew.mxcl.formula_name</string> \t<string>homebrew.mxcl.formula_name</string>
\t<key>LimitLoadToSessionType</key>
\t<array>
\t\t<string>Aqua</string>
\t\t<string>Background</string>
\t\t<string>LoginWindow</string>
\t\t<string>StandardIO</string>
\t\t<string>System</string>
\t</array>
\t<key>ProgramArguments</key> \t<key>ProgramArguments</key>
\t<array> \t<array>
\t\t<string>#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd</string> \t\t<string>#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd</string>

View File

@ -171,5 +171,12 @@ describe SoftwareSpec do
expect(spec.patches.count).to eq(1) expect(spec.patches.count).to eq(1)
expect(spec.patches.first.strip).to eq(:p1) expect(spec.patches.first.strip).to eq(:p1)
end end
it "doesn't add a patch with no url" do
spec.patch do
sha256 "7852a7a365f518b12a1afd763a6a80ece88ac7aeea3c9023aa6c1fe46ac5a1ae"
end
expect(spec.patches.empty?).to be true
end
end end
end end

View File

@ -36,4 +36,37 @@ describe Utils::Bottles::Tag do
expect(tag.linux?).to be true expect(tag.linux?).to be true
expect(tag.to_sym).to eq(symbol) expect(tag.to_sym).to eq(symbol)
end end
describe "#standardized_arch" do
it "returns :x86_64 for :intel" do
expect(described_class.new(system: :all, arch: :intel).standardized_arch).to eq(:x86_64)
end
it "returns :arm64 for :arm" do
expect(described_class.new(system: :all, arch: :arm).standardized_arch).to eq(:arm64)
end
end
describe "#valid_combination?" do
it "returns true for intel archs" do
tag = described_class.new(system: :big_sur, arch: :intel)
expect(tag.valid_combination?).to be true
tag = described_class.new(system: :linux, arch: :x86_64)
expect(tag.valid_combination?).to be true
end
it "returns false for arm archs and macos versions older than big_sur" do
tag = described_class.new(system: :catalina, arch: :arm64)
expect(tag.valid_combination?).to be false
tag = described_class.new(system: :mojave, arch: :arm)
expect(tag.valid_combination?).to be false
end
it "returns false for arm archs and linux" do
tag = described_class.new(system: :linux, arch: :arm64)
expect(tag.valid_combination?).to be false
tag = described_class.new(system: :linux, arch: :arm)
expect(tag.valid_combination?).to be false
end
end
end end

View File

@ -169,14 +169,22 @@ module Utils
[system, arch].hash [system, arch].hash
end end
sig { returns(Symbol) }
def standardized_arch
return :x86_64 if [:x86_64, :intel].include? arch
return :arm64 if [:arm64, :arm].include? arch
arch
end
sig { returns(Symbol) } sig { returns(Symbol) }
def to_sym def to_sym
if system == :all && arch == :all if system == :all && arch == :all
:all :all
elsif macos? && arch == :x86_64 elsif macos? && [:x86_64, :intel].include?(arch)
system system
else else
"#{arch}_#{system}".to_sym "#{standardized_arch}_#{system}".to_sym
end end
end end
@ -203,6 +211,15 @@ module Utils
false false
end end
sig { returns(T::Boolean) }
def valid_combination?
return true unless [:arm64, :arm].include? arch
return false if linux?
# Big Sur is the first version of macOS that runs on ARM
to_macos_version >= :big_sur
end
sig { returns(String) } sig { returns(String) }
def default_prefix def default_prefix
if linux? if linux?

View File

@ -6,7 +6,7 @@ path = File.expand_path('..', __FILE__)
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/i18n-1.12.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/i18n-1.12.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/minitest-5.16.2/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/minitest-5.16.2/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/tzinfo-2.0.4/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/tzinfo-2.0.5/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/zeitwerk-2.6.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/zeitwerk-2.6.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/activesupport-6.1.6.1/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/activesupport-6.1.6.1/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/public_suffix-4.0.7/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/public_suffix-4.0.7/lib"
@ -60,7 +60,7 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parallel-1.22.1/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parallel_tests-3.11.1/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parallel_tests-3.11.1/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parser-3.1.2.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parser-3.1.2.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rainbow-3.1.1/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rainbow-3.1.1/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-runtime-0.5.10160/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-runtime-0.5.10175/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parlour-8.0.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parlour-8.0.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/patchelf-1.3.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/patchelf-1.3.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/plist-3.6.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/plist-3.6.0/lib"
@ -86,8 +86,8 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rspec_junit_formatter
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-ast-1.19.1/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-ast-1.19.1/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ruby-progressbar-1.11.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ruby-progressbar-1.11.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/unicode-display_width-2.2.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/unicode-display_width-2.2.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-1.31.2/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-1.32.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-performance-1.14.2/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-performance-1.14.3/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rails-2.15.2/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rails-2.15.2/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rspec-2.12.1/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rspec-2.12.1/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-sorbet-0.6.11/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-sorbet-0.6.11/lib"
@ -96,10 +96,9 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/simplecov-html-0.12.3
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/simplecov_json_formatter-0.1.4/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/simplecov_json_formatter-0.1.4/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/simplecov-0.21.2/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/simplecov-0.21.2/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/simplecov-cobertura-2.1.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/simplecov-cobertura-2.1.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-static-0.5.10160-universal-darwin-15/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-static-0.5.10175-universal-darwin-15/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-0.5.10160/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-0.5.10175/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-runtime-stub-0.2.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-static-and-runtime-0.5.10175/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-static-and-runtime-0.5.10160/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/thor-1.2.1/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/thor-1.2.1/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/spoom-1.1.11/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/spoom-1.1.11/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/yard-0.9.28/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/yard-0.9.28/lib"

View File

@ -160,7 +160,7 @@ Performance/FlatMap:
Description: >- Description: >-
Use `Enumerable#flat_map` Use `Enumerable#flat_map`
instead of `Enumerable#map...Array#flatten(1)` instead of `Enumerable#map...Array#flatten(1)`
or `Enumberable#collect..Array#flatten(1)`. or `Enumerable#collect..Array#flatten(1)`.
Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code' Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code'
Enabled: true Enabled: true
VersionAdded: '0.30' VersionAdded: '0.30'

View File

@ -47,13 +47,13 @@ module RuboCop
RETURNS_NEW_ARRAY = (ALWAYS_RETURNS_NEW_ARRAY + RETURNS_NEW_ARRAY_WHEN_NO_BLOCK).freeze RETURNS_NEW_ARRAY = (ALWAYS_RETURNS_NEW_ARRAY + RETURNS_NEW_ARRAY_WHEN_NO_BLOCK).freeze
MSG = 'Use unchained `%<method>s` and `%<second_method>s!` '\ MSG = 'Use unchained `%<method>s` and `%<second_method>s!` ' \
'(followed by `return array` if required) instead of chaining '\ '(followed by `return array` if required) instead of chaining ' \
'`%<method>s...%<second_method>s`.' '`%<method>s...%<second_method>s`.'
def_node_matcher :chain_array_allocation?, <<~PATTERN def_node_matcher :chain_array_allocation?, <<~PATTERN
(send { (send {
(send _ $%RETURN_NEW_ARRAY_WHEN_ARGS {int lvar ivar cvar gvar}) (send _ $%RETURN_NEW_ARRAY_WHEN_ARGS {int lvar ivar cvar gvar send})
(block (send _ $%ALWAYS_RETURNS_NEW_ARRAY) ...) (block (send _ $%ALWAYS_RETURNS_NEW_ARRAY) ...)
(send _ $%RETURNS_NEW_ARRAY ...) (send _ $%RETURNS_NEW_ARRAY ...)
} $%HAS_MUTATION_ALTERNATIVE ...) } $%HAS_MUTATION_ALTERNATIVE ...)

View File

@ -32,7 +32,7 @@ module RuboCop
# end # end
# #
class CollectionLiteralInLoop < Base class CollectionLiteralInLoop < Base
MSG = 'Avoid immutable %<literal_class>s literals in loops. '\ MSG = 'Avoid immutable %<literal_class>s literals in loops. ' \
'It is better to extract it into a local variable or a constant.' 'It is better to extract it into a local variable or a constant.'
POST_CONDITION_LOOP_TYPES = %i[while_post until_post].freeze POST_CONDITION_LOOP_TYPES = %i[while_post until_post].freeze

View File

@ -58,8 +58,7 @@ module RuboCop
# `key?`/`value?` method. # `key?`/`value?` method.
corrector.replace( corrector.replace(
node.loc.expression, node.loc.expression,
"#{autocorrect_hash_expression(node)}."\ "#{autocorrect_hash_expression(node)}.#{autocorrect_method(node)}(#{autocorrect_argument(node)})"
"#{autocorrect_method(node)}(#{autocorrect_argument(node)})"
) )
end end
end end
@ -68,8 +67,7 @@ module RuboCop
private private
def message(node) def message(node)
"Use `##{autocorrect_method(node)}` instead of "\ "Use `##{autocorrect_method(node)}` instead of `##{current_method(node)}.include?`."
"`##{current_method(node)}.include?`."
end end
def autocorrect_method(node) def autocorrect_method(node)

View File

@ -38,7 +38,7 @@ module RuboCop
remove_class_variable remove_method undef_method class_variable_get class_variable_set remove_class_variable remove_method undef_method class_variable_get class_variable_set
deprecate_constant module_function private private_constant protected public public_constant deprecate_constant module_function private private_constant protected public public_constant
remove_const ruby2_keywords remove_const ruby2_keywords
define_singleton_method instance_variable_defined instance_variable_get instance_variable_set define_singleton_method instance_variable_defined? instance_variable_get instance_variable_set
method public_method public_send remove_instance_variable respond_to? send singleton_method method public_method public_send remove_instance_variable respond_to? send singleton_method
__send__ __send__
].freeze ].freeze

View File

@ -7,7 +7,7 @@ module RuboCop
# in some Enumerable object can be replaced by `Enumerable#sum` method. # in some Enumerable object can be replaced by `Enumerable#sum` method.
# #
# @safety # @safety
# Auto-corrections are unproblematic wherever an initial value is provided explicitly: # Autocorrections are unproblematic wherever an initial value is provided explicitly:
# #
# [source,ruby] # [source,ruby]
# ---- # ----
@ -43,7 +43,7 @@ module RuboCop
# #
# @example OnlySumOrWithInitialValue: false (default) # @example OnlySumOrWithInitialValue: false (default)
# # bad # # bad
# [1, 2, 3].inject(:+) # Auto-corrections for cases without initial value are unsafe # [1, 2, 3].inject(:+) # Autocorrections for cases without initial value are unsafe
# [1, 2, 3].inject(&:+) # and will only be performed when using the `-A` option. # [1, 2, 3].inject(&:+) # and will only be performed when using the `-A` option.
# [1, 2, 3].reduce { |acc, elem| acc + elem } # They can be prohibited completely using `SafeAutoCorrect: true`. # [1, 2, 3].reduce { |acc, elem| acc + elem } # They can be prohibited completely using `SafeAutoCorrect: true`.
# [1, 2, 3].reduce(10, :+) # [1, 2, 3].reduce(10, :+)

View File

@ -4,7 +4,7 @@ module RuboCop
module Performance module Performance
# This module holds the RuboCop Performance version information. # This module holds the RuboCop Performance version information.
module Version module Version
STRING = '1.14.2' STRING = '1.14.3'
def self.document_version def self.document_version
STRING.match('\d+\.\d+').to_s STRING.match('\d+\.\d+').to_s

View File

@ -0,0 +1,116 @@
# frozen_string_literal: true
# typed: true
# This file is hand-crafted to encode the dependencies. They load the whole type
# system since there is such a high chance of it being used, using an autoloader
# wouldn't buy us any startup time saving.
# Namespaces without any implementation
module T; end
module T::Helpers; end
module T::Private; end
module T::Private::Abstract; end
module T::Private::Types; end
# Each section is a group that I believe need a fixed ordering. There is also
# an ordering between groups.
# These are pre-reqs for almost everything in here.
require_relative 'types/configuration'
require_relative 'types/_types'
require_relative 'types/private/decl_state'
require_relative 'types/private/class_utils'
require_relative 'types/private/runtime_levels'
require_relative 'types/private/methods/_methods'
require_relative 'types/sig'
require_relative 'types/helpers'
require_relative 'types/private/final'
require_relative 'types/private/sealed'
# The types themselves. First base classes
require_relative 'types/types/base'
require_relative 'types/types/typed_enumerable'
# Everything else
require_relative 'types/types/class_of'
require_relative 'types/types/enum'
require_relative 'types/types/fixed_array'
require_relative 'types/types/fixed_hash'
require_relative 'types/types/intersection'
require_relative 'types/types/noreturn'
require_relative 'types/types/proc'
require_relative 'types/types/attached_class'
require_relative 'types/types/self_type'
require_relative 'types/types/simple'
require_relative 'types/types/t_enum'
require_relative 'types/types/type_parameter'
require_relative 'types/types/typed_array'
require_relative 'types/types/typed_enumerator'
require_relative 'types/types/typed_enumerator_lazy'
require_relative 'types/types/typed_hash'
require_relative 'types/types/typed_range'
require_relative 'types/types/typed_set'
require_relative 'types/types/union'
require_relative 'types/types/untyped'
require_relative 'types/private/types/not_typed'
require_relative 'types/private/types/void'
require_relative 'types/private/types/string_holder'
require_relative 'types/private/types/type_alias'
require_relative 'types/types/type_variable'
require_relative 'types/types/type_member'
require_relative 'types/types/type_template'
# Call validation
require_relative 'types/private/methods/modes'
require_relative 'types/private/methods/call_validation'
# Signature validation
require_relative 'types/private/methods/signature_validation'
require_relative 'types/abstract_utils'
require_relative 'types/private/abstract/validate'
# Catch all. Sort of built by `cd extn; find types -type f | grep -v test | sort`
require_relative 'types/generic'
require_relative 'types/interface_wrapper'
require_relative 'types/private/abstract/declare'
require_relative 'types/private/abstract/hooks'
require_relative 'types/private/casts'
require_relative 'types/private/methods/decl_builder'
require_relative 'types/private/methods/signature'
require_relative 'types/private/retry'
require_relative 'types/utils'
require_relative 'types/boolean'
# Props dependencies
require_relative 'types/private/abstract/data'
require_relative 'types/private/mixins/mixins'
require_relative 'types/props/_props'
require_relative 'types/props/custom_type'
require_relative 'types/props/decorator'
require_relative 'types/props/errors'
require_relative 'types/props/plugin'
require_relative 'types/props/utils'
require_relative 'types/enum'
# Props that run sigs statically so have to be after all the others :(
require_relative 'types/props/private/setter_factory'
require_relative 'types/props/private/apply_default'
require_relative 'types/props/has_lazily_specialized_methods'
require_relative 'types/props/optional'
require_relative 'types/props/weak_constructor'
require_relative 'types/props/constructor'
require_relative 'types/props/pretty_printable'
require_relative 'types/props/private/serde_transform'
require_relative 'types/props/private/deserializer_generator'
require_relative 'types/props/private/serializer_generator'
require_relative 'types/props/serializable'
require_relative 'types/props/type_validation'
require_relative 'types/props/private/parser'
require_relative 'types/props/generated_code_validation'
require_relative 'types/struct'
require_relative 'types/non_forcing_constants'
require_relative 'types/compatibility_patches'
# Sorbet Compiler support module
require_relative 'types/private/compiler'

View File

@ -0,0 +1,316 @@
# frozen_string_literal: true
# typed: true
# This is where we define the shortcuts, so we can't use them here
# _____
# |_ _| _ _ __ ___ ___
# | || | | | '_ \ / _ \/ __|
# | || |_| | |_) | __/\__ \
# |_| \__, | .__/ \___||___/
# |___/|_|
#
# Docs at https://sorbet.org/docs/sigs
#
# Types that you can pass to `sig`:
#
# - a Ruby class
#
# - [<Type>, <Type>, ...] -- to specify a "tuple"; a fixed-size array with known types for each member
#
# - {key: <Type>, key2: <Type>, ...} -- to speicfy a "shape"; a fixed-size hash
# with known keys and type values
#
# - Any of the `T.foo` methods below
module T
# T.any(<Type>, <Type>, ...) -- matches any of the types listed
def self.any(type_a, type_b, *types)
type_a = T::Utils.coerce(type_a)
type_b = T::Utils.coerce(type_b)
types = types.map {|t| T::Utils.coerce(t)} if !types.empty?
T::Types::Union::Private::Pool.union_of_types(type_a, type_b, types)
end
# Shorthand for T.any(type, NilClass)
def self.nilable(type)
T::Types::Union::Private::Pool.union_of_types(T::Utils.coerce(type), T::Utils::Nilable::NIL_TYPE)
end
# Matches any object. In the static checker, T.untyped allows any
# method calls or operations.
def self.untyped
T::Types::Untyped::Private::INSTANCE
end
# Indicates a function never returns (e.g. "Kernel#raise")
def self.noreturn
T::Types::NoReturn::Private::INSTANCE
end
# T.all(<Type>, <Type>, ...) -- matches an object that has all of the types listed
def self.all(type_a, type_b, *types)
T::Types::Intersection.new([type_a, type_b] + types)
end
# Matches any of the listed values
# @deprecated Use T::Enum instead.
def self.deprecated_enum(values)
T::Types::Enum.new(values)
end
# Creates a proc type
def self.proc
T::Private::Methods.start_proc
end
# Matches `self`:
def self.self_type
T::Types::SelfType::Private::INSTANCE
end
# Matches the instance type in a singleton-class context
def self.attached_class
T::Types::AttachedClassType::Private::INSTANCE
end
# Matches any class that subclasses or includes the provided class
# or module
def self.class_of(klass)
T::Types::ClassOf.new(klass)
end
## END OF THE METHODS TO PASS TO `sig`.
# Constructs a type alias. Used to create a short name for a larger type. In Ruby this returns a
# wrapper that contains a proc that is evaluated to get the underlying type. This syntax however
# is needed for support by the static checker.
#
# @example
# NilableString = T.type_alias {T.nilable(String)}
#
# sig {params(arg: NilableString, default: String).returns(String)}
# def or_else(arg, default)
# arg || default
# end
#
# The name of the type alias is not preserved; Error messages will
# be printed with reference to the underlying type.
#
# TODO Remove `type` parameter. This was left in to make life easier while migrating.
def self.type_alias(type=nil, &blk)
if blk
T::Private::Types::TypeAlias.new(blk)
else
T::Utils.coerce(type)
end
end
# References a type parameter which was previously defined with
# `type_parameters`.
#
# This is used for generic methods.
#
# @example
# sig
# .type_parameters(:U)
# .params(
# blk: T.proc.params(arg0: Elem).returns(T.type_parameter(:U)),
# )
# .returns(T::Array[T.type_parameter(:U)])
# def map(&blk); end
def self.type_parameter(name)
T::Types::TypeParameter.new(name)
end
# Tells the typechecker that `value` is of type `type`. Use this to get additional checking after
# an expression that the typechecker is unable to analyze. If `checked` is true, raises an
# exception at runtime if the value doesn't match the type.
#
# Compared to `T.let`, `T.cast` is _trusted_ by static system.
def self.cast(value, type, checked: true)
return value unless checked
Private::Casts.cast(value, type, cast_method: "T.cast")
end
# Tells the typechecker to declare a variable of type `type`. Use
# like:
#
# seconds = T.let(0.0, Float)
#
# Compared to `T.cast`, `T.let` is _checked_ by static system.
#
# If `checked` is true, raises an exception at runtime if the value
# doesn't match the type.
def self.let(value, type, checked: true)
return value unless checked
Private::Casts.cast(value, type, cast_method: "T.let")
end
# Tells the type checker to treat `self` in the current block as `type`.
# Useful for blocks that are captured and executed later with instance_exec.
# Use like:
#
# seconds = lambda do
# T.bind(self, NewBinding)
# ...
# end
#
# `T.bind` behaves like `T.cast` in that it is assumed to be true statically.
#
# If `checked` is true, raises an exception at runtime if the value
# doesn't match the type (this is the default).
def self.bind(value, type, checked: true)
return value unless checked
Private::Casts.cast(value, type, cast_method: "T.bind")
end
# Tells the typechecker to ensure that `value` is of type `type` (if not, the typechecker will
# fail). Use this for debugging typechecking errors, or to ensure that type information is
# statically known and being checked appropriately. If `checked` is true, raises an exception at
# runtime if the value doesn't match the type.
def self.assert_type!(value, type, checked: true)
return value unless checked
Private::Casts.cast(value, type, cast_method: "T.assert_type!")
end
# For the static type checker, strips all type information from a value
# and returns the same value, but statically-typed as `T.untyped`.
# Can be used to tell the static checker to "trust you" by discarding type information
# you know to be incorrect. Use with care!
# (This has no effect at runtime.)
#
# We can't actually write this sig because we ourselves are inside
# the `T::` module and doing this would create a bootstrapping
# cycle. However, we also don't actually need to do so; An untyped
# identity method works just as well here.
#
# `sig {params(value: T.untyped).returns(T.untyped)}`
def self.unsafe(value)
value
end
# A convenience method to `raise` when the argument is `nil` and return it
# otherwise.
#
# Intended to be used as:
#
# needs_foo(T.must(maybe_gives_foo))
#
# Equivalent to:
#
# foo = maybe_gives_foo
# raise "nil" if foo.nil?
# needs_foo(foo)
#
# Intended to be used to promise sorbet that a given nilable value happens
# to contain a non-nil value at this point.
#
# `sig {params(arg: T.nilable(A)).returns(A)}`
def self.must(arg)
return arg if arg
return arg if arg == false
begin
raise TypeError.new("Passed `nil` into T.must")
rescue TypeError => e # raise into rescue to ensure e.backtrace is populated
T::Configuration.inline_type_error_handler(e, {kind: 'T.must', value: arg, type: nil})
end
end
# A way to ask Sorbet to show what type it thinks an expression has.
# This can be useful for debugging and checking assumptions.
# In the runtime, merely returns the value passed in.
def self.reveal_type(value)
value
end
# A way to ask Sorbet to prove that a certain branch of control flow never
# happens. Commonly used to assert that a case or if statement exhausts all
# possible cases.
def self.absurd(value)
msg = "Control flow reached T.absurd."
case value
when Kernel
msg += " Got value: #{value}"
end
begin
raise TypeError.new(msg)
rescue TypeError => e # raise into rescue to ensure e.backtrace is populated
T::Configuration.inline_type_error_handler(e, {kind: 'T.absurd', value: value, type: nil})
end
end
### Generic classes ###
module Array
def self.[](type)
if type.is_a?(T::Types::Untyped)
T::Types::TypedArray::Untyped.new
else
T::Types::TypedArray.new(type)
end
end
end
module Hash
def self.[](keys, values)
if keys.is_a?(T::Types::Untyped) && values.is_a?(T::Types::Untyped)
T::Types::TypedHash::Untyped.new
else
T::Types::TypedHash.new(keys: keys, values: values)
end
end
end
module Enumerable
def self.[](type)
if type.is_a?(T::Types::Untyped)
T::Types::TypedEnumerable::Untyped.new
else
T::Types::TypedEnumerable.new(type)
end
end
end
module Enumerator
def self.[](type)
if type.is_a?(T::Types::Untyped)
T::Types::TypedEnumerator::Untyped.new
else
T::Types::TypedEnumerator.new(type)
end
end
module Lazy
def self.[](type)
if type.is_a?(T::Types::Untyped)
T::Types::TypedEnumeratorLazy::Untyped.new
else
T::Types::TypedEnumeratorLazy.new(type)
end
end
end
end
module Range
def self.[](type)
T::Types::TypedRange.new(type)
end
end
module Set
def self.[](type)
if type.is_a?(T::Types::Untyped)
T::Types::TypedSet::Untyped.new
else
T::Types::TypedSet.new(type)
end
end
end
end

View File

@ -0,0 +1,50 @@
# frozen_string_literal: true
# typed: true
module T::AbstractUtils
Methods = T::Private::Methods
# Returns whether a module is declared as abstract. After the module is finished being declared,
# this is equivalent to whether it has any abstract methods that haven't been implemented
# (because we validate that and raise an error otherwise).
#
# Note that checking `mod.is_a?(Abstract::Hooks)` is not a safe substitute for this method; when
# a class extends `Abstract::Hooks`, all of its subclasses, including the eventual concrete
# ones, will still have `Abstract::Hooks` as an ancestor.
def self.abstract_module?(mod)
!T::Private::Abstract::Data.get(mod, :abstract_type).nil?
end
def self.abstract_method?(method)
signature = Methods.signature_for_method(method)
signature&.mode == Methods::Modes.abstract
end
# Given a module, returns the set of methods declared as abstract (in itself or ancestors)
# that have not been implemented.
def self.abstract_methods_for(mod)
declared_methods = declared_abstract_methods_for(mod)
declared_methods.select do |declared_method|
actual_method = mod.instance_method(declared_method.name)
# Note that in the case where an abstract method is overridden by another abstract method,
# this method will return them both. This is intentional to ensure we validate the final
# implementation against all declarations of an abstract method (they might not all have the
# same signature).
abstract_method?(actual_method)
end
end
# Given a module, returns the set of methods declared as abstract (in itself or ancestors)
# regardless of whether they have been implemented.
def self.declared_abstract_methods_for(mod)
methods = []
mod.ancestors.each do |ancestor|
ancestor_methods = ancestor.private_instance_methods(false) + ancestor.instance_methods(false)
ancestor_methods.each do |method_name|
method = ancestor.instance_method(method_name)
methods << method if abstract_method?(method)
end
end
methods
end
end

View File

@ -0,0 +1,8 @@
# typed: strict
# frozen_string_literal: true
module T
# T::Boolean is a type alias helper for the common `T.any(TrueClass, FalseClass)`.
# Defined separately from _types.rb because it has a dependency on T::Types::Union.
Boolean = T.type_alias {T.any(TrueClass, FalseClass)}
end

View File

@ -0,0 +1,93 @@
# frozen_string_literal: true
# typed: ignore
# Work around an interaction bug with sorbet-runtime and rspec-mocks,
# which occurs when using message expectations (*_any_instance_of,
# expect, allow) and and_call_original.
#
# When a sig is defined, sorbet-runtime will replace the sigged method
# with a wrapper that, upon first invocation, re-wraps the method with a faster
# implementation.
#
# When expect_any_instance_of is used, rspec stores a reference to the first wrapper,
# to be restored later.
#
# The first wrapper is invoked as part of the test and sorbet-runtime replaces
# the method definition with the second wrapper.
#
# But when mocks are cleaned up, rspec restores back to the first wrapper.
# Upon subsequent invocations, the first wrapper is called, and sorbet-runtime
# throws a runtime error, since this is an unexpected state.
#
# We work around this by forcing re-wrapping before rspec stores a reference
# to the method.
if defined? ::RSpec::Mocks
module T
module CompatibilityPatches
module RSpecCompatibility
module RecorderExtensions
def observe!(method_name)
method = @klass.instance_method(method_name.to_sym)
T::Private::Methods.maybe_run_sig_block_for_method(method)
super(method_name)
end
end
::RSpec::Mocks::AnyInstance::Recorder.prepend(RecorderExtensions) if defined?(::RSpec::Mocks::AnyInstance::Recorder)
module MethodDoubleExtensions
def initialize(object, method_name, proxy)
if ::Kernel.instance_method(:respond_to?).bind(object).call(method_name, true)
method = ::RSpec::Support.method_handle_for(object, method_name)
T::Private::Methods.maybe_run_sig_block_for_method(method)
end
super(object, method_name, proxy)
end
end
::RSpec::Mocks::MethodDouble.prepend(MethodDoubleExtensions) if defined?(::RSpec::Mocks::MethodDouble)
end
end
end
end
# Work around for sorbet-runtime wrapped methods.
#
# When a sig is defined, sorbet-runtime will replace the sigged method
# with a wrapper. Those wrapper methods look like `foo(*args, &blk)`
# so that wrappers can handle and pass on all the arguments supplied.
#
# However, that creates a problem with runtime reflection on the methods,
# since when a sigged method is introspected, it will always return its
# `arity` as `-1`, its `parameters` as `[[:rest, :args], [:block, :blk]]`,
# and its `source_location` as `[<some_file_in_sorbet>, <some_line_number>]`.
#
# This might be a problem for some applications that rely on getting the
# correct information from these methods.
#
# This compatibility module, when prepended to the `Method` class, would fix
# the return values of `arity`, `parameters` and `source_location`.
#
# @example
# require 'sorbet-runtime'
# ::Method.prepend(T::CompatibilityPatches::MethodExtensions)
module T
module CompatibilityPatches
module MethodExtensions
def arity
arity = super
return arity if arity != -1 || self.is_a?(Proc)
sig = T::Private::Methods.signature_for_method(self)
sig ? sig.method.arity : arity
end
def source_location
sig = T::Private::Methods.signature_for_method(self)
sig ? sig.method.source_location : super
end
def parameters
sig = T::Private::Methods.signature_for_method(self)
sig ? sig.method.parameters : super
end
end
end
end

View File

@ -0,0 +1,591 @@
# typed: true
# frozen_string_literal: true
module T::Configuration
# Cache this comparisonn to avoid two allocations all over the place.
AT_LEAST_RUBY_2_7 = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.7')
# Announces to Sorbet that we are currently in a test environment, so it
# should treat any sigs which are marked `.checked(:tests)` as if they were
# just a normal sig.
#
# If this method is not called, sigs marked `.checked(:tests)` will not be
# checked. In fact, such methods won't even be wrapped--the runtime will put
# back the original method.
#
# Note: Due to the way sigs are evaluated and methods are wrapped, this
# method MUST be called before any code calls `sig`. This method raises if
# it has been called too late.
def self.enable_checking_for_sigs_marked_checked_tests
T::Private::RuntimeLevels.enable_checking_in_tests
end
# Announce to Sorbet that we would like the final checks to be enabled when
# including and extending modules. Iff this is not called, then the following
# example will not raise an error.
#
# ```ruby
# module M
# extend T::Sig
# sig(:final) {void}
# def foo; end
# end
# class C
# include M
# def foo; end
# end
# ```
def self.enable_final_checks_on_hooks
T::Private::Methods.set_final_checks_on_hooks(true)
end
# Undo the effects of a previous call to
# `enable_final_checks_on_hooks`.
def self.reset_final_checks_on_hooks
T::Private::Methods.set_final_checks_on_hooks(false)
end
@include_value_in_type_errors = true
# Whether to include values in TypeError messages.
#
# Including values is useful for debugging, but can potentially leak
# sensitive information to logs.
#
# @return [T::Boolean]
def self.include_value_in_type_errors?
@include_value_in_type_errors
end
# Configure if type errors excludes the value of the problematic type.
#
# The default is to include values in type errors:
# TypeError: Expected type Integer, got String with value "foo"
#
# When values are excluded from type errors:
# TypeError: Expected type Integer, got String
def self.exclude_value_in_type_errors
@include_value_in_type_errors = false
end
# Opposite of exclude_value_in_type_errors.
# (Including values in type errors is the default)
def self.include_value_in_type_errors
@include_value_in_type_errors = true
end
# Whether VM-defined prop serialization/deserialization routines can be enabled.
#
# @return [T::Boolean]
def self.can_enable_vm_prop_serde?
T::Props::Private::DeserializerGenerator.respond_to?(:generate2)
end
@use_vm_prop_serde = false
# Whether to use VM-defined prop serialization/deserialization routines.
#
# The default is to use runtime codegen inside sorbet-runtime itself.
#
# @return [T::Boolean]
def self.use_vm_prop_serde?
@use_vm_prop_serde || false
end
# Enable using VM-defined prop serialization/deserialization routines.
#
# This method is likely to break things outside of Stripe's systems.
def self.enable_vm_prop_serde
if !can_enable_vm_prop_serde?
hard_assert_handler('Ruby VM is not setup to use VM-defined prop serde')
end
@use_vm_prop_serde = true
end
# Disable using VM-defined prop serialization/deserialization routines.
def self.disable_vm_prop_serde
@use_vm_prop_serde = false
end
# Configure the default checked level for a sig with no explicit `.checked`
# builder. When unset, the default checked level is `:always`.
#
# Note: setting this option is potentially dangerous! Sorbet can't check all
# code statically. The runtime checks complement the checks that Sorbet does
# statically, so that methods don't have to guard themselves from being
# called incorrectly by untyped code.
#
# @param [:never, :compiled, :tests, :always] default_checked_level
def self.default_checked_level=(default_checked_level)
T::Private::RuntimeLevels.default_checked_level = default_checked_level
end
@inline_type_error_handler = nil
# Set a handler to handle `TypeError`s raised by any in-line type assertions,
# including `T.must`, `T.let`, `T.cast`, and `T.assert_type!`.
#
# By default, any `TypeError`s detected by this gem will be raised. Setting
# inline_type_error_handler to an object that implements :call (e.g. proc or
# lambda) allows users to customize the behavior when a `TypeError` is
# raised on any inline type assertion.
#
# @param [Lambda, Proc, Object, nil] value Proc that handles the error (pass
# nil to reset to default behavior)
#
# Parameters passed to value.call:
#
# @param [TypeError] error TypeError that was raised
# @param [Hash] opts A hash containing contextual information on the error:
# @option opts [String] :kind One of:
# ['T.cast', 'T.let', 'T.bind', 'T.assert_type!', 'T.must', 'T.absurd']
# @option opts [Object, nil] :type Expected param/return value type
# @option opts [Object] :value Actual param/return value
#
# @example
# T::Configuration.inline_type_error_handler = lambda do |error, opts|
# puts error.message
# end
def self.inline_type_error_handler=(value)
validate_lambda_given!(value)
@inline_type_error_handler = value
end
private_class_method def self.inline_type_error_handler_default(error, opts)
raise error
end
def self.inline_type_error_handler(error, opts={})
if @inline_type_error_handler
# Backwards compatibility before `inline_type_error_handler` took a second arg
if @inline_type_error_handler.arity == 1
@inline_type_error_handler.call(error)
else
@inline_type_error_handler.call(error, opts)
end
else
inline_type_error_handler_default(error, opts)
end
nil
end
@sig_builder_error_handler = nil
# Set a handler to handle errors that occur when the builder methods in the
# body of a sig are executed. The sig builder methods are inside a proc so
# that they can be lazily evaluated the first time the method being sig'd is
# called.
#
# By default, improper use of the builder methods within the body of a sig
# cause an ArgumentError to be raised. Setting sig_builder_error_handler to an
# object that implements :call (e.g. proc or lambda) allows users to
# customize the behavior when a sig can't be built for some reason.
#
# @param [Lambda, Proc, Object, nil] value Proc that handles the error (pass
# nil to reset to default behavior)
#
# Parameters passed to value.call:
#
# @param [StandardError] error The error that was raised
# @param [Thread::Backtrace::Location] location Location of the error
#
# @example
# T::Configuration.sig_builder_error_handler = lambda do |error, location|
# puts error.message
# end
def self.sig_builder_error_handler=(value)
validate_lambda_given!(value)
@sig_builder_error_handler = value
end
private_class_method def self.sig_builder_error_handler_default(error, location)
raise ArgumentError.new("#{location.path}:#{location.lineno}: Error interpreting `sig`:\n #{error.message}\n\n")
end
def self.sig_builder_error_handler(error, location)
if @sig_builder_error_handler
@sig_builder_error_handler.call(error, location)
else
sig_builder_error_handler_default(error, location)
end
nil
end
@sig_validation_error_handler = nil
# Set a handler to handle sig validation errors.
#
# Sig validation errors include things like abstract checks, override checks,
# and type compatibility of arguments. They happen after a sig has been
# successfully built, but the built sig is incompatible with other sigs in
# some way.
#
# By default, sig validation errors cause an exception to be raised.
# Setting sig_validation_error_handler to an object that implements :call
# (e.g. proc or lambda) allows users to customize the behavior when a method
# signature's build fails.
#
# @param [Lambda, Proc, Object, nil] value Proc that handles the error (pass
# nil to reset to default behavior)
#
# Parameters passed to value.call:
#
# @param [StandardError] error The error that was raised
# @param [Hash] opts A hash containing contextual information on the error:
# @option opts [Method, UnboundMethod] :method Method on which the signature build failed
# @option opts [T::Private::Methods::Declaration] :declaration Method
# signature declaration struct
# @option opts [T::Private::Methods::Signature, nil] :signature Signature
# that failed (nil if sig build failed before Signature initialization)
# @option opts [T::Private::Methods::Signature, nil] :super_signature Super
# method's signature (nil if method is not an override or super method
# does not have a method signature)
#
# @example
# T::Configuration.sig_validation_error_handler = lambda do |error, opts|
# puts error.message
# end
def self.sig_validation_error_handler=(value)
validate_lambda_given!(value)
@sig_validation_error_handler = value
end
private_class_method def self.sig_validation_error_handler_default(error, opts)
raise error
end
def self.sig_validation_error_handler(error, opts={})
if @sig_validation_error_handler
@sig_validation_error_handler.call(error, opts)
else
sig_validation_error_handler_default(error, opts)
end
nil
end
@call_validation_error_handler = nil
# Set a handler for type errors that result from calling a method.
#
# By default, errors from calling a method cause an exception to be raised.
# Setting call_validation_error_handler to an object that implements :call
# (e.g. proc or lambda) allows users to customize the behavior when a method
# is called with invalid parameters, or returns an invalid value.
#
# @param [Lambda, Proc, Object, nil] value Proc that handles the error
# report (pass nil to reset to default behavior)
#
# Parameters passed to value.call:
#
# @param [T::Private::Methods::Signature] signature Signature that failed
# @param [Hash] opts A hash containing contextual information on the error:
# @option opts [String] :message Error message
# @option opts [String] :kind One of:
# ['Parameter', 'Block parameter', 'Return value']
# @option opts [Symbol] :name Param or block param name (nil for return
# value)
# @option opts [Object] :type Expected param/return value type
# @option opts [Object] :value Actual param/return value
# @option opts [Thread::Backtrace::Location] :location Location of the
# caller
#
# @example
# T::Configuration.call_validation_error_handler = lambda do |signature, opts|
# puts opts[:message]
# end
def self.call_validation_error_handler=(value)
validate_lambda_given!(value)
@call_validation_error_handler = value
end
private_class_method def self.call_validation_error_handler_default(signature, opts)
raise TypeError.new(opts[:pretty_message])
end
def self.call_validation_error_handler(signature, opts={})
if @call_validation_error_handler
@call_validation_error_handler.call(signature, opts)
else
call_validation_error_handler_default(signature, opts)
end
nil
end
@log_info_handler = nil
# Set a handler for logging
#
# @param [Lambda, Proc, Object, nil] value Proc that handles the error
# report (pass nil to reset to default behavior)
#
# Parameters passed to value.call:
#
# @param [String] str Message to be logged
# @param [Hash] extra A hash containing additional parameters to be passed along to the logger.
#
# @example
# T::Configuration.log_info_handler = lambda do |str, extra|
# puts "#{str}, context: #{extra}"
# end
def self.log_info_handler=(value)
validate_lambda_given!(value)
@log_info_handler = value
end
private_class_method def self.log_info_handler_default(str, extra)
puts "#{str}, extra: #{extra}"
end
def self.log_info_handler(str, extra)
if @log_info_handler
@log_info_handler.call(str, extra)
else
log_info_handler_default(str, extra)
end
end
@soft_assert_handler = nil
# Set a handler for soft assertions
#
# These generally shouldn't stop execution of the program, but rather inform
# some party of the assertion to action on later.
#
# @param [Lambda, Proc, Object, nil] value Proc that handles the error
# report (pass nil to reset to default behavior)
#
# Parameters passed to value.call:
#
# @param [String] str Assertion message
# @param [Hash] extra A hash containing additional parameters to be passed along to the handler.
#
# @example
# T::Configuration.soft_assert_handler = lambda do |str, extra|
# puts "#{str}, context: #{extra}"
# end
def self.soft_assert_handler=(value)
validate_lambda_given!(value)
@soft_assert_handler = value
end
private_class_method def self.soft_assert_handler_default(str, extra)
puts "#{str}, extra: #{extra}"
end
def self.soft_assert_handler(str, extra)
if @soft_assert_handler
@soft_assert_handler.call(str, extra)
else
soft_assert_handler_default(str, extra)
end
end
@hard_assert_handler = nil
# Set a handler for hard assertions
#
# These generally should stop execution of the program, and optionally inform
# some party of the assertion.
#
# @param [Lambda, Proc, Object, nil] value Proc that handles the error
# report (pass nil to reset to default behavior)
#
# Parameters passed to value.call:
#
# @param [String] str Assertion message
# @param [Hash] extra A hash containing additional parameters to be passed along to the handler.
#
# @example
# T::Configuration.hard_assert_handler = lambda do |str, extra|
# raise "#{str}, context: #{extra}"
# end
def self.hard_assert_handler=(value)
validate_lambda_given!(value)
@hard_assert_handler = value
end
private_class_method def self.hard_assert_handler_default(str, _)
raise str
end
def self.hard_assert_handler(str, extra={})
if @hard_assert_handler
@hard_assert_handler.call(str, extra)
else
hard_assert_handler_default(str, extra)
end
end
@scalar_types = nil
# Set a list of class strings that are to be considered scalar.
# (pass nil to reset to default behavior)
#
# @param [String] values Class name.
#
# @example
# T::Configuration.scalar_types = ["NilClass", "TrueClass", "FalseClass", ...]
def self.scalar_types=(values)
if values.nil?
@scalar_types = values
else
bad_values = values.reject {|v| v.class == String}
unless bad_values.empty?
raise ArgumentError.new("Provided values must all be class name strings.")
end
@scalar_types = values.each_with_object({}) {|x, acc| acc[x] = true}.freeze
end
end
@default_scalar_types = {
"NilClass" => true,
"TrueClass" => true,
"FalseClass" => true,
"Integer" => true,
"Float" => true,
"String" => true,
"Symbol" => true,
"Time" => true,
"T::Enum" => true,
}.freeze
def self.scalar_types
@scalar_types || @default_scalar_types
end
# Guard against overrides of `name` or `to_s`
MODULE_NAME = Module.instance_method(:name)
private_constant :MODULE_NAME
@default_module_name_mangler = if T::Configuration::AT_LEAST_RUBY_2_7
->(type) {MODULE_NAME.bind_call(type)}
else
->(type) {MODULE_NAME.bind(type).call}
end
@module_name_mangler = nil
def self.module_name_mangler
@module_name_mangler || @default_module_name_mangler
end
# Set to override the default behavior for converting types
# to names in generated code. Used by the runtime implementation
# associated with `--stripe-packages` mode.
#
# @param [Lambda, Proc, nil] handler Proc that converts a type (Class/Module)
# to a String (pass nil to reset to default behavior)
def self.module_name_mangler=(handler)
@module_name_mangler = handler
end
@sensitivity_and_pii_handler = nil
# Set to a PII handler function. This will be called with the `sensitivity:`
# annotations on things that use `T::Props` and can modify them ahead-of-time.
#
# @param [Lambda, Proc, nil] handler Proc that takes a hash mapping symbols to the
# prop values. Pass nil to avoid changing `sensitivity:` annotations.
def self.normalize_sensitivity_and_pii_handler=(handler)
@sensitivity_and_pii_handler = handler
end
def self.normalize_sensitivity_and_pii_handler
@sensitivity_and_pii_handler
end
@redaction_handler = nil
# Set to a redaction handling function. This will be called when the
# `_redacted` version of a prop reader is used. By default this is set to
# `nil` and will raise an exception when the redacted version of a prop is
# accessed.
#
# @param [Lambda, Proc, nil] handler Proc that converts a value into its
# redacted version according to the spec passed as the second argument.
def self.redaction_handler=(handler)
@redaction_handler = handler
end
def self.redaction_handler
@redaction_handler
end
@class_owner_finder = nil
# Set to a function which can get the 'owner' of a class. This is
# used in reporting deserialization errors
#
# @param [Lambda, Proc, nil] handler Proc that takes a class and
# produces its owner, or `nil` if it does not have one.
def self.class_owner_finder=(handler)
@class_owner_finder = handler
end
def self.class_owner_finder
@class_owner_finder
end
# Temporarily disable ruby warnings while executing the given block. This is
# useful when doing something that would normally cause a warning to be
# emitted in Ruby verbose mode ($VERBOSE = true).
#
# @yield
#
def self.without_ruby_warnings
if $VERBOSE
begin
original_verbose = $VERBOSE
$VERBOSE = false
yield
ensure
$VERBOSE = original_verbose
end
else
yield
end
end
@legacy_t_enum_migration_mode = false
def self.enable_legacy_t_enum_migration_mode
@legacy_t_enum_migration_mode = true
end
def self.disable_legacy_t_enum_migration_mode
@legacy_t_enum_migration_mode = false
end
def self.legacy_t_enum_migration_mode?
@legacy_t_enum_migration_mode || false
end
@prop_freeze_handler = ->(instance, prop_name) {}
def self.prop_freeze_handler=(handler)
@prop_freeze_handler = handler
end
def self.prop_freeze_handler
@prop_freeze_handler
end
@sealed_violation_whitelist = nil
# @param [Array] sealed_violation_whitelist An array of Regexp to validate
# whether inheriting /including a sealed module outside the defining module
# should be allowed. Useful to whitelist benign violations, like shim files
# generated for an autoloader.
def self.sealed_violation_whitelist=(sealed_violation_whitelist)
if !@sealed_violation_whitelist.nil?
raise ArgumentError.new("Cannot overwrite sealed_violation_whitelist after setting it")
end
case sealed_violation_whitelist
when Array
sealed_violation_whitelist.each do |x|
case x
when Regexp then nil
else raise TypeError.new("sealed_violation_whitelist accepts an Array of Regexp")
end
end
else
raise TypeError.new("sealed_violation_whitelist= accepts an Array of Regexp")
end
@sealed_violation_whitelist = sealed_violation_whitelist
end
def self.sealed_violation_whitelist
@sealed_violation_whitelist
end
private_class_method def self.validate_lambda_given!(value)
if !value.nil? && !value.respond_to?(:call)
raise ArgumentError.new("Provided value must respond to :call")
end
end
end

View File

@ -0,0 +1,377 @@
# frozen_string_literal: true
# typed: strict
# Enumerations allow for type-safe declarations of a fixed set of values.
#
# Every value is a singleton instance of the class (i.e. `Suit::SPADE.is_a?(Suit) == true`).
#
# Each value has a corresponding serialized value. By default this is the constant's name converted
# to lowercase (e.g. `Suit::Club.serialize == 'club'`); however a custom value may be passed to the
# constructor. Enum will `freeze` the serialized value.
#
# @example Declaring an Enum:
# class Suit < T::Enum
# enums do
# CLUB = new
# SPADE = new
# DIAMOND = new
# HEART = new
# end
# end
#
# @example Custom serialization value:
# class Status < T::Enum
# enums do
# READY = new('rdy')
# ...
# end
# end
#
# @example Accessing values:
# Suit::SPADE
#
# @example Converting from serialized value to enum instance:
# Suit.deserialize('club') == Suit::CLUB
#
# @example Using enums in type signatures:
# sig {params(suit: Suit).returns(Boolean)}
# def is_red?(suit); ...; end
#
# WARNING: Enum instances are singletons that are shared among all their users. Their internals
# should be kept immutable to avoid unpredictable action at a distance.
class T::Enum
extend T::Sig
extend T::Props::CustomType
# TODO(jez) Might want to restrict this, or make subclasses provide this type
SerializedVal = T.type_alias {T.untyped}
private_constant :SerializedVal
### Enum class methods ###
sig {returns(T::Array[T.attached_class])}
def self.values
if @values.nil?
raise "Attempting to access values of #{self.class} before it has been initialized." \
" Enums are not initialized until the 'enums do' block they are defined in has finished running."
end
@values
end
# This exists for compatibility with the interface of `Hash` & mostly to support
# the HashEachMethods Rubocop.
sig {params(blk: T.nilable(T.proc.params(arg0: T.attached_class).void)).returns(T.any(T::Enumerator[T.attached_class], T::Array[T.attached_class]))}
def self.each_value(&blk)
if blk
values.each(&blk)
else
values.each
end
end
# Convert from serialized value to enum instance
#
# Note: It would have been nice to make this method final before people started overriding it.
# Note: Failed CriticalMethodsNoRuntimeTypingTest
sig {params(serialized_val: SerializedVal).returns(T.nilable(T.attached_class)).checked(:never)}
def self.try_deserialize(serialized_val)
if @mapping.nil?
raise "Attempting to access serialization map of #{self.class} before it has been initialized." \
" Enums are not initialized until the 'enums do' block they are defined in has finished running."
end
@mapping[serialized_val]
end
# Convert from serialized value to enum instance.
#
# Note: It would have been nice to make this method final before people started overriding it.
# Note: Failed CriticalMethodsNoRuntimeTypingTest
#
# @return [self]
# @raise [KeyError] if serialized value does not match any instance.
sig {overridable.params(serialized_val: SerializedVal).returns(T.attached_class).checked(:never)}
def self.from_serialized(serialized_val)
res = try_deserialize(serialized_val)
if res.nil?
raise KeyError.new("Enum #{self} key not found: #{serialized_val.inspect}")
end
res
end
# Note: It would have been nice to make this method final before people started overriding it.
# @return [Boolean] Does the given serialized value correspond with any of this enum's values.
sig {overridable.params(serialized_val: SerializedVal).returns(T::Boolean).checked(:never)}
def self.has_serialized?(serialized_val)
if @mapping.nil?
raise "Attempting to access serialization map of #{self.class} before it has been initialized." \
" Enums are not initialized until the 'enums do' block they are defined in has finished running."
end
@mapping.include?(serialized_val)
end
# Note: Failed CriticalMethodsNoRuntimeTypingTest
sig {override.params(instance: T.nilable(T::Enum)).returns(SerializedVal).checked(:never)}
def self.serialize(instance)
# This is needed otherwise if a Chalk::ODM::Document with a property of the shape
# T::Hash[T.nilable(MyEnum), Integer] and a value that looks like {nil => 0} is
# serialized, we throw the error on L102.
return nil if instance.nil?
if self == T::Enum
raise "Cannot call T::Enum.serialize directly. You must call on a specific child class."
end
if instance.class != self
raise "Cannot call #serialize on a value that is not an instance of #{self}."
end
instance.serialize
end
# Note: Failed CriticalMethodsNoRuntimeTypingTest
sig {override.params(mongo_value: SerializedVal).returns(T.attached_class).checked(:never)}
def self.deserialize(mongo_value)
if self == T::Enum
raise "Cannot call T::Enum.deserialize directly. You must call on a specific child class."
end
self.from_serialized(mongo_value)
end
### Enum instance methods ###
sig {returns(T.self_type)}
def dup
self
end
sig {returns(T.self_type).checked(:tests)}
def clone
self
end
# Note: Failed CriticalMethodsNoRuntimeTypingTest
sig {returns(SerializedVal).checked(:never)}
def serialize
assert_bound!
@serialized_val
end
sig {params(args: T.untyped).returns(T.untyped)}
def to_json(*args)
serialize.to_json(*args)
end
sig {params(args: T.untyped).returns(T.untyped)}
def as_json(*args)
serialized_val = serialize
return serialized_val unless serialized_val.respond_to?(:as_json)
serialized_val.as_json(*args)
end
sig {returns(String)}
def to_s
inspect
end
sig {returns(String)}
def inspect
"#<#{self.class.name}::#{@const_name || '__UNINITIALIZED__'}>"
end
sig {params(other: BasicObject).returns(T.nilable(Integer))}
def <=>(other)
case other
when self.class
self.serialize <=> other.serialize
else
nil
end
end
# NB: Do not call this method. This exists to allow for a safe migration path in places where enum
# values are compared directly against string values.
#
# Ruby's string has a weird quirk where `'my_string' == obj` calls obj.==('my_string') if obj
# responds to the `to_str` method. It does not actually call `to_str` however.
#
# See https://ruby-doc.org/core-2.4.0/String.html#method-i-3D-3D
sig {returns(String)}
def to_str
msg = 'Implicit conversion of Enum instances to strings is not allowed. Call #serialize instead.'
if T::Configuration.legacy_t_enum_migration_mode?
T::Configuration.soft_assert_handler(
msg,
storytime: {class: self.class.name},
)
serialize.to_s
else
raise NoMethodError.new(msg)
end
end
sig {params(other: BasicObject).returns(T::Boolean).checked(:never)}
def ==(other)
case other
when String
if T::Configuration.legacy_t_enum_migration_mode?
comparison_assertion_failed(:==, other)
self.serialize == other
else
false
end
else
super(other)
end
end
sig {params(other: BasicObject).returns(T::Boolean).checked(:never)}
def ===(other)
case other
when String
if T::Configuration.legacy_t_enum_migration_mode?
comparison_assertion_failed(:===, other)
self.serialize == other
else
false
end
else
super(other)
end
end
sig {params(method: Symbol, other: T.untyped).void}
private def comparison_assertion_failed(method, other)
T::Configuration.soft_assert_handler(
'Enum to string comparison not allowed. Compare to the Enum instance directly instead. See go/enum-migration',
storytime: {
class: self.class.name,
self: self.inspect,
other: other,
other_class: other.class.name,
method: method,
}
)
end
### Private implementation ###
sig {params(serialized_val: SerializedVal).void}
def initialize(serialized_val=nil)
raise 'T::Enum is abstract' if self.class == T::Enum
if !self.class.started_initializing?
raise "Must instantiate all enum values of #{self.class} inside 'enums do'."
end
if self.class.fully_initialized?
raise "Cannot instantiate a new enum value of #{self.class} after it has been initialized."
end
serialized_val = serialized_val.frozen? ? serialized_val : serialized_val.dup.freeze
@serialized_val = T.let(serialized_val, T.nilable(SerializedVal))
@const_name = T.let(nil, T.nilable(Symbol))
self.class._register_instance(self)
end
sig {returns(NilClass).checked(:never)}
private def assert_bound!
if @const_name.nil?
raise "Attempting to access Enum value on #{self.class} before it has been initialized." \
" Enums are not initialized until the 'enums do' block they are defined in has finished running."
end
end
sig {params(const_name: Symbol).void}
def _bind_name(const_name)
@const_name = const_name
@serialized_val = const_to_serialized_val(const_name) if @serialized_val.nil?
freeze
end
sig {params(const_name: Symbol).returns(String)}
private def const_to_serialized_val(const_name)
# Historical note: We convert to lowercase names because the majority of existing calls to
# `make_accessible` were arrays of lowercase strings. Doing this conversion allowed for the
# least amount of repetition in migrated declarations.
-const_name.to_s.downcase.freeze
end
sig {returns(T::Boolean)}
def self.started_initializing?
unless defined?(@started_initializing)
@started_initializing = T.let(false, T.nilable(T::Boolean))
end
T.must(@started_initializing)
end
sig {returns(T::Boolean)}
def self.fully_initialized?
unless defined?(@fully_initialized)
@fully_initialized = T.let(false, T.nilable(T::Boolean))
end
T.must(@fully_initialized)
end
# Maintains the order in which values are defined
sig {params(instance: T.untyped).void}
def self._register_instance(instance)
@values ||= []
@values << T.cast(instance, T.attached_class)
end
# Entrypoint for allowing people to register new enum values.
# All enum values must be defined within this block.
sig {params(blk: T.proc.void).void}
def self.enums(&blk)
raise "enums cannot be defined for T::Enum" if self == T::Enum
raise "Enum #{self} was already initialized" if fully_initialized?
raise "Enum #{self} is still initializing" if started_initializing?
@started_initializing = true
@values = T.let(nil, T.nilable(T::Array[T.attached_class]))
yield
@mapping = T.let(nil, T.nilable(T::Hash[SerializedVal, T.attached_class]))
@mapping = {}
# Freeze the Enum class and bind the constant names into each of the instances.
self.constants(false).each do |const_name|
instance = self.const_get(const_name, false)
if !instance.is_a?(self)
raise "Invalid constant #{self}::#{const_name} on enum. " \
"All constants defined for an enum must be instances itself (e.g. `Foo = new`)."
end
instance._bind_name(const_name)
serialized = instance.serialize
if @mapping.include?(serialized)
raise "Enum values must have unique serializations. Value '#{serialized}' is repeated on #{self}."
end
@mapping[serialized] = instance
end
@values.freeze
@mapping.freeze
orphaned_instances = T.must(@values) - @mapping.values
if !orphaned_instances.empty?
raise "Enum values must be assigned to constants: #{orphaned_instances.map {|v| v.instance_variable_get('@serialized_val')}}"
end
@fully_initialized = true
end
sig {params(child_class: Module).void}
def self.inherited(child_class)
super
raise "Inheriting from children of T::Enum is prohibited" if self != T::Enum
end
# Marshal support
sig {params(_level: Integer).returns(String)}
def _dump(_level)
Marshal.dump(serialize)
end
sig {params(args: String).returns(T.attached_class)}
def self._load(args)
deserialize(Marshal.load(args)) # rubocop:disable Security/MarshalLoad
end
end

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
# typed: true
# Use as a mixin with extend (`extend T::Generic`).
module T::Generic
include T::Helpers
include Kernel
### Class/Module Helpers ###
def [](*types)
self
end
def type_member(variance=:invariant, &blk)
T::Types::TypeMember.new(variance)
end
def type_template(variance=:invariant, &blk)
T::Types::TypeTemplate.new(variance)
end
end

View File

@ -0,0 +1,58 @@
# frozen_string_literal: true
# typed: true
# Use as a mixin with extend (`extend T::Helpers`).
# Docs at https://sorbet.org/docs/
module T::Helpers
extend T::Sig
Private = T::Private
### Class/Module Helpers ###
def abstract!
Private::Abstract::Declare.declare_abstract(self, type: :abstract)
end
def interface!
Private::Abstract::Declare.declare_abstract(self, type: :interface)
end
def final!
Private::Final.declare(self)
end
def sealed!
Private::Sealed.declare(self, Kernel.caller(1..1)&.first&.split(':')&.first)
end
# Causes a mixin to also mix in class methods from the named module.
#
# Nearly equivalent to
#
# def self.included(other)
# other.extend(mod)
# end
#
# Except that it is statically analyzed by sorbet.
def mixes_in_class_methods(mod, *mods)
Private::Mixins.declare_mixes_in_class_methods(self, [mod].concat(mods))
end
# Specify an inclusion or inheritance requirement for `self`.
#
# Example:
#
# module MyHelper
# extend T::Helpers
#
# requires_ancestor { Kernel }
# end
#
# class MyClass < BasicObject # error: `MyClass` must include `Kernel` (required by `MyHelper`)
# include MyHelper
# end
#
# TODO: implement the checks in sorbet-runtime.
def requires_ancestor(&block); end
end

View File

@ -0,0 +1,158 @@
# frozen_string_literal: true
# typed: false
# Wraps an object, exposing only the methods defined on a given class/module. The idea is that, in
# the absence of a static type checker that would prevent you from calling non-Bar methods on a
# variable of type Bar, we can use these wrappers as a way of enforcing it at runtime.
#
# Once we ship static type checking, we should get rid of this entirely.
class T::InterfaceWrapper
extend T::Sig
module Helpers
def wrap_instance(obj)
T::InterfaceWrapper.wrap_instance(obj, self)
end
def wrap_instances(arr)
T::InterfaceWrapper.wrap_instances(arr, self)
end
end
private_class_method :new # use `wrap_instance`
def self.wrap_instance(obj, interface_mod)
wrapper = wrapped_dynamic_cast(obj, interface_mod)
if wrapper.nil?
raise "#{obj.class} cannot be cast to #{interface_mod}"
end
wrapper
end
sig do
params(
arr: Array,
interface_mod: T.untyped
)
.returns(Array)
end
def self.wrap_instances(arr, interface_mod)
arr.map {|instance| self.wrap_instance(instance, interface_mod)}
end
def initialize(target_obj, interface_mod)
if target_obj.is_a?(T::InterfaceWrapper)
# wrapped_dynamic_cast should guarantee this never happens.
raise "Unexpected: wrapping a wrapper. Please report this bug at https://github.com/sorbet/sorbet/issues"
end
if !target_obj.is_a?(interface_mod)
# wrapped_dynamic_cast should guarantee this never happens.
raise "Unexpected: `is_a?` failed. Please report this bug at https://github.com/sorbet/sorbet/issues"
end
if target_obj.class == interface_mod
# wrapped_dynamic_cast should guarantee this never happens.
raise "Unexpected: exact class match. Please report this bug at https://github.com/sorbet/sorbet/issues"
end
@target_obj = target_obj
@interface_mod = interface_mod
self_methods = self.class.self_methods
# If perf becomes an issue, we can define these on an anonymous subclass, and keep a cache
# so we only need to do it once per unique `interface_mod`
T::Utils.methods_excluding_object(interface_mod).each do |method_name|
if self_methods.include?(method_name)
raise "interface_mod has a method that conflicts with #{self.class}: #{method_name}"
end
define_singleton_method(method_name) do |*args, &blk|
target_obj.send(method_name, *args, &blk)
end
if target_obj.singleton_class.public_method_defined?(method_name)
# no-op, it's already public
elsif target_obj.singleton_class.protected_method_defined?(method_name)
singleton_class.send(:protected, method_name)
elsif target_obj.singleton_class.private_method_defined?(method_name)
singleton_class.send(:private, method_name)
else
raise "This should never happen. Report this bug at https://github.com/sorbet/sorbet/issues"
end
end
end
def kind_of?(other)
is_a?(other)
end
def is_a?(other)
if !other.is_a?(Module)
raise TypeError.new("class or module required")
end
# This makes is_a? return true for T::InterfaceWrapper (and its ancestors),
# as well as for @interface_mod and its ancestors.
self.class <= other || @interface_mod <= other
end
# Prefixed because we're polluting the namespace of the interface we're wrapping, and we don't
# want anyone else (besides dynamic_cast) calling it.
def __target_obj_DO_NOT_USE # rubocop:disable Naming/MethodName
@target_obj
end
# Prefixed because we're polluting the namespace of the interface we're wrapping, and we don't
# want anyone else (besides wrapped_dynamic_cast) calling it.
def __interface_mod_DO_NOT_USE # rubocop:disable Naming/MethodName
@interface_mod
end
# "Cast" an object to another type. If `obj` is an InterfaceWrapper, returns the the wrapped
# object if that matches `type`. Otherwise, returns `obj` if it matches `type`. Otherwise,
# returns nil.
#
# @param obj [Object] object to cast
# @param mod [Module] type to cast `obj` to
#
# @example
# if (impl = T::InterfaceWrapper.dynamic_cast(iface, MyImplementation))
# impl.do_things
# end
def self.dynamic_cast(obj, mod)
if obj.is_a?(T::InterfaceWrapper)
target_obj = obj.__target_obj_DO_NOT_USE
target_obj.is_a?(mod) ? target_obj : nil
elsif obj.is_a?(mod)
obj
else
nil
end
end
# Like dynamic_cast, but puts the result in its own wrapper if necessary.
#
# @param obj [Object] object to cast
# @param mod [Module] type to cast `obj` to
def self.wrapped_dynamic_cast(obj, mod)
# Avoid unwrapping and creating an equivalent wrapper.
if obj.is_a?(T::InterfaceWrapper) && obj.__interface_mod_DO_NOT_USE == mod
return obj
end
cast_obj = dynamic_cast(obj, mod)
if cast_obj.nil?
nil
elsif cast_obj.class == mod
# Nothing to wrap, they want the full class
cast_obj
else
new(cast_obj, mod)
end
end
def self.self_methods
@self_methods ||= self.instance_methods(false).to_set
end
end

View File

@ -0,0 +1,65 @@
# frozen_string_literal: true
# typed: strict
module T::NonForcingConstants
# NOTE: This method is documented on the RBI in Sorbet's payload, so that it
# shows up in the hover/completion documentation via LSP.
T::Sig::WithoutRuntime.sig {params(val: BasicObject, klass: String, package: T.nilable(String)).returns(T::Boolean)}
def self.non_forcing_is_a?(val, klass, package: nil)
method_name = "T::NonForcingConstants.non_forcing_is_a?"
if klass.empty?
raise ArgumentError.new("The string given to `#{method_name}` must not be empty")
end
# We don't treat packages differently at runtime, but the static
# type-checker still needs to have the package and constant
# separated out. This just re-assembles the string as needed
if !package.nil?
klass = "::#{package}::#{klass}"
end
current_klass = T.let(nil, T.nilable(Module))
current_prefix = T.let(nil, T.nilable(String))
parts = klass.split('::')
parts.each do |part|
if current_klass.nil?
# First iteration
if part != "" && package.nil?
# if we've supplied a package, we're probably running in
# package mode, which means absolute references are
# meaningless
raise ArgumentError.new("The string given to `#{method_name}` must be an absolute constant reference that starts with `::`")
end
current_klass = Object
current_prefix = ''
# if this had a :: prefix, then there's no more loading to
# do---skip to the next one
next if part == ""
end
if current_klass.autoload?(part)
# There's an autoload registered for that constant, which means it's not
# yet loaded. `value` can't be an instance of something not yet loaded.
return false
end
# Sorbet guarantees that the string is an absolutely resolved name.
search_inheritance_chain = false
if !current_klass.const_defined?(part, search_inheritance_chain)
return false
end
current_klass = current_klass.const_get(part)
current_prefix = "#{current_prefix}::#{part}"
if !Module.===(current_klass)
raise ArgumentError.new("#{current_prefix} is not a class or module")
end
end
current_klass.===(val)
end
end

Some files were not shown because too many files have changed in this diff Show More