brew vendor-gems: commit updates.

This commit is contained in:
BrewTestBot 2025-07-11 17:02:23 +00:00
parent d4325c0137
commit f3db247045
No known key found for this signature in database
99 changed files with 896 additions and 273 deletions

View File

@ -167,6 +167,7 @@ GEM
PLATFORMS PLATFORMS
aarch64-linux aarch64-linux
arm-linux
arm64-darwin arm64-darwin
x86_64-darwin x86_64-darwin
x86_64-linux x86_64-linux

View File

@ -37,21 +37,21 @@ end
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/public_suffix-6.0.2/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/public_suffix-6.0.2/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/addressable-2.8.7/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/addressable-2.8.7/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/ast-2.4.3/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/ast-2.4.3/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/base64-0.2.0/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/base64-0.3.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/benchmark-0.4.0/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/benchmark-0.4.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/arm64-darwin-20/#{Gem.extension_api_version}/bigdecimal-3.1.9") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/arm64-darwin-20/#{Gem.extension_api_version}/bigdecimal-3.2.2")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/bigdecimal-3.1.9/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/bigdecimal-3.2.2/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/bindata-2.5.1/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/bindata-2.5.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/coderay-1.1.3/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/coderay-1.1.3/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/csv-3.3.4/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/csv-3.3.5/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/diff-lcs-1.6.2/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/diff-lcs-1.6.2/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/docile-1.4.1/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/docile-1.4.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/elftools-1.3.1/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/elftools-1.3.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/erubi-1.13.1/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/erubi-1.13.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/hana-1.3.7/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/hana-1.3.7/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/arm64-darwin-20/#{Gem.extension_api_version}/json-2.12.0") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/arm64-darwin-20/#{Gem.extension_api_version}/json-2.12.2")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/json-2.12.0/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/json-2.12.2/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/regexp_parser-2.10.0/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/regexp_parser-2.10.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/simpleidn-0.2.3/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/simpleidn-0.2.3/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/json_schemer-2.4.0/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/json_schemer-2.4.0/lib")
@ -64,7 +64,7 @@ $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/minitest-5.25.5/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/minitest-5.25.5/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/netrc-0.11.0/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/netrc-0.11.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/parallel-1.27.0/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/parallel-1.27.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/parallel_tests-5.2.0/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/parallel_tests-5.3.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/arm64-darwin-20/#{Gem.extension_api_version}/racc-1.8.1") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/arm64-darwin-20/#{Gem.extension_api_version}/racc-1.8.1")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/racc-1.8.1/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/racc-1.8.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/parser-3.3.8.0/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/parser-3.3.8.0/lib")
@ -76,48 +76,49 @@ $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/arm64-darwin-20/#{Gem.extension_api_version}/pycall-1.5.2") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/arm64-darwin-20/#{Gem.extension_api_version}/pycall-1.5.2")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/pycall-1.5.2/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/pycall-1.5.2/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rainbow-3.1.1/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rainbow-3.1.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/arm64-darwin-20/#{Gem.extension_api_version}/rbs-3.9.4") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/arm64-darwin-20/#{Gem.extension_api_version}/rbs-4.0.0.dev.4")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rbs-3.9.4/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rbs-4.0.0.dev.4/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/sorbet-runtime-0.5.12117/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rbi-0.3.6/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rbi-0.3.3/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/arm64-darwin-20/#{Gem.extension_api_version}/redcarpet-3.6.1") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/arm64-darwin-20/#{Gem.extension_api_version}/redcarpet-3.6.1")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/redcarpet-3.6.1/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/redcarpet-3.6.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rspec-support-3.13.3/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/require-hooks-0.2.2/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rspec-core-3.13.3/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rspec-support-3.13.4/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rspec-expectations-3.13.4/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rspec-core-3.13.5/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rspec-mocks-3.13.4/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rspec-expectations-3.13.5/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rspec-3.13.0/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rspec-mocks-3.13.5/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rspec-3.13.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rspec-github-3.0.0/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rspec-github-3.0.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rspec-retry-0.6.2/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rspec-retry-0.6.2/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/sorbet-runtime-0.5.12222/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rspec-sorbet-1.9.2/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rspec-sorbet-1.9.2/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rspec_junit_formatter-0.6.0/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rspec_junit_formatter-0.6.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-ast-1.44.1/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-ast-1.45.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/ruby-progressbar-1.13.0/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/ruby-progressbar-1.13.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/unicode-emoji-4.0.4/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/unicode-emoji-4.0.4/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/unicode-display_width-3.1.4/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/unicode-display_width-3.1.4/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-1.75.6/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-1.77.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-md-2.0.1/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-md-2.0.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-performance-1.25.0/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-performance-1.25.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-rspec-3.6.0/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-rspec-3.6.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-sorbet-0.10.0/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-sorbet-0.10.5/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/ruby-lsp-0.23.21/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/ruby-lsp-0.24.2/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/ruby-macho-4.1.0/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/ruby-macho-4.1.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/arm64-darwin-20/#{Gem.extension_api_version}/ruby-prof-1.7.1") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/arm64-darwin-20/#{Gem.extension_api_version}/ruby-prof-1.7.2")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/ruby-prof-1.7.1/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/ruby-prof-1.7.2/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/simplecov-html-0.13.1/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/simplecov-html-0.13.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/simplecov_json_formatter-0.1.4/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/simplecov_json_formatter-0.1.4/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/simplecov-0.22.0/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/simplecov-0.22.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/simplecov-cobertura-2.1.0/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/simplecov-cobertura-2.1.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/sorbet-static-0.5.12117-universal-darwin/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/sorbet-static-0.5.12222-universal-darwin/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/sorbet-0.5.12117/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/sorbet-0.5.12222/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/sorbet-static-and-runtime-0.5.12117/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/sorbet-static-and-runtime-0.5.12222/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/thor-1.3.2/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/thor-1.3.2/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/spoom-1.6.3/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/spoom-1.7.4/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/arm64-darwin-20/#{Gem.extension_api_version}/stackprof-0.2.27") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/arm64-darwin-20/#{Gem.extension_api_version}/stackprof-0.2.27")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/stackprof-0.2.27/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/stackprof-0.2.27/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/yard-0.9.37/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/yard-0.9.37/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/yard-sorbet-0.9.0/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/yard-sorbet-0.9.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/tapioca-0.16.11/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/tapioca-0.17.5/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/arm64-darwin-20/#{Gem.extension_api_version}/vernier-1.7.1") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/arm64-darwin-20/#{Gem.extension_api_version}/vernier-1.8.0")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/vernier-1.7.1/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/vernier-1.8.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/warning-1.5.0/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/warning-1.5.0/lib")

View File

@ -1,22 +0,0 @@
Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

View File

@ -2,7 +2,7 @@
# #
# \Module \Base64 provides methods for: # \Module \Base64 provides methods for:
# #
# - Encoding a binary string (containing non-ASCII characters) # - \Encoding a binary string (containing non-ASCII characters)
# as a string of printable ASCII characters. # as a string of printable ASCII characters.
# - Decoding such an encoded string. # - Decoding such an encoded string.
# #
@ -27,7 +27,7 @@
# #
# require 'base64' # require 'base64'
# #
# == Encoding Character Sets # == \Encoding Character Sets
# #
# A \Base64-encoded string consists only of characters from a 64-character set: # A \Base64-encoded string consists only of characters from a 64-character set:
# #
@ -140,7 +140,7 @@
# Base64.strict_decode64("MDEyMzQ1Njc=") # => "01234567" # Base64.strict_decode64("MDEyMzQ1Njc=") # => "01234567"
# Base64.strict_decode64("MDEyMzQ1Njc==") # Raises ArgumentError # Base64.strict_decode64("MDEyMzQ1Njc==") # Raises ArgumentError
# #
# \Method Base64.urlsafe_decode64 allows padding in +str+, # \Method Base64.urlsafe_decode64 allows padding in the encoded string,
# which if present, must be correct: # which if present, must be correct:
# see {Padding}[Base64.html#module-Base64-label-Padding], above: # see {Padding}[Base64.html#module-Base64-label-Padding], above:
# #
@ -183,11 +183,14 @@
# #
module Base64 module Base64
VERSION = "0.2.0" VERSION = "0.3.0"
module_function module_function
# Returns a string containing the RFC-2045-compliant \Base64-encoding of +bin+. # :call-seq:
# Base64.encode64(string) -> encoded_string
#
# Returns a string containing the RFC-2045-compliant \Base64-encoding of +string+.
# #
# Per RFC 2045, the returned string may contain the URL-unsafe characters # Per RFC 2045, the returned string may contain the URL-unsafe characters
# <tt>+</tt> or <tt>/</tt>; # <tt>+</tt> or <tt>/</tt>;
@ -220,19 +223,22 @@ module Base64
[bin].pack("m") [bin].pack("m")
end end
# :call-seq:
# Base64.decode(encoded_string) -> decoded_string
#
# Returns a string containing the decoding of an RFC-2045-compliant # Returns a string containing the decoding of an RFC-2045-compliant
# \Base64-encoded string +str+: # \Base64-encoded string +encoded_string+:
# #
# s = "VGhpcyBpcyBsaW5lIDEKVGhpcyBpcyBsaW5lIDIK\n" # s = "VGhpcyBpcyBsaW5lIDEKVGhpcyBpcyBsaW5lIDIK\n"
# Base64.decode64(s) # => "This is line 1\nThis is line 2\n" # Base64.decode64(s) # => "This is line 1\nThis is line 2\n"
# #
# Non-\Base64 characters in +str+ are ignored; # Non-\Base64 characters in +encoded_string+ are ignored;
# see {Encoding Character Set}[Base64.html#module-Base64-label-Encoding+Character+Sets] above: # see {Encoding Character Set}[Base64.html#module-Base64-label-Encoding+Character+Sets] above:
# these include newline characters and characters <tt>-</tt> and <tt>/</tt>: # these include newline characters and characters <tt>-</tt> and <tt>/</tt>:
# #
# Base64.decode64("\x00\n-_") # => "" # Base64.decode64("\x00\n-_") # => ""
# #
# Padding in +str+ (even if incorrect) is ignored: # Padding in +encoded_string+ (even if incorrect) is ignored:
# #
# Base64.decode64("MDEyMzQ1Njc") # => "01234567" # Base64.decode64("MDEyMzQ1Njc") # => "01234567"
# Base64.decode64("MDEyMzQ1Njc=") # => "01234567" # Base64.decode64("MDEyMzQ1Njc=") # => "01234567"
@ -242,7 +248,10 @@ module Base64
str.unpack1("m") str.unpack1("m")
end end
# Returns a string containing the RFC-2045-compliant \Base64-encoding of +bin+. # :call-seq:
# Base64.strict_encode64(string) -> encoded_string
#
# Returns a string containing the RFC-2045-compliant \Base64-encoding of +string+.
# #
# Per RFC 2045, the returned string may contain the URL-unsafe characters # Per RFC 2045, the returned string may contain the URL-unsafe characters
# <tt>+</tt> or <tt>/</tt>; # <tt>+</tt> or <tt>/</tt>;
@ -274,13 +283,16 @@ module Base64
[bin].pack("m0") [bin].pack("m0")
end end
# :call-seq:
# Base64.strict_decode64(encoded_string) -> decoded_string
#
# Returns a string containing the decoding of an RFC-2045-compliant # Returns a string containing the decoding of an RFC-2045-compliant
# \Base64-encoded string +str+: # \Base64-encoded string +encoded_string+:
# #
# s = "VGhpcyBpcyBsaW5lIDEKVGhpcyBpcyBsaW5lIDIK" # s = "VGhpcyBpcyBsaW5lIDEKVGhpcyBpcyBsaW5lIDIK"
# Base64.strict_decode64(s) # => "This is line 1\nThis is line 2\n" # Base64.strict_decode64(s) # => "This is line 1\nThis is line 2\n"
# #
# Non-\Base64 characters in +str+ not allowed; # Non-\Base64 characters in +encoded_string+ are not allowed;
# see {Encoding Character Set}[Base64.html#module-Base64-label-Encoding+Character+Sets] above: # see {Encoding Character Set}[Base64.html#module-Base64-label-Encoding+Character+Sets] above:
# these include newline characters and characters <tt>-</tt> and <tt>/</tt>: # these include newline characters and characters <tt>-</tt> and <tt>/</tt>:
# #
@ -288,7 +300,7 @@ module Base64
# Base64.strict_decode64('-') # Raises ArgumentError # Base64.strict_decode64('-') # Raises ArgumentError
# Base64.strict_decode64('_') # Raises ArgumentError # Base64.strict_decode64('_') # Raises ArgumentError
# #
# Padding in +str+, if present, must be correct: # Padding in +encoded_string+, if present, must be correct:
# #
# Base64.strict_decode64("MDEyMzQ1Njc") # Raises ArgumentError # Base64.strict_decode64("MDEyMzQ1Njc") # Raises ArgumentError
# Base64.strict_decode64("MDEyMzQ1Njc=") # => "01234567" # Base64.strict_decode64("MDEyMzQ1Njc=") # => "01234567"
@ -298,7 +310,10 @@ module Base64
str.unpack1("m0") str.unpack1("m0")
end end
# Returns the RFC-4648-compliant \Base64-encoding of +bin+. # :call-seq:
# Base64.urlsafe_encode64(string) -> encoded_string
#
# Returns the RFC-4648-compliant \Base64-encoding of +string+.
# #
# Per RFC 4648, the returned string will not contain the URL-unsafe characters # Per RFC 4648, the returned string will not contain the URL-unsafe characters
# <tt>+</tt> or <tt>/</tt>, # <tt>+</tt> or <tt>/</tt>,
@ -332,16 +347,19 @@ module Base64
str str
end end
# Returns the decoding of an RFC-4648-compliant \Base64-encoded string +str+: # :call-seq:
# Base64.urlsafe_decode64(encoded_string) -> decoded_string
# #
# +str+ may not contain non-Base64 characters; # Returns the decoding of an RFC-4648-compliant \Base64-encoded string +encoded_string+:
#
# +encoded_string+ may not contain non-Base64 characters;
# see {Encoding Character Set}[Base64.html#module-Base64-label-Encoding+Character+Sets] above: # see {Encoding Character Set}[Base64.html#module-Base64-label-Encoding+Character+Sets] above:
# #
# Base64.urlsafe_decode64('+') # Raises ArgumentError. # Base64.urlsafe_decode64('+') # Raises ArgumentError.
# Base64.urlsafe_decode64('/') # Raises ArgumentError. # Base64.urlsafe_decode64('/') # Raises ArgumentError.
# Base64.urlsafe_decode64("\n") # Raises ArgumentError. # Base64.urlsafe_decode64("\n") # Raises ArgumentError.
# #
# Padding in +str+, if present, must be correct: # Padding in +encoded_string+, if present, must be correct:
# see {Padding}[Base64.html#module-Base64-label-Padding], above: # see {Padding}[Base64.html#module-Base64-label-Padding], above:
# #
# Base64.urlsafe_decode64("MDEyMzQ1Njc") # => "01234567" # Base64.urlsafe_decode64("MDEyMzQ1Njc") # => "01234567"

View File

@ -0,0 +1,23 @@
Copyright (c) 2023 Vladimir Dementyev
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,3 @@
# frozen_string_literal: true
require "require-hooks/version"

View File

@ -0,0 +1,132 @@
# frozen_string_literal: true
module RequireHooks
@@around_load = []
@@source_transform = []
@@hijack_load = []
class Context
def initialize(around_load, source_transform, hijack_load)
@around_load = around_load
@source_transform = source_transform
@hijack_load = hijack_load
end
def empty?
@around_load.empty? && @source_transform.empty? && @hijack_load.empty?
end
def source_transform?
@source_transform.any?
end
def hijack?
@hijack_load.any?
end
def run_around_load_callbacks(path)
return yield if @around_load.empty?
chain = @around_load.reverse.inject do |acc_proc, next_proc|
proc { |path, &block| acc_proc.call(path) { next_proc.call(path, &block) } }
end
chain.call(path) { yield }
end
def perform_source_transform(path)
return unless @source_transform.any?
source = nil
@source_transform.each do |transform|
source = transform.call(path, source) || source
end
source
end
def try_hijack_load(path, source)
return unless @hijack_load.any?
@hijack_load.each do |hijack|
result = hijack.call(path, source)
return result if result
end
nil
end
end
class << self
attr_accessor :print_warnings
# Define a block to wrap the code loading.
# The return value MUST be a result of calling the passed block.
# For example, you can use such hooks for instrumentation, debugging purposes.
#
# RequireHooks.around_load do |path, &block|
# puts "Loading #{path}"
# block.call.tap { puts "Loaded #{path}" }
# end
def around_load(patterns: nil, exclude_patterns: nil, &block)
@@around_load << [patterns, exclude_patterns, block]
end
# Define hooks to perform source-to-source transformations.
# The return value MUST be either String (new source code) or nil (indicating that no transformations were performed).
#
# NOTE: The second argument (`source`) MAY be nil, indicating that no transformer tried to transform the source code.
#
# For example, you can prepend each file with `# frozen_string_literal: true` pragma:
#
# RequireHooks.source_transform do |path, source|
# "# frozen_string_literal: true\n#{source}"
# end
def source_transform(patterns: nil, exclude_patterns: nil, &block)
@@source_transform << [patterns, exclude_patterns, block]
end
# This hook should be used to manually compile byte code to be loaded by the VM.
# The arguments are (path, source = nil), where source is only defined if transformations took place.
# Otherwise, you MUST read the source code from the file yourself.
#
# The return value MUST be either nil (continue to the next hook or default behavior) or a platform-specific bytecode object (e.g., RubyVM::InstructionSequence).
#
# RequireHooks.hijack_load do |path, source|
# source ||= File.read(path)
# if defined?(RubyVM::InstructionSequence)
# RubyVM::InstructionSequence.compile(source)
# elsif defined?(JRUBY_VERSION)
# JRuby.compile(source)
# end
# end
def hijack_load(patterns: nil, exclude_patterns: nil, &block)
@@hijack_load << [patterns, exclude_patterns, block]
end
def context_for(path)
around_load = @@around_load.select do |patterns, exclude_patterns, _block|
next unless !patterns || patterns.any? { |pattern| File.fnmatch?(pattern, path) }
next if exclude_patterns&.any? { |pattern| File.fnmatch?(pattern, path) }
true
end.map { |_patterns, _exclude_patterns, block| block }
source_transform = @@source_transform.select do |patterns, exclude_patterns, _block|
next unless !patterns || patterns.any? { |pattern| File.fnmatch?(pattern, path) }
next if exclude_patterns&.any? { |pattern| File.fnmatch?(pattern, path) }
true
end.map { |_patterns, _exclude_patterns, block| block }
hijack_load = @@hijack_load.select do |patterns, exclude_patterns, _block|
next unless !patterns || patterns.any? { |pattern| File.fnmatch?(pattern, path) }
next if exclude_patterns&.any? { |pattern| File.fnmatch?(pattern, path) }
true
end.map { |_patterns, _exclude_patterns, block| block }
Context.new(around_load, source_transform, hijack_load)
end
end
end

View File

@ -0,0 +1,36 @@
# frozen_string_literal: true
module RequireHooks
module Bootsnap
module CompileCacheExt
def input_to_storage(source, path, *)
ctx = RequireHooks.context_for(path)
new_contents = ctx.perform_source_transform(path)
hijacked = ctx.try_hijack_load(path, new_contents)
if hijacked
raise TypeError, "Unsupported bytecode format for #{path}: #{hijack.class}" unless hijacked.is_a?(::RubyVM::InstructionSequence)
return hijacked.to_binary
elsif new_contents
return RubyVM::InstructionSequence.compile(new_contents, path, path, 1).to_binary
end
super
rescue SyntaxError, TypeError
::Bootsnap::CompileCache::UNCOMPILABLE
end
end
module LoadIseqExt
# Around hooks must be performed every time we trigger a file load, even if
# the file is already cached.
def load_iseq(path)
RequireHooks.context_for(path).run_around_load_callbacks(path) { super }
end
end
end
end
Bootsnap::CompileCache::ISeq.singleton_class.prepend(RequireHooks::Bootsnap::CompileCacheExt)
RubyVM::InstructionSequence.singleton_class.prepend(RequireHooks::Bootsnap::LoadIseqExt)

View File

@ -0,0 +1,326 @@
# frozen_string_literal: true
require "mutex_m"
require "pathname"
module RequireHooks
module KernelPatch
class << self
def load(path)
ctx = RequireHooks.context_for(path)
ctx.run_around_load_callbacks(path) do
next load_without_require_hooks(path) unless ctx.source_transform? || ctx.hijack?
new_contents = ctx.perform_source_transform(path)
hijacked = ctx.try_hijack_load(path, new_contents)
return try_evaluate(path, hijacked) if hijacked
if new_contents
evaluate(new_contents, path)
true
else
load_without_require_hooks(path)
end
end
end
private
def try_evaluate(path, bytecode)
if defined?(::RubyVM::InstructionSequence) && bytecode.is_a?(::RubyVM::InstructionSequence)
bytecode.eval
else
raise TypeError, "Unknown bytecode format for #{path}: #{bytecode.inspect}"
end
true
end
if defined?(JRUBY_VERSION) || defined?(TruffleRuby)
def evaluate(code, filepath)
new_toplevel.eval(code, filepath)
end
def new_toplevel
# Create new "toplevel" binding to avoid lexical scope re-use
# (aka "leaking refinements")
eval "proc{binding}.call", TOPLEVEL_BINDING, __FILE__, __LINE__
end
else
def evaluate(code, filepath)
# This is workaround to solve the "leaking refinements" problem in MRI
RubyVM::InstructionSequence.compile(code, filepath).tap do |iseq|
iseq.eval
end
end
end
end
module Features
class Locker
class PathLock
def initialize
@mu = Mutex.new
@resolved = false
end
def owned?
@mu.owned?
end
def locked?
@mu.locked?
end
def lock!
@mu.lock
end
def unlock!
@mu.unlock
end
def resolve!
@resolved = true
end
def resolved?
@resolved
end
end
attr_reader :features, :mu
def initialize
@mu = Mutex.new
@features = {}
end
def lock_feature(fname)
lock = mu.synchronize do
features[fname] ||= PathLock.new
end
# Can this even happen?
return yield(true) if lock.resolved?
# Recursive require
if lock.owned? && lock.locked?
warn "loading in progress, circular require considered harmful: #{fname}" if RequireHooks.print_warnings
return yield(true)
end
lock.lock!
begin
yield(lock.resolved?).tap do
lock.resolve!
end
ensure
lock.unlock!
mu.synchronize do
features.delete(fname)
end
end
end
def locked_feature?(fname)
mu.synchronize { features.key?(fname) }
end
end
LOCK = Locker.new
class << self
def feature_path(path, implitic_ext: true)
path = resolve_feature_path(path, implitic_ext: implitic_ext)
return if path.nil?
return if File.extname(path) != ".rb" && implitic_ext
path
end
# Based on https://github.com/ruby/ruby/blob/b588fd552390c55809719100d803c36bc7430f2f/load.c#L403-L415
def feature_loaded?(feature)
return true if $LOADED_FEATURES.include?(feature) && !LOCK.locked_feature?(feature)
feature = Pathname.new(feature).cleanpath.to_s
efeature = File.expand_path(feature)
# Check absoulute and relative paths
return true if $LOADED_FEATURES.include?(efeature) && !LOCK.locked_feature?(efeature)
candidates = []
$LOADED_FEATURES.each do |lf|
candidates << lf if lf.end_with?("/#{feature}")
end
return false if candidates.empty?
$LOAD_PATH.each do |lp|
lp_feature = File.join(lp, feature)
return true if candidates.include?(lp_feature) && !LOCK.locked_feature?(lp_feature)
end
false
end
private
def lookup_feature_path(path, implitic_ext: true)
path = "#{path}.rb" if File.extname(path).empty? && implitic_ext
# Resolve relative paths only against current directory
if path.match?(/^\.\.?\//)
path = File.expand_path(path)
return path if File.file?(path)
return nil
end
if Pathname.new(path).absolute?
path = File.expand_path(path)
return File.file?(path) ? path : nil
end
# not a relative, not an absolute path — bare path; try looking relative to current dir,
# if it's in the $LOAD_PATH
if $LOAD_PATH.include?(Dir.pwd) && File.file?(path)
return File.expand_path(path)
end
$LOAD_PATH.find do |lp|
lpath = File.join(lp, path)
return File.expand_path(lpath) if File.file?(lpath)
end
end
if $LOAD_PATH.respond_to?(:resolve_feature_path)
def resolve_feature_path(feature, implitic_ext: true)
if implitic_ext
path = $LOAD_PATH.resolve_feature_path(feature)
path.last if path # rubocop:disable Style/SafeNavigation
else
lookup_feature_path(feature, implitic_ext: implitic_ext)
end
rescue LoadError
end
else
def resolve_feature_path(feature, implitic_ext: true)
lookup_feature_path(feature, implitic_ext: implitic_ext)
end
end
end
end
end
end
# Patch Kernel to hijack require/require_relative/load
module Kernel
module_function
alias_method :require_without_require_hooks, :require
# See https://github.com/ruby/ruby/blob/d814722fb8299c4baace3e76447a55a3d5478e3a/load.c#L1181
def require(path)
path = path.to_path if path.respond_to?(:to_path)
raise TypeError unless path.respond_to?(:to_str)
path = path.to_str
raise TypeError unless path.is_a?(::String)
realpath = nil
feature = path
# if extname == ".rb" => lookup feature -> resolve feature -> load
# if extname != ".rb" => append ".rb" - lookup feature -> resolve feature -> lookup orig (no ext) -> resolve orig (no ext) -> load
if File.extname(path) != ".rb"
realpath = RequireHooks::KernelPatch::Features.feature_path(path + ".rb")
if realpath
feature = path + ".rb"
end
end
realpath ||= RequireHooks::KernelPatch::Features.feature_path(path)
return require_without_require_hooks(path) unless realpath
ctx = RequireHooks.context_for(realpath)
return require_without_require_hooks(path) if ctx.empty?
return false if RequireHooks::KernelPatch::Features.feature_loaded?(feature)
RequireHooks::KernelPatch::Features::LOCK.lock_feature(feature) do |loaded|
return false if loaded
$LOADED_FEATURES << realpath
RequireHooks::KernelPatch.load(realpath)
true
end
rescue LoadError => e
$LOADED_FEATURES.delete(realpath) if realpath
warn "RequireHooks failed to require '#{path}': #{e.message}" if RequireHooks.print_warnings
require_without_require_hooks(path)
rescue Errno::ENOENT, Errno::EACCES
raise LoadError, "cannot load such file -- #{path}"
rescue
$LOADED_FEATURES.delete(realpath) if realpath
raise
end
alias_method :require_relative_without_require_hooks, :require_relative
def require_relative(path)
path = path.to_path if path.respond_to?(:to_path)
raise TypeError unless path.respond_to?(:to_str)
path = path.to_str
raise TypeError unless path.is_a?(::String)
return require(path) if Pathname.new(path).absolute?
loc = caller_locations(1..1).first
from = loc.absolute_path || loc.path || File.join(Dir.pwd, "main")
realpath = File.absolute_path(
File.join(
File.dirname(File.absolute_path(from)),
path
)
)
require(realpath)
end
alias_method :load_without_require_hooks, :load
def load(path, wrap = false)
if wrap
warn "RequireHooks does not support `load(smth, wrap: ...)`. Falling back to original `Kernel#load`" if RequireHooks.print_warnings
return load_without_require_hooks(path, wrap)
end
path = path.to_path if path.respond_to?(:to_path)
raise TypeError unless path.respond_to?(:to_str)
path = path.to_str
raise TypeError unless path.is_a?(::String)
realpath =
if path =~ /^\.\.?\//
path
else
RequireHooks::KernelPatch::Features.feature_path(path, implitic_ext: false)
end
return load_without_require_hooks(path, wrap) unless realpath
RequireHooks::KernelPatch.load(realpath)
rescue Errno::ENOENT, Errno::EACCES
raise LoadError, "cannot load such file -- #{path}"
rescue LoadError => e
warn "RuquireHooks failed to load '#{path}': #{e.message}" if RequireHooks.print_warnings
load_without_require_hooks(path)
end
end

View File

@ -0,0 +1,31 @@
# frozen_string_literal: true
module RequireHooks
module LoadIseq
def load_iseq(path)
ctx = RequireHooks.context_for(path)
ctx.run_around_load_callbacks(path) do
if ctx.source_transform? || ctx.hijack?
new_contents = ctx.perform_source_transform(path)
hijacked = ctx.try_hijack_load(path, new_contents)
if hijacked
raise TypeError, "Unsupported bytecode format for #{path}: #{hijack.class}" unless hijacked.is_a?(::RubyVM::InstructionSequence)
return hijacked
elsif new_contents
return RubyVM::InstructionSequence.compile(new_contents, path, path, 1)
end
end
defined?(super) ? super : RubyVM::InstructionSequence.compile_file(path)
end
end
end
end
if RubyVM::InstructionSequence.respond_to?(:load_iseq)
warn "require-hooks: RubyVM::InstructionSequence.load_iseq is already defined. It won't be used by files processed by require-hooks."
end
RubyVM::InstructionSequence.singleton_class.prepend(RequireHooks::LoadIseq)

View File

@ -0,0 +1,28 @@
# frozen_string_literal: true
require "require-hooks/api"
mode = ENV["REQUIRE_HOOKS_MODE"]
case mode
when "patch"
require "require-hooks/mode/kernel_patch"
when "load_iseq"
require "require-hooks/mode/load_iseq"
when "bootsnap"
require "require-hooks/mode/bootsnap"
else
if defined?(::RubyVM::InstructionSequence)
# Check if Bootsnap has been loaded.
# Based on https://github.com/kddeisz/preval/blob/master/lib/preval.rb
if RubyVM::InstructionSequence.respond_to?(:load_iseq) &&
(load_iseq = RubyVM::InstructionSequence.method(:load_iseq)) &&
load_iseq.source_location[0].include?("/bootsnap/")
require "require-hooks/mode/bootsnap"
else
require "require-hooks/mode/load_iseq"
end
else
require "require-hooks/mode/kernel_patch"
end
end

View File

@ -0,0 +1,5 @@
# frozen_string_literal: true
module RequireHooks
VERSION = "0.2.2"
end

View File

@ -27,7 +27,7 @@ module T
def self.any(type_a, type_b, *types) def self.any(type_a, type_b, *types)
type_a = T::Utils.coerce(type_a) type_a = T::Utils.coerce(type_a)
type_b = T::Utils.coerce(type_b) type_b = T::Utils.coerce(type_b)
types = types.map {|t| T::Utils.coerce(t)} if !types.empty? types = types.map { |t| T::Utils.coerce(t) } if !types.empty?
T::Types::Union::Private::Pool.union_of_types(type_a, type_b, types) T::Types::Union::Private::Pool.union_of_types(type_a, type_b, types)
end end

View File

@ -4,5 +4,5 @@
module T module T
# T::Boolean is a type alias helper for the common `T.any(TrueClass, FalseClass)`. # 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. # Defined separately from _types.rb because it has a dependency on T::Types::Union.
Boolean = T.type_alias {T.any(TrueClass, FalseClass)} Boolean = T.type_alias { T.any(TrueClass, FalseClass) }
end end

View File

@ -419,12 +419,12 @@ module T::Configuration
if values.nil? if values.nil?
@scalar_types = values @scalar_types = values
else else
bad_values = values.reject {|v| v.class == String} bad_values = values.reject { |v| v.class == String }
unless bad_values.empty? unless bad_values.empty?
raise ArgumentError.new("Provided values must all be class name strings.") raise ArgumentError.new("Provided values must all be class name strings.")
end end
@scalar_types = values.each_with_object({}) {|x, acc| acc[x] = true}.freeze @scalar_types = values.each_with_object({}) { |x, acc| acc[x] = true }.freeze
end end
end end
@ -449,9 +449,9 @@ module T::Configuration
private_constant :MODULE_NAME private_constant :MODULE_NAME
@default_module_name_mangler = if T::Configuration::AT_LEAST_RUBY_2_7 @default_module_name_mangler = if T::Configuration::AT_LEAST_RUBY_2_7
->(type) {MODULE_NAME.bind_call(type)} ->(type) { MODULE_NAME.bind_call(type) }
else else
->(type) {MODULE_NAME.bind(type).call} # rubocop:disable Performance/BindCall ->(type) { MODULE_NAME.bind(type).call } # rubocop:disable Performance/BindCall
end end
@module_name_mangler = nil @module_name_mangler = nil

View File

@ -44,11 +44,11 @@ class T::Enum
extend T::Props::CustomType extend T::Props::CustomType
# TODO(jez) Might want to restrict this, or make subclasses provide this type # TODO(jez) Might want to restrict this, or make subclasses provide this type
SerializedVal = T.type_alias {T.untyped} SerializedVal = T.type_alias { T.untyped }
private_constant :SerializedVal private_constant :SerializedVal
### Enum class methods ### ### Enum class methods ###
sig {returns(T::Array[T.attached_class])} sig { returns(T::Array[T.attached_class]) }
def self.values def self.values
if @values.nil? if @values.nil?
raise "Attempting to access values of #{self.class} before it has been initialized." \ raise "Attempting to access values of #{self.class} before it has been initialized." \
@ -59,7 +59,7 @@ class T::Enum
# This exists for compatibility with the interface of `Hash` & mostly to support # This exists for compatibility with the interface of `Hash` & mostly to support
# the HashEachMethods Rubocop. # 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]))} 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) def self.each_value(&blk)
if blk if blk
values.each(&blk) values.each(&blk)
@ -72,7 +72,7 @@ class T::Enum
# #
# Note: It would have been nice to make this method final before people started overriding it. # Note: It would have been nice to make this method final before people started overriding it.
# Note: Failed CriticalMethodsNoRuntimeTypingTest # Note: Failed CriticalMethodsNoRuntimeTypingTest
sig {params(serialized_val: SerializedVal).returns(T.nilable(T.attached_class)).checked(:never)} sig { params(serialized_val: SerializedVal).returns(T.nilable(T.attached_class)).checked(:never) }
def self.try_deserialize(serialized_val) def self.try_deserialize(serialized_val)
if @mapping.nil? if @mapping.nil?
raise "Attempting to access serialization map of #{self.class} before it has been initialized." \ raise "Attempting to access serialization map of #{self.class} before it has been initialized." \
@ -88,7 +88,7 @@ class T::Enum
# #
# @return [self] # @return [self]
# @raise [KeyError] if serialized value does not match any instance. # @raise [KeyError] if serialized value does not match any instance.
sig {overridable.params(serialized_val: SerializedVal).returns(T.attached_class).checked(:never)} sig { overridable.params(serialized_val: SerializedVal).returns(T.attached_class).checked(:never) }
def self.from_serialized(serialized_val) def self.from_serialized(serialized_val)
res = try_deserialize(serialized_val) res = try_deserialize(serialized_val)
if res.nil? if res.nil?
@ -99,7 +99,7 @@ class T::Enum
# Note: It would have been nice to make this method final before people started overriding it. # 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. # @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)} sig { overridable.params(serialized_val: SerializedVal).returns(T::Boolean).checked(:never) }
def self.has_serialized?(serialized_val) def self.has_serialized?(serialized_val)
if @mapping.nil? if @mapping.nil?
raise "Attempting to access serialization map of #{self.class} before it has been initialized." \ raise "Attempting to access serialization map of #{self.class} before it has been initialized." \
@ -109,7 +109,7 @@ class T::Enum
end end
# Note: Failed CriticalMethodsNoRuntimeTypingTest # Note: Failed CriticalMethodsNoRuntimeTypingTest
sig {override.params(instance: T.nilable(T::Enum)).returns(SerializedVal).checked(:never)} sig { override.params(instance: T.nilable(T::Enum)).returns(SerializedVal).checked(:never) }
def self.serialize(instance) def self.serialize(instance)
# This is needed otherwise if a Chalk::ODM::Document with a property of the shape # 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 # T::Hash[T.nilable(MyEnum), Integer] and a value that looks like {nil => 0} is
@ -126,7 +126,7 @@ class T::Enum
end end
# Note: Failed CriticalMethodsNoRuntimeTypingTest # Note: Failed CriticalMethodsNoRuntimeTypingTest
sig {override.params(mongo_value: SerializedVal).returns(T.attached_class).checked(:never)} sig { override.params(mongo_value: SerializedVal).returns(T.attached_class).checked(:never) }
def self.deserialize(mongo_value) def self.deserialize(mongo_value)
if self == T::Enum if self == T::Enum
raise "Cannot call T::Enum.deserialize directly. You must call on a specific child class." raise "Cannot call T::Enum.deserialize directly. You must call on a specific child class."
@ -136,46 +136,46 @@ class T::Enum
### Enum instance methods ### ### Enum instance methods ###
sig {returns(T.self_type)} sig { returns(T.self_type) }
def dup def dup
self self
end end
sig {returns(T.self_type).checked(:tests)} sig { returns(T.self_type).checked(:tests) }
def clone def clone
self self
end end
# Note: Failed CriticalMethodsNoRuntimeTypingTest # Note: Failed CriticalMethodsNoRuntimeTypingTest
sig {returns(SerializedVal).checked(:never)} sig { returns(SerializedVal).checked(:never) }
def serialize def serialize
assert_bound! assert_bound!
@serialized_val @serialized_val
end end
sig {params(args: T.untyped).returns(T.untyped)} sig { params(args: T.untyped).returns(T.untyped) }
def to_json(*args) def to_json(*args)
serialize.to_json(*args) serialize.to_json(*args)
end end
sig {params(args: T.untyped).returns(T.untyped)} sig { params(args: T.untyped).returns(T.untyped) }
def as_json(*args) def as_json(*args)
serialized_val = serialize serialized_val = serialize
return serialized_val unless serialized_val.respond_to?(:as_json) return serialized_val unless serialized_val.respond_to?(:as_json)
serialized_val.as_json(*args) serialized_val.as_json(*args)
end end
sig {returns(String)} sig { returns(String) }
def to_s def to_s
inspect inspect
end end
sig {returns(String)} sig { returns(String) }
def inspect def inspect
"#<#{self.class.name}::#{@const_name || '__UNINITIALIZED__'}>" "#<#{self.class.name}::#{@const_name || '__UNINITIALIZED__'}>"
end end
sig {params(other: BasicObject).returns(T.nilable(Integer))} sig { params(other: BasicObject).returns(T.nilable(Integer)) }
def <=>(other) def <=>(other)
case other case other
when self.class when self.class
@ -192,7 +192,7 @@ class T::Enum
# responds to the `to_str` method. It does not actually call `to_str` however. # 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 # See https://ruby-doc.org/core-2.4.0/String.html#method-i-3D-3D
T::Sig::WithoutRuntime.sig {returns(String)} T::Sig::WithoutRuntime.sig { returns(String) }
def to_str def to_str
msg = 'Implicit conversion of Enum instances to strings is not allowed. Call #serialize instead.' msg = 'Implicit conversion of Enum instances to strings is not allowed. Call #serialize instead.'
if T::Configuration.legacy_t_enum_migration_mode? if T::Configuration.legacy_t_enum_migration_mode?
@ -200,7 +200,7 @@ class T::Enum
msg, msg,
storytime: { storytime: {
class: self.class.name, class: self.class.name,
caller_location: Kernel.caller_locations(1..1)&.[](0)&.then {"#{_1.path}:#{_1.lineno}"}, caller_location: Kernel.caller_locations(1..1)&.[](0)&.then { "#{_1.path}:#{_1.lineno}" },
}, },
) )
serialize.to_s serialize.to_s
@ -217,12 +217,12 @@ class T::Enum
if T.unsafe(false) if T.unsafe(false)
# Declare to the type system that the `serialize` method for sure exists # Declare to the type system that the `serialize` method for sure exists
# on whatever we mix this into. # on whatever we mix this into.
T::Sig::WithoutRuntime.sig {abstract.returns(T.untyped)} T::Sig::WithoutRuntime.sig { abstract.returns(T.untyped) }
def serialize; end def serialize; end
end end
# WithoutRuntime so that comparison_assertion_failed can assume a constant stack depth # WithoutRuntime so that comparison_assertion_failed can assume a constant stack depth
T::Sig::WithoutRuntime.sig {params(other: BasicObject).returns(T::Boolean)} T::Sig::WithoutRuntime.sig { params(other: BasicObject).returns(T::Boolean) }
def ==(other) def ==(other)
case other case other
when String when String
@ -238,7 +238,7 @@ class T::Enum
end end
# WithoutRuntime so that comparison_assertion_failed can assume a constant stack depth # WithoutRuntime so that comparison_assertion_failed can assume a constant stack depth
T::Sig::WithoutRuntime.sig {params(other: BasicObject).returns(T::Boolean)} T::Sig::WithoutRuntime.sig { params(other: BasicObject).returns(T::Boolean) }
def ===(other) def ===(other)
case other case other
when String when String
@ -255,7 +255,7 @@ class T::Enum
# WithoutRuntime so that caller_locations can assume a constant stack depth # WithoutRuntime so that caller_locations can assume a constant stack depth
# (Otherwise, the first call would be the method with the wrapping, which would have a different stack depth.) # (Otherwise, the first call would be the method with the wrapping, which would have a different stack depth.)
T::Sig::WithoutRuntime.sig {params(method: Symbol, other: T.untyped).void} T::Sig::WithoutRuntime.sig { params(method: Symbol, other: T.untyped).void }
private def comparison_assertion_failed(method, other) private def comparison_assertion_failed(method, other)
T::Configuration.soft_assert_handler( T::Configuration.soft_assert_handler(
'Enum to string comparison not allowed. Compare to the Enum instance directly instead. See go/enum-migration', 'Enum to string comparison not allowed. Compare to the Enum instance directly instead. See go/enum-migration',
@ -265,7 +265,7 @@ class T::Enum
other: other, other: other,
other_class: other.class.name, other_class: other.class.name,
method: method, method: method,
caller_location: Kernel.caller_locations(2..2)&.[](0)&.then {"#{_1.path}:#{_1.lineno}"}, caller_location: Kernel.caller_locations(2..2)&.[](0)&.then { "#{_1.path}:#{_1.lineno}" },
} }
) )
end end
@ -276,7 +276,7 @@ class T::Enum
UNSET = T.let(Module.new.freeze, Module) UNSET = T.let(Module.new.freeze, Module)
private_constant :UNSET private_constant :UNSET
sig {params(serialized_val: SerializedVal).void} sig { params(serialized_val: SerializedVal).void }
def initialize(serialized_val=UNSET) def initialize(serialized_val=UNSET)
raise 'T::Enum is abstract' if self.class == T::Enum raise 'T::Enum is abstract' if self.class == T::Enum
if !self.class.started_initializing? if !self.class.started_initializing?
@ -292,7 +292,7 @@ class T::Enum
self.class._register_instance(self) self.class._register_instance(self)
end end
sig {returns(NilClass).checked(:never)} sig { returns(NilClass).checked(:never) }
private def assert_bound! private def assert_bound!
if @const_name.nil? if @const_name.nil?
raise "Attempting to access Enum value on #{self.class} before it has been initialized." \ raise "Attempting to access Enum value on #{self.class} before it has been initialized." \
@ -300,14 +300,14 @@ class T::Enum
end end
end end
sig {params(const_name: Symbol).void} sig { params(const_name: Symbol).void }
def _bind_name(const_name) def _bind_name(const_name)
@const_name = const_name @const_name = const_name
@serialized_val = const_to_serialized_val(const_name) if @serialized_val.equal?(UNSET) @serialized_val = const_to_serialized_val(const_name) if @serialized_val.equal?(UNSET)
freeze freeze
end end
sig {params(const_name: Symbol).returns(String)} sig { params(const_name: Symbol).returns(String) }
private def const_to_serialized_val(const_name) private def const_to_serialized_val(const_name)
# Historical note: We convert to lowercase names because the majority of existing calls to # 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 # `make_accessible` were arrays of lowercase strings. Doing this conversion allowed for the
@ -315,7 +315,7 @@ class T::Enum
-const_name.to_s.downcase.freeze -const_name.to_s.downcase.freeze
end end
sig {returns(T::Boolean)} sig { returns(T::Boolean) }
def self.started_initializing? def self.started_initializing?
unless defined?(@started_initializing) unless defined?(@started_initializing)
@started_initializing = T.let(false, T.nilable(T::Boolean)) @started_initializing = T.let(false, T.nilable(T::Boolean))
@ -323,7 +323,7 @@ class T::Enum
T.must(@started_initializing) T.must(@started_initializing)
end end
sig {returns(T::Boolean)} sig { returns(T::Boolean) }
def self.fully_initialized? def self.fully_initialized?
unless defined?(@fully_initialized) unless defined?(@fully_initialized)
@fully_initialized = T.let(false, T.nilable(T::Boolean)) @fully_initialized = T.let(false, T.nilable(T::Boolean))
@ -332,7 +332,7 @@ class T::Enum
end end
# Maintains the order in which values are defined # Maintains the order in which values are defined
sig {params(instance: T.untyped).void} sig { params(instance: T.untyped).void }
def self._register_instance(instance) def self._register_instance(instance)
@values ||= [] @values ||= []
@values << T.cast(instance, T.attached_class) @values << T.cast(instance, T.attached_class)
@ -340,7 +340,7 @@ class T::Enum
# Entrypoint for allowing people to register new enum values. # Entrypoint for allowing people to register new enum values.
# All enum values must be defined within this block. # All enum values must be defined within this block.
sig {params(blk: T.proc.void).void} sig { params(blk: T.proc.void).void }
def self.enums(&blk) def self.enums(&blk)
raise "enums cannot be defined for T::Enum" if self == T::Enum raise "enums cannot be defined for T::Enum" if self == T::Enum
raise "Enum #{self} was already initialized" if fully_initialized? raise "Enum #{self} was already initialized" if fully_initialized?
@ -375,13 +375,13 @@ class T::Enum
orphaned_instances = T.must(@values) - @mapping.values orphaned_instances = T.must(@values) - @mapping.values
if !orphaned_instances.empty? if !orphaned_instances.empty?
raise "Enum values must be assigned to constants: #{orphaned_instances.map {|v| v.instance_variable_get('@serialized_val')}}" raise "Enum values must be assigned to constants: #{orphaned_instances.map { |v| v.instance_variable_get('@serialized_val') }}"
end end
@fully_initialized = true @fully_initialized = true
end end
sig {params(child_class: T::Class[T.anything]).void} sig { params(child_class: T::Class[T.anything]).void }
def self.inherited(child_class) def self.inherited(child_class)
super super
@ -394,12 +394,12 @@ class T::Enum
end end
# Marshal support # Marshal support
sig {params(_level: Integer).returns(String)} sig { params(_level: Integer).returns(String) }
def _dump(_level) def _dump(_level)
Marshal.dump(serialize) Marshal.dump(serialize)
end end
sig {params(args: String).returns(T.attached_class)} sig { params(args: String).returns(T.attached_class) }
def self._load(args) def self._load(args)
deserialize(Marshal.load(args)) # rubocop:disable Security/MarshalLoad deserialize(Marshal.load(args)) # rubocop:disable Security/MarshalLoad
end end

View File

@ -4,7 +4,7 @@
module T::NonForcingConstants module T::NonForcingConstants
# NOTE: This method is documented on the RBI in Sorbet's payload, so that it # NOTE: This method is documented on the RBI in Sorbet's payload, so that it
# shows up in the hover/completion documentation via LSP. # shows up in the hover/completion documentation via LSP.
T::Sig::WithoutRuntime.sig {params(val: BasicObject, klass: String).returns(T::Boolean)} T::Sig::WithoutRuntime.sig { params(val: BasicObject, klass: String).returns(T::Boolean) }
def self.non_forcing_is_a?(val, klass) def self.non_forcing_is_a?(val, klass)
method_name = "T::NonForcingConstants.non_forcing_is_a?" method_name = "T::NonForcingConstants.non_forcing_is_a?"
if klass.empty? if klass.empty?

View File

@ -25,7 +25,7 @@ module T::Private::Methods
# twice is permitted). we could do this with two tables, but it seems slightly # twice is permitted). we could do this with two tables, but it seems slightly
# cleaner with a single table. # cleaner with a single table.
# Effectively T::Hash[Module, T.nilable(Set))] # Effectively T::Hash[Module, T.nilable(Set))]
@modules_with_final = Hash.new {|hash, key| hash[key] = nil}.compare_by_identity @modules_with_final = Hash.new { |hash, key| hash[key] = nil }.compare_by_identity
# this stores the old [included, extended] hooks for Module and inherited hook for Class that we override when # this stores the old [included, extended] hooks for Module and inherited hook for Class that we override when
# enabling final checks for when those hooks are called. the 'hooks' here don't have anything to do with the 'hooks' # enabling final checks for when those hooks are called. the 'hooks' here don't have anything to do with the 'hooks'
# in installed_hooks. # in installed_hooks.
@ -168,7 +168,7 @@ module T::Private::Methods
definition_file, definition_line = T::Private::Methods.signature_for_method(ancestor.instance_method(method_name)).method.source_location definition_file, definition_line = T::Private::Methods.signature_for_method(ancestor.instance_method(method_name)).method.source_location
is_redefined = target == ancestor is_redefined = target == ancestor
caller_loc = T::Private::CallerUtils.find_caller {|loc| !loc.path.to_s.start_with?(SORBET_RUNTIME_LIB_PATH)} caller_loc = T::Private::CallerUtils.find_caller { |loc| !loc.path.to_s.start_with?(SORBET_RUNTIME_LIB_PATH) }
extra_info = "\n" extra_info = "\n"
if caller_loc if caller_loc
extra_info = (is_redefined ? "Redefined" : "Overridden") + " here: #{caller_loc.path}:#{caller_loc.lineno}\n" extra_info = (is_redefined ? "Redefined" : "Overridden") + " here: #{caller_loc.path}:#{caller_loc.lineno}\n"
@ -471,7 +471,7 @@ module T::Private::Methods
end end
def self.all_checked_tests_sigs def self.all_checked_tests_sigs
@signatures_by_method.values.select {|sig| sig.check_level == :tests} @signatures_by_method.values.select { |sig| sig.check_level == :tests }
end end
# the module target is adding the methods from the module source to itself. we need to check that for all instance # the module target is adding the methods from the module source to itself. we need to check that for all instance
@ -589,6 +589,22 @@ module T::Private::Methods
mod.extend(SingletonMethodHooks) mod.extend(SingletonMethodHooks)
end end
# `name` must be an instance method (for class methods, pass in mod.singleton_class)
def self.visibility_method_name(mod, name)
if mod.public_method_defined?(name)
:public
elsif mod.protected_method_defined?(name)
:protected
elsif mod.private_method_defined?(name)
:private
else
# Raises a NameError formatted like the Ruby VM would (the exact text formatting
# of these errors changed across Ruby VM versions, in ways that would sometimes
# cause tests to fail if they were dependent on hard coding errors).
mod.method(name)
end
end
# use this directly if you don't want/need to box up the method into an object to pass to method_to_key. # use this directly if you don't want/need to box up the method into an object to pass to method_to_key.
private_class_method def self.method_owner_and_name_to_key(owner, name) private_class_method def self.method_owner_and_name_to_key(owner, name)
"#{owner.object_id}##{name}" "#{owner.object_id}##{name}"

View File

@ -12,7 +12,7 @@ module T::Private::Methods::CallValidation
# @param method_sig [T::Private::Methods::Signature] # @param method_sig [T::Private::Methods::Signature]
# @return [UnboundMethod] the new wrapper method (or the original one if we didn't wrap it) # @return [UnboundMethod] the new wrapper method (or the original one if we didn't wrap it)
def self.wrap_method_if_needed(mod, method_sig, original_method) def self.wrap_method_if_needed(mod, method_sig, original_method)
original_visibility = visibility_method_name(mod, method_sig.method_name) original_visibility = T::Private::Methods.visibility_method_name(mod, method_sig.method_name)
if method_sig.mode == T::Private::Methods::Modes.abstract if method_sig.mode == T::Private::Methods::Modes.abstract
create_abstract_wrapper(mod, method_sig, original_method, original_visibility) create_abstract_wrapper(mod, method_sig, original_method, original_visibility)
# Do nothing in this case; this method was not wrapped in _on_method_added. # Do nothing in this case; this method was not wrapped in _on_method_added.
@ -69,11 +69,11 @@ module T::Private::Methods::CallValidation
def self.create_validator_method(mod, original_method, method_sig, original_visibility) def self.create_validator_method(mod, original_method, method_sig, original_visibility)
has_fixed_arity = method_sig.kwarg_types.empty? && !method_sig.has_rest && !method_sig.has_keyrest && has_fixed_arity = method_sig.kwarg_types.empty? && !method_sig.has_rest && !method_sig.has_keyrest &&
original_method.parameters.all? {|(kind, _name)| kind == :req || kind == :block} original_method.parameters.all? { |(kind, _name)| kind == :req || kind == :block }
can_skip_block_type = method_sig.block_type.nil? || method_sig.block_type.valid?(nil) can_skip_block_type = method_sig.block_type.nil? || method_sig.block_type.valid?(nil)
ok_for_fast_path = has_fixed_arity && can_skip_block_type && !method_sig.bind && method_sig.arg_types.length < 5 && is_allowed_to_have_fast_path ok_for_fast_path = has_fixed_arity && can_skip_block_type && !method_sig.bind && method_sig.arg_types.length < 5 && is_allowed_to_have_fast_path
all_args_are_simple = ok_for_fast_path && method_sig.arg_types.all? {|_name, type| type.is_a?(T::Types::Simple)} all_args_are_simple = ok_for_fast_path && method_sig.arg_types.all? { |_name, type| type.is_a?(T::Types::Simple) }
simple_method = all_args_are_simple && method_sig.return_type.is_a?(T::Types::Simple) simple_method = all_args_are_simple && method_sig.return_type.is_a?(T::Types::Simple)
simple_procedure = all_args_are_simple && method_sig.return_type.is_a?(T::Private::Types::Void) simple_procedure = all_args_are_simple && method_sig.return_type.is_a?(T::Private::Types::Void)
@ -330,19 +330,6 @@ module T::Private::Methods::CallValidation
location: caller_loc location: caller_loc
) )
end end
# `name` must be an instance method (for class methods, pass in mod.singleton_class)
private_class_method def self.visibility_method_name(mod, name)
if mod.public_method_defined?(name)
:public
elsif mod.protected_method_defined?(name)
:protected
elsif mod.private_method_defined?(name)
:private
else
mod.method(name) # Raises
end
end
end end
if T::Configuration::AT_LEAST_RUBY_2_7 if T::Configuration::AT_LEAST_RUBY_2_7

View File

@ -154,7 +154,12 @@ module T::Private::Methods
case decl.mode case decl.mode
when Modes.standard when Modes.standard
decl.mode = Modes.override decl.mode = Modes.override
case allow_incompatible
when true, false, :visibility
decl.override_allow_incompatible = allow_incompatible decl.override_allow_incompatible = allow_incompatible
else
raise BuilderError.new(".override(allow_incompatible: ...) only accepts `true`, `false`, or `:visibility`, got: #{allow_incompatible.inspect}")
end
when Modes.override, Modes.overridable_override when Modes.override, Modes.overridable_override
raise BuilderError.new(".override cannot be repeated in a single signature") raise BuilderError.new(".override cannot be repeated in a single signature")
when Modes.overridable when Modes.overridable

View File

@ -68,14 +68,14 @@ class T::Private::Methods::Signature
writer_method = !(raw_arg_types.size == 1 && raw_arg_types.key?(nil)) && parameters == UNNAMED_REQUIRED_PARAMETERS && method_name[-1] == "=" writer_method = !(raw_arg_types.size == 1 && raw_arg_types.key?(nil)) && parameters == UNNAMED_REQUIRED_PARAMETERS && method_name[-1] == "="
# For writer methods, map the single parameter to the method name without the "=" at the end # For writer methods, map the single parameter to the method name without the "=" at the end
parameters = [[:req, method_name[0...-1].to_sym]] if writer_method parameters = [[:req, method_name[0...-1].to_sym]] if writer_method
is_name_missing = parameters.any? {|_, name| !raw_arg_types.key?(name)} is_name_missing = parameters.any? { |_, name| !raw_arg_types.key?(name) }
if is_name_missing if is_name_missing
param_names = parameters.map {|_, name| name} param_names = parameters.map { |_, name| name }
missing_names = param_names - raw_arg_types.keys missing_names = param_names - raw_arg_types.keys
raise "The declaration for `#{method.name}` is missing parameter(s): #{missing_names.join(', ')}" raise "The declaration for `#{method.name}` is missing parameter(s): #{missing_names.join(', ')}"
elsif parameters.length != raw_arg_types.size elsif parameters.length != raw_arg_types.size
param_names = parameters.map {|_, name| name} param_names = parameters.map { |_, name| name }
has_extra_names = parameters.count {|_, name| raw_arg_types.key?(name)} < raw_arg_types.size has_extra_names = parameters.count { |_, name| raw_arg_types.key?(name) } < raw_arg_types.size
if has_extra_names if has_extra_names
extra_names = raw_arg_types.keys - param_names extra_names = raw_arg_types.keys - param_names
raise "The declaration for `#{method.name}` has extra parameter(s): #{extra_names.join(', ')}" raise "The declaration for `#{method.name}` has extra parameter(s): #{extra_names.join(', ')}"
@ -95,7 +95,7 @@ class T::Private::Methods::Signature
# always precede optional keyword arguments. We can't tell # always precede optional keyword arguments. We can't tell
# whether the culprit is the Ruby reordering or user error, so # whether the culprit is the Ruby reordering or user error, so
# we error but include a note # we error but include a note
if param_kind == :keyreq && parameters.any? {|k, _| k == :key} if param_kind == :keyreq && parameters.any? { |k, _| k == :key }
hint = "\n\nNote: Any required keyword arguments must precede any optional keyword " \ hint = "\n\nNote: Any required keyword arguments must precede any optional keyword " \
"arguments. If your method declaration matches your `def`, try reordering any " \ "arguments. If your method declaration matches your `def`, try reordering any " \
"optional keyword parameters to the end of the method list." "optional keyword parameters to the end of the method list."
@ -103,7 +103,7 @@ class T::Private::Methods::Signature
raise "Parameter `#{type_name}` is declared out of order (declared as arg number " \ raise "Parameter `#{type_name}` is declared out of order (declared as arg number " \
"#{i + 1}, defined in the method as arg number " \ "#{i + 1}, defined in the method as arg number " \
"#{parameters.index {|_, name| name == type_name} + 1}).#{hint}\nMethod: #{method_desc}" "#{parameters.index { |_, name| name == type_name } + 1}).#{hint}\nMethod: #{method_desc}"
end end
type = T::Utils.coerce(raw_type) type = T::Utils.coerce(raw_type)
@ -245,8 +245,8 @@ class T::Private::Methods::Signature
end end
def force_type_init def force_type_init
@arg_types.each {|_, type| type.build_type} @arg_types.each { |_, type| type.build_type }
@kwarg_types.each {|_, type| type.build_type} @kwarg_types.each { |_, type| type.build_type }
@block_type&.build_type @block_type&.build_type
@rest_type&.build_type @rest_type&.build_type
@keyrest_type&.build_type @keyrest_type&.build_type

View File

@ -72,7 +72,7 @@ module T::Private::Methods::SignatureValidation
# If the super_method has any kwargs we can't build a # If the super_method has any kwargs we can't build a
# Signature for it, so we'll just skip validation in that case. # Signature for it, so we'll just skip validation in that case.
if !super_signature && !super_method.parameters.select {|kind, _| kind == :rest || kind == :kwrest}.empty? if !super_signature && !super_method.parameters.select { |kind, _| kind == :rest || kind == :kwrest }.empty?
nil nil
else else
# super_signature can be nil when we're overriding a method (perhaps a builtin) that didn't use # super_signature can be nil when we're overriding a method (perhaps a builtin) that didn't use
@ -89,6 +89,7 @@ module T::Private::Methods::SignatureValidation
validate_override_mode(signature, super_signature) validate_override_mode(signature, super_signature)
validate_override_shape(signature, super_signature) validate_override_shape(signature, super_signature)
validate_override_types(signature, super_signature) validate_override_types(signature, super_signature)
validate_override_visibility(signature, super_signature)
end end
else else
validate_non_override_mode(signature) validate_non_override_mode(signature)
@ -175,7 +176,7 @@ module T::Private::Methods::SignatureValidation
end end
def self.validate_override_shape(signature, super_signature) def self.validate_override_shape(signature, super_signature)
return if signature.override_allow_incompatible return if signature.override_allow_incompatible == true
return if super_signature.mode == Modes.untyped return if super_signature.mode == Modes.untyped
method_name = signature.method_name method_name = signature.method_name
@ -231,7 +232,7 @@ module T::Private::Methods::SignatureValidation
end end
def self.validate_override_types(signature, super_signature) def self.validate_override_types(signature, super_signature)
return if signature.override_allow_incompatible return if signature.override_allow_incompatible == true
return if super_signature.mode == Modes.untyped return if super_signature.mode == Modes.untyped
return unless [signature, super_signature].all? do |sig| return unless [signature, super_signature].all? do |sig|
sig.check_level == :always || (sig.check_level == :tests && T::Private::RuntimeLevels.check_tests?) sig.check_level == :always || (sig.check_level == :tests && T::Private::RuntimeLevels.check_tests?)
@ -276,6 +277,38 @@ module T::Private::Methods::SignatureValidation
end end
end end
def self.validate_override_visibility(signature, super_signature)
return if super_signature.mode == Modes.untyped
# This departs from the behavior of other `validate_override_whatever` functions in that it
# only comes into effect when the child signature explicitly says the word `override`. This was
# done because the primary method for silencing these errors (`allow_incompatible: :visibility`)
# requires an `override` node to attach to. Once we have static override checking for implicitly
# overridden methods, we can remove this.
return unless [Modes.override, Modes.overridable_override].include?(signature.mode)
return if [:visibility, true].include?(signature.override_allow_incompatible)
method = signature.method
super_method = super_signature.method
mode_noun = super_signature.mode == Modes.abstract ? 'implementation' : 'override'
vis = method_visibility(method)
super_vis = method_visibility(super_method)
if visibility_strength(vis) > visibility_strength(super_vis)
raise "Incompatible visibility for #{mode_noun} of method #{method.name}\n" \
"* Base: #{super_vis} (in #{method_loc_str(super_method)})\n" \
"* #{mode_noun.capitalize}: #{vis} (in #{method_loc_str(method)})\n" \
"(The override must be at least as permissive as the supermethod)" \
end
end
private_class_method def self.method_visibility(method)
T::Private::Methods.visibility_method_name(method.owner, method.name)
end
# Higher = more restrictive.
private_class_method def self.visibility_strength(vis)
%i[public protected private].find_index(vis)
end
private_class_method def self.base_override_loc_str(signature, super_signature) private_class_method def self.base_override_loc_str(signature, super_signature)
mode_noun = super_signature.mode == Modes.abstract ? 'Implementation' : 'Override' mode_noun = super_signature.mode == Modes.abstract ? 'Implementation' : 'Override'
"\n * Base definition: in #{method_loc_str(super_signature.method)}" \ "\n * Base definition: in #{method_loc_str(super_signature.method)}" \

View File

@ -5,7 +5,7 @@ module T::Private
module MixesInClassMethods module MixesInClassMethods
def included(other) def included(other)
mods = Abstract::Data.get(self, :class_methods_mixins) mods = Abstract::Data.get(self, :class_methods_mixins)
mods.each {|mod| other.extend(mod)} mods.each { |mod| other.extend(mod) }
super super
end end
end end

View File

@ -32,7 +32,7 @@ module T::Private::RuntimeLevels
def self.enable_checking_in_tests def self.enable_checking_in_tests
if !@check_tests && @wrapped_tests_with_validation if !@check_tests && @wrapped_tests_with_validation
all_checked_tests_sigs = T::Private::Methods.all_checked_tests_sigs all_checked_tests_sigs = T::Private::Methods.all_checked_tests_sigs
locations = all_checked_tests_sigs.map {|sig| sig.method.source_location.join(':')}.join("\n- ") locations = all_checked_tests_sigs.map { |sig| sig.method.source_location.join(':') }.join("\n- ")
raise "Toggle `:tests`-level runtime type checking earlier. " \ raise "Toggle `:tests`-level runtime type checking earlier. " \
"There are already some methods wrapped with `sig.checked(:tests)`:\n" \ "There are already some methods wrapped with `sig.checked(:tests)`:\n" \
"- #{locations}" "- #{locations}"

View File

@ -5,7 +5,7 @@ module T::Private::Sealed
module NoInherit module NoInherit
def inherited(child) def inherited(child)
super super
caller_loc = T::Private::CallerUtils.find_caller {|loc| loc.base_label != 'inherited'} caller_loc = T::Private::CallerUtils.find_caller { |loc| loc.base_label != 'inherited' }
T::Private::Sealed.validate_inheritance(caller_loc, self, child, 'inherited') T::Private::Sealed.validate_inheritance(caller_loc, self, child, 'inherited')
@sorbet_sealed_module_all_subclasses << child @sorbet_sealed_module_all_subclasses << child
end end
@ -22,14 +22,14 @@ module T::Private::Sealed
module NoIncludeExtend module NoIncludeExtend
def included(child) def included(child)
super super
caller_loc = T::Private::CallerUtils.find_caller {|loc| loc.base_label != 'included'} caller_loc = T::Private::CallerUtils.find_caller { |loc| loc.base_label != 'included' }
T::Private::Sealed.validate_inheritance(caller_loc, self, child, 'included') T::Private::Sealed.validate_inheritance(caller_loc, self, child, 'included')
@sorbet_sealed_module_all_subclasses << child @sorbet_sealed_module_all_subclasses << child
end end
def extended(child) def extended(child)
super super
caller_loc = T::Private::CallerUtils.find_caller {|loc| loc.base_label != 'extended'} caller_loc = T::Private::CallerUtils.find_caller { |loc| loc.base_label != 'extended' }
T::Private::Sealed.validate_inheritance(caller_loc, self, child, 'extended') T::Private::Sealed.validate_inheritance(caller_loc, self, child, 'extended')
@sorbet_sealed_module_all_subclasses << child @sorbet_sealed_module_all_subclasses << child
end end
@ -81,7 +81,7 @@ module T::Private::Sealed
if !this_file.start_with?(decl_file) if !this_file.start_with?(decl_file)
whitelist = T::Configuration.sealed_violation_whitelist whitelist = T::Configuration.sealed_violation_whitelist
if !whitelist.nil? && whitelist.any? {|pattern| this_file =~ pattern} if !whitelist.nil? && whitelist.any? { |pattern| this_file =~ pattern }
return return
end end

View File

@ -109,7 +109,7 @@ module T::Props
# form. # form.
# #
# @return [void] # @return [void]
sig {params(name: Symbol, cls: T.untyped, rules: T.untyped).void} sig { params(name: Symbol, cls: T.untyped, rules: T.untyped).void }
def prop(name, cls, **rules) def prop(name, cls, **rules)
cls = T::Utils.coerce(cls) if !cls.is_a?(Module) cls = T::Utils.coerce(cls) if !cls.is_a?(Module)
decorator.prop_defined(name, cls, rules) decorator.prop_defined(name, cls, rules)
@ -132,7 +132,7 @@ module T::Props
end end
# Shorthand helper to define a `prop` with `immutable => true` # Shorthand helper to define a `prop` with `immutable => true`
sig {params(name: Symbol, cls_or_args: T.untyped, args: T.untyped).void} sig { params(name: Symbol, cls_or_args: T.untyped, args: T.untyped).void }
def const(name, cls_or_args, **args) def const(name, cls_or_args, **args)
if (cls_or_args.is_a?(Hash) && cls_or_args.key?(:immutable)) || args.key?(:immutable) if (cls_or_args.is_a?(Hash) && cls_or_args.key?(:immutable)) || args.key?(:immutable)
Kernel.raise ArgumentError.new("Cannot pass 'immutable' argument when using 'const' keyword to define a prop") Kernel.raise ArgumentError.new("Cannot pass 'immutable' argument when using 'const' keyword to define a prop")

View File

@ -15,7 +15,7 @@ module T::Props::Constructor::DecoratorMethods
# we'll use to check for any unrecognized input.) # we'll use to check for any unrecognized input.)
# #
# checked(:never) - O(runtime object construction) # checked(:never) - O(runtime object construction)
sig {params(instance: T::Props::Constructor, hash: T::Hash[Symbol, T.untyped]).returns(Integer).checked(:never)} sig { params(instance: T::Props::Constructor, hash: T::Hash[Symbol, T.untyped]).returns(Integer).checked(:never) }
def construct_props_without_defaults(instance, hash) def construct_props_without_defaults(instance, hash)
# Use `each_pair` rather than `count` because, as of Ruby 2.6, the latter delegates to Enumerator # Use `each_pair` rather than `count` because, as of Ruby 2.6, the latter delegates to Enumerator
# and therefore allocates for each entry. # and therefore allocates for each entry.

View File

@ -39,7 +39,7 @@ module T::Props
# #
# @param [Object] instance # @param [Object] instance
# @return An instance of one of T::Configuration.scalar_types # @return An instance of one of T::Configuration.scalar_types
sig {abstract.params(instance: T.untyped).returns(T.untyped).checked(:never)} sig { abstract.params(instance: T.untyped).returns(T.untyped).checked(:never) }
def serialize(instance); end def serialize(instance); end
# Given the serialized form of your type, this returns an instance # Given the serialized form of your type, this returns an instance
@ -47,17 +47,17 @@ module T::Props
# #
# @param scalar One of T::Configuration.scalar_types # @param scalar One of T::Configuration.scalar_types
# @return Object # @return Object
sig {abstract.params(scalar: T.untyped).returns(T.untyped).checked(:never)} sig { abstract.params(scalar: T.untyped).returns(T.untyped).checked(:never) }
def deserialize(scalar); end def deserialize(scalar); end
sig {override.params(_base: Module).void} sig { override.params(_base: Module).void }
def self.included(_base) def self.included(_base)
super super
raise 'Please use "extend", not "include" to attach this module' raise 'Please use "extend", not "include" to attach this module'
end end
sig(:final) {params(val: T.untyped).returns(T::Boolean).checked(:never)} sig(:final) { params(val: T.untyped).returns(T::Boolean).checked(:never) }
def self.scalar_type?(val) def self.scalar_type?(val)
# We don't need to check for val's included modules in # We don't need to check for val's included modules in
# T::Configuration.scalar_types, because T::Configuration.scalar_types # T::Configuration.scalar_types, because T::Configuration.scalar_types
@ -74,7 +74,7 @@ module T::Props
# implement set-like fields that store a unique-array, but forbid # implement set-like fields that store a unique-array, but forbid
# hashes; Custom hash types should be implemented via an emebdded # hashes; Custom hash types should be implemented via an emebdded
# T::Struct (or a subclass like Chalk::ODM::Document) or via T. # T::Struct (or a subclass like Chalk::ODM::Document) or via T.
sig(:final) {params(val: Object).returns(T::Boolean).checked(:never)} sig(:final) { params(val: Object).returns(T::Boolean).checked(:never) }
def self.valid_serialization?(val) def self.valid_serialization?(val)
case val case val
when Array when Array

View File

@ -10,17 +10,17 @@
class T::Props::Decorator class T::Props::Decorator
extend T::Sig extend T::Sig
Rules = T.type_alias {T::Hash[Symbol, T.untyped]} Rules = T.type_alias { T::Hash[Symbol, T.untyped] }
DecoratedInstance = T.type_alias {Object} # Would be T::Props, but that produces circular reference errors in some circumstances DecoratedInstance = T.type_alias { Object } # Would be T::Props, but that produces circular reference errors in some circumstances
PropType = T.type_alias {T::Types::Base} PropType = T.type_alias { T::Types::Base }
PropTypeOrClass = T.type_alias {T.any(PropType, Module)} PropTypeOrClass = T.type_alias { T.any(PropType, Module) }
class NoRulesError < StandardError; end class NoRulesError < StandardError; end
EMPTY_PROPS = T.let({}.freeze, T::Hash[Symbol, Rules], checked: false) EMPTY_PROPS = T.let({}.freeze, T::Hash[Symbol, Rules], checked: false)
private_constant :EMPTY_PROPS private_constant :EMPTY_PROPS
sig {params(klass: T.untyped).void.checked(:never)} sig { params(klass: T.untyped).void.checked(:never) }
def initialize(klass) def initialize(klass)
@class = T.let(klass, T.all(Module, T::Props::ClassMethods)) @class = T.let(klass, T.all(Module, T::Props::ClassMethods))
@class.plugins.each do |mod| @class.plugins.each do |mod|
@ -30,22 +30,22 @@ class T::Props::Decorator
end end
# checked(:never) - O(prop accesses) # checked(:never) - O(prop accesses)
sig {returns(T::Hash[Symbol, Rules]).checked(:never)} sig { returns(T::Hash[Symbol, Rules]).checked(:never) }
attr_reader :props attr_reader :props
sig {returns(T::Array[Symbol])} sig { returns(T::Array[Symbol]) }
def all_props def all_props
props.keys props.keys
end end
# checked(:never) - O(prop accesses) # checked(:never) - O(prop accesses)
sig {params(prop: T.any(Symbol, String)).returns(Rules).checked(:never)} sig { params(prop: T.any(Symbol, String)).returns(Rules).checked(:never) }
def prop_rules(prop) def prop_rules(prop)
props[prop.to_sym] || raise("No such prop: #{prop.inspect}") props[prop.to_sym] || raise("No such prop: #{prop.inspect}")
end end
# checked(:never) - Rules hash is expensive to check # checked(:never) - Rules hash is expensive to check
sig {params(prop: Symbol, rules: Rules).void.checked(:never)} sig { params(prop: Symbol, rules: Rules).void.checked(:never) }
def add_prop_definition(prop, rules) def add_prop_definition(prop, rules)
override = rules.delete(:override) override = rules.delete(:override)
@ -79,16 +79,16 @@ class T::Props::Decorator
extra extra
setter_validate setter_validate
_tnilable _tnilable
].to_h {|k| [k, true]}.freeze, T::Hash[Symbol, T::Boolean], checked: false) ].to_h { |k| [k, true] }.freeze, T::Hash[Symbol, T::Boolean], checked: false)
private_constant :VALID_RULE_KEYS private_constant :VALID_RULE_KEYS
sig {params(key: Symbol).returns(T::Boolean).checked(:never)} sig { params(key: Symbol).returns(T::Boolean).checked(:never) }
def valid_rule_key?(key) def valid_rule_key?(key)
!!VALID_RULE_KEYS[key] !!VALID_RULE_KEYS[key]
end end
# checked(:never) - O(prop accesses) # checked(:never) - O(prop accesses)
sig {returns(T.all(Module, T::Props::ClassMethods)).checked(:never)} sig { returns(T.all(Module, T::Props::ClassMethods)).checked(:never) }
def decorated_class def decorated_class
@class @class
end end
@ -98,7 +98,7 @@ class T::Props::Decorator
# Use this to validate that a value will validate for a given prop. Useful for knowing whether a value can be set on a model without setting it. # Use this to validate that a value will validate for a given prop. Useful for knowing whether a value can be set on a model without setting it.
# #
# checked(:never) - potentially O(prop accesses) depending on usage pattern # checked(:never) - potentially O(prop accesses) depending on usage pattern
sig {params(prop: Symbol, val: T.untyped).void.checked(:never)} sig { params(prop: Symbol, val: T.untyped).void.checked(:never) }
def validate_prop_value(prop, val) def validate_prop_value(prop, val)
prop_rules(prop).fetch(:value_validate_proc).call(val) prop_rules(prop).fetch(:value_validate_proc).call(val)
end end
@ -202,7 +202,7 @@ class T::Props::Decorator
end end
# TODO: we should really be checking all the methods on `cls`, not just Object # TODO: we should really be checking all the methods on `cls`, not just Object
BANNED_METHOD_NAMES = T.let(Object.instance_methods.each_with_object({}) {|x, acc| acc[x] = true}.freeze, T::Hash[Symbol, TrueClass], checked: false) BANNED_METHOD_NAMES = T.let(Object.instance_methods.each_with_object({}) { |x, acc| acc[x] = true }.freeze, T::Hash[Symbol, TrueClass], checked: false)
# checked(:never) - Rules hash is expensive to check # checked(:never) - Rules hash is expensive to check
sig do sig do
@ -223,7 +223,7 @@ class T::Props::Decorator
"to 'sensitivity:' (in prop #{@class.name}.#{name})") "to 'sensitivity:' (in prop #{@class.name}.#{name})")
end end
if rules.keys.any? {|k| !valid_rule_key?(k)} if rules.keys.any? { |k| !valid_rule_key?(k) }
raise ArgumentError.new("At least one invalid prop arg supplied in #{self}: #{rules.keys.inspect}") raise ArgumentError.new("At least one invalid prop arg supplied in #{self}: #{rules.keys.inspect}")
end end
@ -247,7 +247,7 @@ class T::Props::Decorator
SAFE_NAME = T.let(/\A[A-Za-z_][A-Za-z0-9_-]*\z/.freeze, Regexp, checked: false) SAFE_NAME = T.let(/\A[A-Za-z_][A-Za-z0-9_-]*\z/.freeze, Regexp, checked: false)
# Used to validate both prop names and serialized forms # Used to validate both prop names and serialized forms
sig {params(name: T.any(Symbol, String)).void.checked(:never)} sig { params(name: T.any(Symbol, String)).void.checked(:never) }
private def validate_prop_name(name) private def validate_prop_name(name)
if !name.match?(SAFE_NAME) if !name.match?(SAFE_NAME)
raise ArgumentError.new("Invalid prop name in #{@class.name}: #{name}") raise ArgumentError.new("Invalid prop name in #{@class.name}: #{name}")
@ -255,7 +255,7 @@ class T::Props::Decorator
end end
# This converts the type from a T::Type to a regular old ruby class. # This converts the type from a T::Type to a regular old ruby class.
sig {params(type: T::Types::Base).returns(Module).checked(:never)} sig { params(type: T::Types::Base).returns(Module).checked(:never) }
private def convert_type_to_class(type) private def convert_type_to_class(type)
case type case type
when T::Types::TypedArray, T::Types::FixedArray when T::Types::TypedArray, T::Types::FixedArray
@ -392,7 +392,7 @@ class T::Props::Decorator
end end
# checked(:never) - Rules hash is expensive to check # checked(:never) - Rules hash is expensive to check
sig {params(name: Symbol, rules: Rules).void.checked(:never)} sig { params(name: Symbol, rules: Rules).void.checked(:never) }
private def define_getter_and_setter(name, rules) private def define_getter_and_setter(name, rules)
T::Configuration.without_ruby_warnings do T::Configuration.without_ruby_warnings do
if !rules[:immutable] if !rules[:immutable]
@ -440,7 +440,7 @@ class T::Props::Decorator
end end
# checked(:never) - Rules hash is expensive to check # checked(:never) - Rules hash is expensive to check
sig {params(prop_name: Symbol, rules: Rules).void.checked(:never)} sig { params(prop_name: Symbol, rules: Rules).void.checked(:never) }
private def validate_not_missing_sensitivity(prop_name, rules) private def validate_not_missing_sensitivity(prop_name, rules)
if rules[:sensitivity].nil? if rules[:sensitivity].nil?
if rules[:redaction] if rules[:redaction]
@ -612,7 +612,7 @@ class T::Props::Decorator
# #
# This gets called when a module or class that extends T::Props gets included, extended, # This gets called when a module or class that extends T::Props gets included, extended,
# prepended, or inherited. # prepended, or inherited.
sig {params(child: Module).void.checked(:never)} sig { params(child: Module).void.checked(:never) }
def model_inherited(child) def model_inherited(child)
child.extend(T::Props::ClassMethods) child.extend(T::Props::ClassMethods)
child = T.cast(child, T.all(Module, T::Props::ClassMethods)) child = T.cast(child, T.all(Module, T::Props::ClassMethods))
@ -656,19 +656,19 @@ class T::Props::Decorator
end end
end end
sig {params(child: T.all(Module, T::Props::ClassMethods), prop: Symbol).returns(T::Boolean).checked(:never)} sig { params(child: T.all(Module, T::Props::ClassMethods), prop: Symbol).returns(T::Boolean).checked(:never) }
private def clobber_getter?(child, prop) private def clobber_getter?(child, prop)
!!(child.decorator.method(:prop_get).owner != method(:prop_get).owner && !!(child.decorator.method(:prop_get).owner != method(:prop_get).owner &&
child.instance_method(prop).source_location&.first == __FILE__) child.instance_method(prop).source_location&.first == __FILE__)
end end
sig {params(child: T.all(Module, T::Props::ClassMethods), prop: Symbol).returns(T::Boolean).checked(:never)} sig { params(child: T.all(Module, T::Props::ClassMethods), prop: Symbol).returns(T::Boolean).checked(:never) }
private def clobber_setter?(child, prop) private def clobber_setter?(child, prop)
!!(child.decorator.method(:prop_set).owner != method(:prop_set).owner && !!(child.decorator.method(:prop_set).owner != method(:prop_set).owner &&
child.instance_method("#{prop}=").source_location&.first == __FILE__) child.instance_method("#{prop}=").source_location&.first == __FILE__)
end end
sig {params(mod: Module).void.checked(:never)} sig { params(mod: Module).void.checked(:never) }
def plugin(mod) def plugin(mod)
decorated_class.plugins << mod decorated_class.plugins << mod
T::Props::Plugin::Private.apply_class_methods(mod, decorated_class) T::Props::Plugin::Private.apply_class_methods(mod, decorated_class)

View File

@ -159,7 +159,7 @@ module T::Props
assert_equal(:resbody, rescue_body.type) assert_equal(:resbody, rescue_body.type)
exceptions, assignment, handler = rescue_body.children exceptions, assignment, handler = rescue_body.children
assert_equal(:array, exceptions.type) assert_equal(:array, exceptions.type)
exceptions.children.each {|c| assert_equal(:const, c.type)} exceptions.children.each { |c| assert_equal(:const, c.type) }
assert_equal(:lvasgn, assignment.type) assert_equal(:lvasgn, assignment.type)
assert_equal([:e], assignment.children) assert_equal([:e], assignment.children)
@ -169,7 +169,7 @@ module T::Props
receiver, method, *args = deserialization_error.children receiver, method, *args = deserialization_error.children
assert_equal(nil, receiver) assert_equal(nil, receiver)
assert_equal(:raise_deserialization_error, method) assert_equal(:raise_deserialization_error, method)
args.each {|a| validate_lack_of_side_effects(a, whitelisted_methods_for_deserialize)} args.each { |a| validate_lack_of_side_effects(a, whitelisted_methods_for_deserialize) }
validate_lack_of_side_effects(val_return, whitelisted_methods_for_deserialize) validate_lack_of_side_effects(val_return, whitelisted_methods_for_deserialize)
else else
@ -222,12 +222,12 @@ module T::Props
# Primitives & self are ok # Primitives & self are ok
when :lvar, :arg, :ivar when :lvar, :arg, :ivar
# Reading local & instance variables & arguments is ok # Reading local & instance variables & arguments is ok
unless node.children.all? {|c| c.is_a?(Symbol)} unless node.children.all? { |c| c.is_a?(Symbol) }
raise ValidationError.new("Unexpected child for #{node.type}: #{node.inspect}") raise ValidationError.new("Unexpected child for #{node.type}: #{node.inspect}")
end end
when :args, :mlhs, :block, :begin, :if when :args, :mlhs, :block, :begin, :if
# Blocks etc are read-only if their contents are read-only # Blocks etc are read-only if their contents are read-only
node.children.each {|c| validate_lack_of_side_effects(c, whitelisted_methods_by_receiver_type) if c} node.children.each { |c| validate_lack_of_side_effects(c, whitelisted_methods_by_receiver_type) if c }
when :send when :send
# Sends are riskier so check a whitelist # Sends are riskier so check a whitelist
receiver, method, *args = node.children receiver, method, *args = node.children

View File

@ -30,12 +30,12 @@ module T::Props
# #
# Note it does _not_ prevent explicit calls to `eagerly_define_lazy_methods!` # Note it does _not_ prevent explicit calls to `eagerly_define_lazy_methods!`
# from working. # from working.
sig {void} sig { void }
def self.disable_lazy_evaluation! def self.disable_lazy_evaluation!
@lazy_evaluation_disabled ||= true @lazy_evaluation_disabled ||= true
end end
sig {returns(T::Boolean)} sig { returns(T::Boolean) }
def self.lazy_evaluation_enabled? def self.lazy_evaluation_enabled?
!defined?(@lazy_evaluation_disabled) || !@lazy_evaluation_disabled !defined?(@lazy_evaluation_disabled) || !@lazy_evaluation_disabled
end end
@ -43,17 +43,17 @@ module T::Props
module DecoratorMethods module DecoratorMethods
extend T::Sig extend T::Sig
sig {returns(T::Hash[Symbol, T.proc.returns(String)]).checked(:never)} sig { returns(T::Hash[Symbol, T.proc.returns(String)]).checked(:never) }
private def lazily_defined_methods private def lazily_defined_methods
@lazily_defined_methods ||= {} @lazily_defined_methods ||= {}
end end
sig {returns(T::Hash[Symbol, T.untyped]).checked(:never)} sig { returns(T::Hash[Symbol, T.untyped]).checked(:never) }
private def lazily_defined_vm_methods private def lazily_defined_vm_methods
@lazily_defined_vm_methods ||= {} @lazily_defined_vm_methods ||= {}
end end
sig {params(name: Symbol).void} sig { params(name: Symbol).void }
private def eval_lazily_defined_method!(name) private def eval_lazily_defined_method!(name)
if !HasLazilySpecializedMethods.lazy_evaluation_enabled? if !HasLazilySpecializedMethods.lazy_evaluation_enabled?
raise SourceEvaluationDisabled.new raise SourceEvaluationDisabled.new
@ -68,7 +68,7 @@ module T::Props
cls.send(:private, name) cls.send(:private, name)
end end
sig {params(name: Symbol).void} sig { params(name: Symbol).void }
private def eval_lazily_defined_vm_method!(name) private def eval_lazily_defined_vm_method!(name)
if !HasLazilySpecializedMethods.lazy_evaluation_enabled? if !HasLazilySpecializedMethods.lazy_evaluation_enabled?
raise SourceEvaluationDisabled.new raise SourceEvaluationDisabled.new
@ -80,7 +80,7 @@ module T::Props
cls.send(:private, name) cls.send(:private, name)
end end
sig {params(name: Symbol, blk: T.proc.returns(String)).void} sig { params(name: Symbol, blk: T.proc.returns(String)).void }
private def enqueue_lazy_method_definition!(name, &blk) private def enqueue_lazy_method_definition!(name, &blk)
lazily_defined_methods[name] = blk lazily_defined_methods[name] = blk
@ -100,7 +100,7 @@ module T::Props
cls.send(:private, name) cls.send(:private, name)
end end
sig {params(name: Symbol, blk: T.untyped).void} sig { params(name: Symbol, blk: T.untyped).void }
private def enqueue_lazy_vm_method_definition!(name, &blk) private def enqueue_lazy_vm_method_definition!(name, &blk)
lazily_defined_vm_methods[name] = blk lazily_defined_vm_methods[name] = blk
@ -115,7 +115,7 @@ module T::Props
cls.send(:private, name) cls.send(:private, name)
end end
sig {void} sig { void }
def eagerly_define_lazy_methods! def eagerly_define_lazy_methods!
return if lazily_defined_methods.empty? return if lazily_defined_methods.empty?
@ -125,18 +125,18 @@ module T::Props
cls = decorated_class cls = decorated_class
cls.class_eval(source) cls.class_eval(source)
lazily_defined_methods.each_key {|name| cls.send(:private, name)} lazily_defined_methods.each_key { |name| cls.send(:private, name) }
lazily_defined_methods.clear lazily_defined_methods.clear
end end
sig {void} sig { void }
def eagerly_define_lazy_vm_methods! def eagerly_define_lazy_vm_methods!
return if lazily_defined_vm_methods.empty? return if lazily_defined_vm_methods.empty?
lazily_defined_vm_methods.values.map(&:call) lazily_defined_vm_methods.values.map(&:call)
cls = decorated_class cls = decorated_class
lazily_defined_vm_methods.each_key {|name| cls.send(:private, name)} lazily_defined_vm_methods.each_key { |name| cls.send(:private, name) }
lazily_defined_vm_methods.clear lazily_defined_vm_methods.clear
end end
end end

View File

@ -43,11 +43,11 @@ module T::Props::Optional::DecoratorMethods
end end
# checked(:never) - O(runtime object construction) # checked(:never) - O(runtime object construction)
sig {returns(T::Hash[Symbol, T::Props::Private::ApplyDefault]).checked(:never)} sig { returns(T::Hash[Symbol, T::Props::Private::ApplyDefault]).checked(:never) }
attr_reader :props_with_defaults attr_reader :props_with_defaults
# checked(:never) - O(runtime object construction) # checked(:never) - O(runtime object construction)
sig {returns(T::Hash[Symbol, T::Props::Private::SetterFactory::SetterProc]).checked(:never)} sig { returns(T::Hash[Symbol, T::Props::Private::SetterFactory::SetterProc]).checked(:never) }
attr_reader :props_without_defaults attr_reader :props_without_defaults
def add_prop_definition(prop, rules) def add_prop_definition(prop, rules)

View File

@ -49,21 +49,21 @@ module T::Props::PrettyPrintable
module DecoratorMethods module DecoratorMethods
extend T::Sig extend T::Sig
sig {params(key: Symbol).returns(T::Boolean).checked(:never)} sig { params(key: Symbol).returns(T::Boolean).checked(:never) }
def valid_rule_key?(key) def valid_rule_key?(key)
super || key == :inspect super || key == :inspect
end end
# Overridable method to specify how the first part of a `pretty_print`d object's class should look like # Overridable method to specify how the first part of a `pretty_print`d object's class should look like
# NOTE: This is just to support Stripe's `PrettyPrintableModel` case, and not recommended to be overridden # NOTE: This is just to support Stripe's `PrettyPrintableModel` case, and not recommended to be overridden
sig {params(instance: T::Props::PrettyPrintable).returns(String)} sig { params(instance: T::Props::PrettyPrintable).returns(String) }
def inspect_class_with_decoration(instance) def inspect_class_with_decoration(instance)
T.unsafe(instance).class.to_s T.unsafe(instance).class.to_s
end end
# Overridable method to add anything that is not a prop # Overridable method to add anything that is not a prop
# NOTE: This is to support cases like Serializable's `@_extra_props`, and Stripe's `PrettyPrintableModel#@_deleted` # NOTE: This is to support cases like Serializable's `@_extra_props`, and Stripe's `PrettyPrintableModel#@_deleted`
sig {params(instance: T::Props::PrettyPrintable, pp: T.any(PrettyPrint, PP::SingleLine)).void} sig { params(instance: T::Props::PrettyPrintable, pp: T.any(PrettyPrint, PP::SingleLine)).void }
def pretty_print_extra(instance, pp); end def pretty_print_extra(instance, pp); end
end end
end end

View File

@ -9,28 +9,28 @@ module T::Props
abstract! abstract!
# checked(:never) - O(object construction x prop count) # checked(:never) - O(object construction x prop count)
sig {returns(SetterFactory::SetterProc).checked(:never)} sig { returns(SetterFactory::SetterProc).checked(:never) }
attr_reader :setter_proc attr_reader :setter_proc
# checked(:never) - We do this with `T.let` instead # checked(:never) - We do this with `T.let` instead
sig {params(accessor_key: Symbol, setter_proc: SetterFactory::SetterProc).void.checked(:never)} sig { params(accessor_key: Symbol, setter_proc: SetterFactory::SetterProc).void.checked(:never) }
def initialize(accessor_key, setter_proc) def initialize(accessor_key, setter_proc)
@accessor_key = T.let(accessor_key, Symbol) @accessor_key = T.let(accessor_key, Symbol)
@setter_proc = T.let(setter_proc, SetterFactory::SetterProc) @setter_proc = T.let(setter_proc, SetterFactory::SetterProc)
end end
# checked(:never) - O(object construction x prop count) # checked(:never) - O(object construction x prop count)
sig {abstract.returns(T.untyped).checked(:never)} sig { abstract.returns(T.untyped).checked(:never) }
def default; end def default; end
# checked(:never) - O(object construction x prop count) # checked(:never) - O(object construction x prop count)
sig {abstract.params(instance: T.all(T::Props::Optional, Object)).void.checked(:never)} sig { abstract.params(instance: T.all(T::Props::Optional, Object)).void.checked(:never) }
def set_default(instance); end def set_default(instance); end
NO_CLONE_TYPES = T.let([TrueClass, FalseClass, NilClass, Symbol, Numeric, T::Enum].freeze, T::Array[Module]) NO_CLONE_TYPES = T.let([TrueClass, FalseClass, NilClass, Symbol, Numeric, T::Enum].freeze, T::Array[Module])
# checked(:never) - Rules hash is expensive to check # checked(:never) - Rules hash is expensive to check
sig {params(cls: Module, rules: T::Hash[Symbol, T.untyped]).returns(T.nilable(ApplyDefault)).checked(:never)} sig { params(cls: Module, rules: T::Hash[Symbol, T.untyped]).returns(T.nilable(ApplyDefault)).checked(:never) }
def self.for(cls, rules) def self.for(cls, rules)
accessor_key = rules.fetch(:accessor_key) accessor_key = rules.fetch(:accessor_key)
setter = rules.fetch(:setter_proc) setter = rules.fetch(:setter_proc)
@ -67,7 +67,7 @@ module T::Props
abstract! abstract!
# checked(:never) - We do this with `T.let` instead # checked(:never) - We do this with `T.let` instead
sig {params(default: BasicObject, accessor_key: Symbol, setter_proc: SetterFactory::SetterProc).void.checked(:never)} sig { params(default: BasicObject, accessor_key: Symbol, setter_proc: SetterFactory::SetterProc).void.checked(:never) }
def initialize(default, accessor_key, setter_proc) def initialize(default, accessor_key, setter_proc)
# FIXME: Ideally we'd check here that the default is actually a valid # FIXME: Ideally we'd check here that the default is actually a valid
# value for this field, but existing code relies on the fact that we don't. # value for this field, but existing code relies on the fact that we don't.
@ -80,7 +80,7 @@ module T::Props
end end
# checked(:never) - O(object construction x prop count) # checked(:never) - O(object construction x prop count)
sig {override.params(instance: T.all(T::Props::Optional, Object)).void.checked(:never)} sig { override.params(instance: T.all(T::Props::Optional, Object)).void.checked(:never) }
def set_default(instance) def set_default(instance)
instance.instance_variable_set(@accessor_key, default) instance.instance_variable_set(@accessor_key, default)
end end
@ -88,13 +88,13 @@ module T::Props
class ApplyPrimitiveDefault < ApplyFixedDefault class ApplyPrimitiveDefault < ApplyFixedDefault
# checked(:never) - O(object construction x prop count) # checked(:never) - O(object construction x prop count)
sig {override.returns(T.untyped).checked(:never)} sig { override.returns(T.untyped).checked(:never) }
attr_reader :default attr_reader :default
end end
class ApplyComplexDefault < ApplyFixedDefault class ApplyComplexDefault < ApplyFixedDefault
# checked(:never) - O(object construction x prop count) # checked(:never) - O(object construction x prop count)
sig {override.returns(T.untyped).checked(:never)} sig { override.returns(T.untyped).checked(:never) }
def default def default
T::Props::Utils.deep_clone_object(@default) T::Props::Utils.deep_clone_object(@default)
end end
@ -105,13 +105,13 @@ module T::Props
# `some_empty_array.dup` # `some_empty_array.dup`
class ApplyEmptyArrayDefault < ApplyDefault class ApplyEmptyArrayDefault < ApplyDefault
# checked(:never) - O(object construction x prop count) # checked(:never) - O(object construction x prop count)
sig {override.params(instance: T.all(T::Props::Optional, Object)).void.checked(:never)} sig { override.params(instance: T.all(T::Props::Optional, Object)).void.checked(:never) }
def set_default(instance) def set_default(instance)
instance.instance_variable_set(@accessor_key, []) instance.instance_variable_set(@accessor_key, [])
end end
# checked(:never) - O(object construction x prop count) # checked(:never) - O(object construction x prop count)
sig {override.returns(T::Array[T.untyped]).checked(:never)} sig { override.returns(T::Array[T.untyped]).checked(:never) }
def default def default
[] []
end end
@ -122,13 +122,13 @@ module T::Props
# `some_empty_hash.dup` # `some_empty_hash.dup`
class ApplyEmptyHashDefault < ApplyDefault class ApplyEmptyHashDefault < ApplyDefault
# checked(:never) - O(object construction x prop count) # checked(:never) - O(object construction x prop count)
sig {override.params(instance: T.all(T::Props::Optional, Object)).void.checked(:never)} sig { override.params(instance: T.all(T::Props::Optional, Object)).void.checked(:never) }
def set_default(instance) def set_default(instance)
instance.instance_variable_set(@accessor_key, {}) instance.instance_variable_set(@accessor_key, {})
end end
# checked(:never) - O(object construction x prop count) # checked(:never) - O(object construction x prop count)
sig {override.returns(T::Hash[T.untyped, T.untyped]).checked(:never)} sig { override.returns(T::Hash[T.untyped, T.untyped]).checked(:never) }
def default def default
{} {}
end end
@ -153,7 +153,7 @@ module T::Props
end end
# checked(:never) - O(object construction x prop count) # checked(:never) - O(object construction x prop count)
sig {override.params(instance: T.all(T::Props::Optional, Object)).void.checked(:never)} sig { override.params(instance: T.all(T::Props::Optional, Object)).void.checked(:never) }
def set_default(instance) def set_default(instance)
# Use the actual setter to validate the factory returns a legitimate # Use the actual setter to validate the factory returns a legitimate
# value every time # value every time
@ -161,7 +161,7 @@ module T::Props
end end
# checked(:never) - O(object construction x prop count) # checked(:never) - O(object construction x prop count)
sig {override.returns(T.untyped).checked(:never)} sig { override.returns(T.untyped).checked(:never) }
def default def default
@class.class_exec(&@factory) @class.class_exec(&@factory)
end end

View File

@ -29,7 +29,7 @@ module T::Props
.checked(:never) .checked(:never)
end end
def self.generate(props, defaults) def self.generate(props, defaults)
stored_props = props.reject {|_, rules| rules[:dont_store]} stored_props = props.reject { |_, rules| rules[:dont_store] }
parts = stored_props.map do |prop, rules| parts = stored_props.map do |prop, rules|
# All of these strings should already be validated (directly or # All of these strings should already be validated (directly or
# indirectly) in `validate_prop_name`, so we don't bother with a nice # indirectly) in `validate_prop_name`, so we don't bother with a nice

View File

@ -10,7 +10,7 @@ module T::Props
private_constant :Serialize private_constant :Serialize
class Deserialize; end class Deserialize; end
private_constant :Deserialize private_constant :Deserialize
ModeType = T.type_alias {T.any(Serialize, Deserialize)} ModeType = T.type_alias { T.any(Serialize, Deserialize) }
private_constant :ModeType private_constant :ModeType
module Mode module Mode
@ -63,7 +63,7 @@ module T::Props
end end
when T::Types::Simple when T::Types::Simple
raw = type.raw_type raw = type.raw_type
if NO_TRANSFORM_TYPES.any? {|cls| raw <= cls} if NO_TRANSFORM_TYPES.any? { |cls| raw <= cls }
nil nil
elsif raw <= Float elsif raw <= Float
case mode case mode
@ -99,7 +99,7 @@ module T::Props
else else
"#{varname}.nil? ? nil : #{inner}" "#{varname}.nil? ? nil : #{inner}"
end end
elsif type.types.all? {|t| generate(t, mode, varname).nil?} elsif type.types.all? { |t| generate(t, mode, varname).nil? }
# Handle, e.g., T::Boolean # Handle, e.g., T::Boolean
nil nil
else else
@ -122,8 +122,8 @@ module T::Props
# NB: This deliberately does include `nil`, which means we know we # NB: This deliberately does include `nil`, which means we know we
# don't need to do any transforming. # don't need to do any transforming.
inner_known = type.types inner_known = type.types
.map {|t| generate(t, mode, varname)} .map { |t| generate(t, mode, varname) }
.reject {|t| t == dynamic_fallback} .reject { |t| t == dynamic_fallback }
.uniq .uniq
if inner_known.size != 1 if inner_known.size != 1
@ -151,7 +151,7 @@ module T::Props
end end
end end
sig {params(varname: String, type: Module, mode: ModeType).returns(String).checked(:never)} sig { params(varname: String, type: Module, mode: ModeType).returns(String).checked(:never) }
private_class_method def self.handle_serializable_subtype(varname, type, mode) private_class_method def self.handle_serializable_subtype(varname, type, mode)
case mode case mode
when Serialize when Serialize
@ -164,7 +164,7 @@ module T::Props
end end
end end
sig {params(varname: String, type: Module, mode: ModeType).returns(String).checked(:never)} sig { params(varname: String, type: Module, mode: ModeType).returns(String).checked(:never) }
private_class_method def self.handle_custom_type(varname, type, mode) private_class_method def self.handle_custom_type(varname, type, mode)
case mode case mode
when Serialize when Serialize
@ -177,7 +177,7 @@ module T::Props
end end
end end
sig {params(type: Module).returns(T.nilable(String)).checked(:never)} sig { params(type: Module).returns(T.nilable(String)).checked(:never) }
private_class_method def self.module_name(type) private_class_method def self.module_name(type)
T::Configuration.module_name_mangler.call(type) T::Configuration.module_name_mangler.call(type)
end end

View File

@ -24,7 +24,7 @@ module T::Props
.checked(:never) .checked(:never)
end end
def self.generate(props) def self.generate(props)
stored_props = props.reject {|_, rules| rules[:dont_store]} stored_props = props.reject { |_, rules| rules[:dont_store] }
parts = stored_props.map do |prop, rules| parts = stored_props.map do |prop, rules|
# All of these strings should already be validated (directly or # All of these strings should already be validated (directly or
# indirectly) in `validate_prop_name`, so we don't bother with a nice # indirectly) in `validate_prop_name`, so we don't bother with a nice

View File

@ -6,9 +6,9 @@ module T::Props
module SetterFactory module SetterFactory
extend T::Sig extend T::Sig
SetterProc = T.type_alias {T.proc.params(val: T.untyped).void} SetterProc = T.type_alias { T.proc.params(val: T.untyped).void }
ValueValidationProc = T.type_alias {T.proc.params(val: T.untyped).void} ValueValidationProc = T.type_alias { T.proc.params(val: T.untyped).void }
ValidateProc = T.type_alias {T.proc.params(prop: Symbol, value: T.untyped).void} ValidateProc = T.type_alias { T.proc.params(prop: Symbol, value: T.untyped).void }
sig do sig do
params( params(
@ -236,7 +236,7 @@ module T::Props
base_message = "Can't set #{klass.name}.#{prop} to #{val.inspect} (instance of #{val.class}) - need a #{type}" base_message = "Can't set #{klass.name}.#{prop} to #{val.inspect} (instance of #{val.class}) - need a #{type}"
pretty_message = "Parameter '#{prop}': #{base_message}\n" pretty_message = "Parameter '#{prop}': #{base_message}\n"
caller_loc = caller_locations.find {|l| !l.to_s.include?('sorbet-runtime/lib/types/props')} caller_loc = caller_locations.find { |l| !l.to_s.include?('sorbet-runtime/lib/types/props') }
if caller_loc if caller_loc
pretty_message += "Caller: #{caller_loc.path}:#{caller_loc.lineno}\n" pretty_message += "Caller: #{caller_loc.path}:#{caller_loc.lineno}\n"
end end

View File

@ -78,7 +78,7 @@ module T::Props::Serializable
if hash.size > hash_keys_matching_props if hash.size > hash_keys_matching_props
serialized_forms = self.class.decorator.prop_by_serialized_forms serialized_forms = self.class.decorator.prop_by_serialized_forms
extra = hash.reject {|k, _| serialized_forms.key?(k)} extra = hash.reject { |k, _| serialized_forms.key?(k) }
# `extra` could still be empty here if the input matches a `dont_store` prop; # `extra` could still be empty here if the input matches a `dont_store` prop;
# historically, we just ignore those # historically, we just ignore those
@ -111,7 +111,7 @@ module T::Props::Serializable
new_obj[k.to_s] = recursive_stringify_keys(v) new_obj[k.to_s] = recursive_stringify_keys(v)
end end
elsif obj.is_a?(Array) elsif obj.is_a?(Array)
new_obj = obj.map {|v| recursive_stringify_keys(v)} new_obj = obj.map { |v| recursive_stringify_keys(v) }
else else
new_obj = obj new_obj = obj
end end
@ -126,7 +126,7 @@ module T::Props::Serializable
if old_extra != new_extra if old_extra != new_extra
difference = difference =
if old_extra if old_extra
new_extra.reject {|k, v| old_extra[k] == v} new_extra.reject { |k, v| old_extra[k] == v }
else else
new_extra new_extra
end end
@ -195,7 +195,7 @@ module T::Props::Serializable::DecoratorMethods
end end
def required_props def required_props
@class.props.select {|_, v| T::Props::Utils.required_prop?(v)}.keys @class.props.select { |_, v| T::Props::Utils.required_prop?(v) }.keys
end end
def prop_dont_store?(prop) def prop_dont_store?(prop)
@ -228,11 +228,11 @@ module T::Props::Serializable::DecoratorMethods
res = super res = super
prop_by_serialized_forms[serialized_form] = prop prop_by_serialized_forms[serialized_form] = prop
if T::Configuration.use_vm_prop_serde? if T::Configuration.use_vm_prop_serde?
enqueue_lazy_vm_method_definition!(:__t_props_generated_serialize) {generate_serialize2} enqueue_lazy_vm_method_definition!(:__t_props_generated_serialize) { generate_serialize2 }
enqueue_lazy_vm_method_definition!(:__t_props_generated_deserialize) {generate_deserialize2} enqueue_lazy_vm_method_definition!(:__t_props_generated_deserialize) { generate_deserialize2 }
else else
enqueue_lazy_method_definition!(:__t_props_generated_serialize) {generate_serialize_source} enqueue_lazy_method_definition!(:__t_props_generated_serialize) { generate_serialize_source }
enqueue_lazy_method_definition!(:__t_props_generated_deserialize) {generate_deserialize_source} enqueue_lazy_method_definition!(:__t_props_generated_deserialize) { generate_deserialize_source }
end end
res res
end end
@ -263,7 +263,7 @@ module T::Props::Serializable::DecoratorMethods
def message_with_generated_source_context(error, generated_method, generate_source_method) def message_with_generated_source_context(error, generated_method, generate_source_method)
generated_method = generated_method.to_s generated_method = generated_method.to_s
if error.backtrace_locations if error.backtrace_locations
line_loc = error.backtrace_locations.find {|l| l.base_label == generated_method} line_loc = error.backtrace_locations.find { |l| l.base_label == generated_method }
return unless line_loc return unless line_loc
line_num = line_loc.lineno line_num = line_loc.lineno
@ -275,7 +275,7 @@ module T::Props::Serializable::DecoratorMethods
# in `__t_props_generated_serialize'" # in `__t_props_generated_serialize'"
"in `#{generated_method}'" "in `#{generated_method}'"
end end
line_label = error.backtrace.find {|l| l.end_with?(label)} line_label = error.backtrace.find { |l| l.end_with?(label) }
return unless line_label return unless line_label
line_num = if line_label.start_with?("(eval)") line_num = if line_label.start_with?("(eval)")

View File

@ -11,7 +11,7 @@ module T::Props::TypeValidation
module DecoratorMethods module DecoratorMethods
extend T::Sig extend T::Sig
sig {params(key: Symbol).returns(T::Boolean).checked(:never)} sig { params(key: Symbol).returns(T::Boolean).checked(:never) }
def valid_rule_key?(key) def valid_rule_key?(key)
super || key == :DEPRECATED_underspecified_type super || key == :DEPRECATED_underspecified_type
end end
@ -58,19 +58,19 @@ module T::Props::TypeValidation
# If the type is fully valid, returns nil. # If the type is fully valid, returns nil.
# #
# checked(:never) - called potentially many times recursively # checked(:never) - called potentially many times recursively
sig {params(type: T::Types::Base).returns(T.nilable(T::Types::Base)).checked(:never)} sig { params(type: T::Types::Base).returns(T.nilable(T::Types::Base)).checked(:never) }
private def find_invalid_subtype(type) private def find_invalid_subtype(type)
case type case type
when T::Types::TypedEnumerable when T::Types::TypedEnumerable
find_invalid_subtype(type.type) find_invalid_subtype(type.type)
when T::Types::FixedHash when T::Types::FixedHash
type.types.values.map {|subtype| find_invalid_subtype(subtype)}.compact.first type.types.values.map { |subtype| find_invalid_subtype(subtype) }.compact.first
when T::Types::Union, T::Types::FixedArray when T::Types::Union, T::Types::FixedArray
# `T.any` is valid if all of the members are valid # `T.any` is valid if all of the members are valid
type.types.map {|subtype| find_invalid_subtype(subtype)}.compact.first type.types.map { |subtype| find_invalid_subtype(subtype) }.compact.first
when T::Types::Intersection when T::Types::Intersection
# `T.all` is valid if at least one of the members is valid # `T.all` is valid if at least one of the members is valid
invalid = type.types.map {|subtype| find_invalid_subtype(subtype)}.compact invalid = type.types.map { |subtype| find_invalid_subtype(subtype) }.compact
if invalid.length == type.types.length if invalid.length == type.types.length
invalid.first invalid.first
else else

View File

@ -13,7 +13,7 @@ module T::Props::Utils
when Symbol, NilClass, Numeric when Symbol, NilClass, Numeric
what what
when Array when Array
what.map {|v| deep_clone_object(v, freeze: freeze)} what.map { |v| deep_clone_object(v, freeze: freeze) }
when Hash when Hash
h = what.class.new h = what.class.new
what.each do |k, v| what.each do |k, v|

View File

@ -6,7 +6,7 @@ module T::Props::WeakConstructor
extend T::Sig extend T::Sig
# checked(:never) - O(runtime object construction) # checked(:never) - O(runtime object construction)
sig {params(hash: T::Hash[Symbol, T.untyped]).void.checked(:never)} sig { params(hash: T::Hash[Symbol, T.untyped]).void.checked(:never) }
def initialize(hash={}) def initialize(hash={})
decorator = self.class.decorator decorator = self.class.decorator
@ -28,7 +28,7 @@ module T::Props::WeakConstructor::DecoratorMethods
# we'll use to check for any unrecognized input.) # we'll use to check for any unrecognized input.)
# #
# checked(:never) - O(runtime object construction) # checked(:never) - O(runtime object construction)
sig {params(instance: T::Props::WeakConstructor, hash: T::Hash[Symbol, T.untyped]).returns(Integer).checked(:never)} sig { params(instance: T::Props::WeakConstructor, hash: T::Hash[Symbol, T.untyped]).returns(Integer).checked(:never) }
def construct_props_without_defaults(instance, hash) def construct_props_without_defaults(instance, hash)
# Use `each_pair` rather than `count` because, as of Ruby 2.6, the latter delegates to Enumerator # Use `each_pair` rather than `count` because, as of Ruby 2.6, the latter delegates to Enumerator
# and therefore allocates for each entry. # and therefore allocates for each entry.
@ -49,7 +49,7 @@ module T::Props::WeakConstructor::DecoratorMethods
# we'll use to check for any unrecognized input.) # we'll use to check for any unrecognized input.)
# #
# checked(:never) - O(runtime object construction) # checked(:never) - O(runtime object construction)
sig {params(instance: T::Props::WeakConstructor, hash: T::Hash[Symbol, T.untyped]).returns(Integer).checked(:never)} sig { params(instance: T::Props::WeakConstructor, hash: T::Hash[Symbol, T.untyped]).returns(Integer).checked(:never) }
def construct_props_with_defaults(instance, hash) def construct_props_with_defaults(instance, hash)
# Use `each_pair` rather than `count` because, as of Ruby 2.6, the latter delegates to Enumerator # Use `each_pair` rather than `count` because, as of Ruby 2.6, the latter delegates to Enumerator
# and therefore allocates for each entry. # and therefore allocates for each entry.

View File

@ -14,7 +14,7 @@ module T::Sig
# At runtime, does nothing, but statically it is treated exactly the same # At runtime, does nothing, but statically it is treated exactly the same
# as T::Sig#sig. Only use it in cases where you can't use T::Sig#sig. # as T::Sig#sig. Only use it in cases where you can't use T::Sig#sig.
T::Sig::WithoutRuntime.sig {params(arg0: T.nilable(Symbol), blk: T.proc.bind(T::Private::Methods::DeclBuilder).void).void} T::Sig::WithoutRuntime.sig { params(arg0: T.nilable(Symbol), blk: T.proc.bind(T::Private::Methods::DeclBuilder).void).void }
def self.sig(arg0=nil, &blk); end # rubocop:disable Lint/DuplicateMethods def self.sig(arg0=nil, &blk); end # rubocop:disable Lint/DuplicateMethods
$VERBOSE = original_verbose $VERBOSE = original_verbose
@ -23,7 +23,7 @@ module T::Sig
# Declares a method with type signatures and/or # Declares a method with type signatures and/or
# abstract/override/... helpers. See the documentation URL on # abstract/override/... helpers. See the documentation URL on
# {T::Helpers} # {T::Helpers}
T::Sig::WithoutRuntime.sig {params(arg0: T.nilable(Symbol), blk: T.proc.bind(T::Private::Methods::DeclBuilder).void).void} T::Sig::WithoutRuntime.sig { params(arg0: T.nilable(Symbol), blk: T.proc.bind(T::Private::Methods::DeclBuilder).void).void }
def sig(arg0=nil, &blk) def sig(arg0=nil, &blk)
T::Private::Methods.declare_sig(self, Kernel.caller_locations(1, 1)&.first, arg0, &blk) T::Private::Methods.declare_sig(self, Kernel.caller_locations(1, 1)&.first, arg0, &blk)
end end

View File

@ -30,7 +30,7 @@ class T::ImmutableStruct < T::InexactStruct
end end
# Matches the one in WeakConstructor, but freezes the object # Matches the one in WeakConstructor, but freezes the object
sig {params(hash: T::Hash[Symbol, T.untyped]).void.checked(:never)} sig { params(hash: T::Hash[Symbol, T.untyped]).void.checked(:never) }
def initialize(hash={}) def initialize(hash={})
super super
@ -38,7 +38,7 @@ class T::ImmutableStruct < T::InexactStruct
end end
# Matches the signature in Props, but raises since this is an immutable struct and only const is allowed # Matches the signature in Props, but raises since this is an immutable struct and only const is allowed
sig {params(name: Symbol, cls: T.untyped, rules: T.untyped).void} sig { params(name: Symbol, cls: T.untyped, rules: T.untyped).void }
def self.prop(name, cls, **rules) def self.prop(name, cls, **rules)
return super if (cls.is_a?(Hash) && cls[:immutable]) || rules[:immutable] return super if (cls.is_a?(Hash) && cls[:immutable]) || rules[:immutable]

View File

@ -83,27 +83,27 @@ module T::Types
# Note: order of cases here matters! # Note: order of cases here matters!
if t1.is_a?(T::Types::Union) # 7, 8, 9 if t1.is_a?(T::Types::Union) # 7, 8, 9
# this will be incorrect if/when we have Type members # this will be incorrect if/when we have Type members
return t1.types.all? {|t1_member| t1_member.subtype_of?(t2)} return t1.types.all? { |t1_member| t1_member.subtype_of?(t2) }
end end
if t2.is_a?(T::Types::Intersection) # 2, 5 if t2.is_a?(T::Types::Intersection) # 2, 5
# this will be incorrect if/when we have Type members # this will be incorrect if/when we have Type members
return t2.types.all? {|t2_member| t1.subtype_of?(t2_member)} return t2.types.all? { |t2_member| t1.subtype_of?(t2_member) }
end end
if t2.is_a?(T::Types::Union) if t2.is_a?(T::Types::Union)
if t1.is_a?(T::Types::Intersection) # 6 if t1.is_a?(T::Types::Intersection) # 6
# dropping either of parts eagerly make subtype test be too strict. # dropping either of parts eagerly make subtype test be too strict.
# we have to try both cases, when we normally try only one # we have to try both cases, when we normally try only one
return t2.types.any? {|t2_member| t1.subtype_of?(t2_member)} || return t2.types.any? { |t2_member| t1.subtype_of?(t2_member) } ||
t1.types.any? {|t1_member| t1_member.subtype_of?(t2)} t1.types.any? { |t1_member| t1_member.subtype_of?(t2) }
end end
return t2.types.any? {|t2_member| t1.subtype_of?(t2_member)} # 3 return t2.types.any? { |t2_member| t1.subtype_of?(t2_member) } # 3
end end
if t1.is_a?(T::Types::Intersection) # 4 if t1.is_a?(T::Types::Intersection) # 4
# this will be incorrect if/when we have Type members # this will be incorrect if/when we have Type members
return t1.types.any? {|t1_member| t1_member.subtype_of?(t2)} return t1.types.any? { |t1_member| t1_member.subtype_of?(t2) }
end end
# 1; Start with some special cases # 1; Start with some special cases

View File

@ -11,7 +11,7 @@ module T::Types
end end
def types def types
@types ||= @inner_types.map {|type| T::Utils.coerce(type)} @types ||= @inner_types.map { |type| T::Utils.coerce(type) }
end end
def build_type def build_type

View File

@ -10,7 +10,7 @@ module T::Types
end end
def types def types
@types ||= @inner_types.transform_values {|v| T::Utils.coerce(v)} @types ||= @inner_types.transform_values { |v| T::Utils.coerce(v) }
end end
def build_type def build_type
@ -26,16 +26,16 @@ module T::Types
# overrides Base # overrides Base
def recursively_valid?(obj) def recursively_valid?(obj)
return false unless obj.is_a?(Hash) return false unless obj.is_a?(Hash)
return false if types.any? {|key, type| !type.recursively_valid?(obj[key])} return false if types.any? { |key, type| !type.recursively_valid?(obj[key]) }
return false if obj.any? {|key, _| !types[key]} return false if obj.any? { |key, _| !types[key] }
true true
end end
# overrides Base # overrides Base
def valid?(obj) def valid?(obj)
return false unless obj.is_a?(Hash) return false unless obj.is_a?(Hash)
return false if types.any? {|key, type| !type.valid?(obj[key])} return false if types.any? { |key, type| !type.valid?(obj[key]) }
return false if obj.any? {|key, _| !types[key]} return false if obj.any? { |key, _| !types[key] }
true true
end end
@ -48,7 +48,7 @@ module T::Types
when TypedHash when TypedHash
# warning: covariant hashes # warning: covariant hashes
key1, key2, *keys_rest = types.keys.map {|key| T::Utils.coerce(key.class)} key1, key2, *keys_rest = types.keys.map { |key| T::Utils.coerce(key.class) }
key_type = if !key2.nil? key_type = if !key2.nil?
T::Types::Union::Private::Pool.union_of_types(key1, key2, keys_rest) T::Types::Union::Private::Pool.union_of_types(key1, key2, keys_rest)
elsif key1.nil? elsif key1.nil?

View File

@ -32,12 +32,12 @@ module T::Types
# overrides Base # overrides Base
def recursively_valid?(obj) def recursively_valid?(obj)
types.all? {|type| type.recursively_valid?(obj)} types.all? { |type| type.recursively_valid?(obj) }
end end
# overrides Base # overrides Base
def valid?(obj) def valid?(obj)
types.all? {|type| type.valid?(obj)} types.all? { |type| type.valid?(obj) }
end end
# overrides Base # overrides Base

View File

@ -44,11 +44,11 @@ module T::Types
nilable = T::Utils.coerce(NilClass) nilable = T::Utils.coerce(NilClass)
trueclass = T::Utils.coerce(TrueClass) trueclass = T::Utils.coerce(TrueClass)
falseclass = T::Utils.coerce(FalseClass) falseclass = T::Utils.coerce(FalseClass)
if types.any? {|t| t == nilable} if types.any? { |t| t == nilable }
remaining_types = types.reject {|t| t == nilable} remaining_types = types.reject { |t| t == nilable }
"T.nilable(#{type_shortcuts(remaining_types)})" "T.nilable(#{type_shortcuts(remaining_types)})"
elsif types.any? {|t| t == trueclass} && types.any? {|t| t == falseclass} elsif types.any? { |t| t == trueclass } && types.any? { |t| t == falseclass }
remaining_types = types.reject {|t| t == trueclass || t == falseclass} remaining_types = types.reject { |t| t == trueclass || t == falseclass }
type_shortcuts([T::Private::Types::StringHolder.new("T::Boolean")] + remaining_types) type_shortcuts([T::Private::Types::StringHolder.new("T::Boolean")] + remaining_types)
else else
names = types.map(&:name).compact.sort names = types.map(&:name).compact.sort
@ -58,12 +58,12 @@ module T::Types
# overrides Base # overrides Base
def recursively_valid?(obj) def recursively_valid?(obj)
types.any? {|type| type.recursively_valid?(obj)} types.any? { |type| type.recursively_valid?(obj) }
end end
# overrides Base # overrides Base
def valid?(obj) def valid?(obj)
types.any? {|type| type.valid?(obj)} types.any? { |type| type.valid?(obj) }
end end
# overrides Base # overrides Base
@ -72,7 +72,7 @@ module T::Types
end end
def unwrap_nilable def unwrap_nilable
non_nil_types = types.reject {|t| t == T::Utils::Nilable::NIL_TYPE} non_nil_types = types.reject { |t| t == T::Utils::Nilable::NIL_TYPE }
return nil if types.length == non_nil_types.length return nil if types.length == non_nil_types.length
case non_nil_types.length case non_nil_types.length
when 0 then nil when 0 then nil