diff --git a/.bazelrc b/.bazelrc index 4d1e645987..b3562d2149 100644 --- a/.bazelrc +++ b/.bazelrc @@ -76,8 +76,8 @@ test:coverage --spawn_strategy=local build --define=versioned=false build:versioned --workspace_status_command=tools/buildstamp/get_workspace_status --stamp --define=versioned=true -build:dbg-darwin --config=dbg --platforms=@//tools/config:darwin_x86_64 -build:dbg-linux --config=dbg --platforms=@//tools/config:linux_x86_64 +build:dbg-darwin --config=dbg --platforms=@//tools/platforms:darwin_x86_64 +build:dbg-linux --config=dbg --platforms=@//tools/platforms:linux_x86_64 ## ## Configurations used for releases @@ -94,9 +94,19 @@ build:release-debug-common --config=forcedebug build:release-debug-common --config=debugsymbols build:release-debug-common --config=skipslowenforce +# This mode has been added to allow tracking the symbols +# which are to blame for usages of untyped code in a codebase. +# This allows identifying what symbols would be the highest impact +# to type. +# +# Earlier, we allowed for such statistic collection only in DEBUG mode +# However, that was far too slow for our purposes. Thus, this mode allows +# you to build in release mode and track these statistics. +build:untyped-blame --copt=-DTRACK_UNTYPED_BLAME_MODE + # harden: mark relocation sections read-only build:release-linux --linkopt=-Wl,-z,relro,-z,now -build:release-linux --config=lto-linux --config=release-common --platforms=@//tools/config:linux_x86_64 +build:release-linux --config=lto-linux --config=release-common # This is to turn on vector instructions where available. # We used to do this unconditionally, but Rosetta 2 doesn't translate all vector instructions well. @@ -109,7 +119,7 @@ build:release-linux --config=lto-linux --config=release-common --platforms=@//to build:release-linux --copt=-march=sandybridge build:release-sanitized-linux --copt=-march=sandybridge -build:release-mac --config=release-common --platforms=@//tools/config:darwin_x86_64 +build:release-mac --config=release-common --platforms=@//tools/platforms:darwin_x86_64 build:release-debug-linux --config=release-linux build:release-debug-linux --config=release-debug-common @@ -170,6 +180,9 @@ build:lto --config=static-libs ## flags that substantially increase Clang&LLVMs ability to devirtualize calls build:lto-linux --linkopt=-Wl,--icf=all + +# Improves linking time on arm64 hosts +build:lto-linux --linkopt=-Wl,--thinlto-jobs=all build:lto-linux --config=lto # By default, we make static builds, but we can also use dynamic linking if asked to. diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 09bafdb0db..0f169838bc 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,14 +12,20 @@ jobs: runs-on: 'ubuntu-20.04' steps: - uses: actions/checkout@v3 + - name: Format Bazel files + run: | + ./tools/scripts/format_build_files.sh + if ! git diff --quiet; then + git diff + echo "" + echo "-----------------------------------------------------" + echo "Re-run ./tools/scripts/format_build_files.sh and push" + echo "-----------------------------------------------------" + exit 1 + fi - uses: actions/setup-go@v3 with: go-version: '>=1.19' - - name: Format Bazel files - run: | - set -e - GOBIN="$PWD" go install github.com/bazelbuild/buildtools/buildifier@latest - git ls-files -- '**.bzl' '**/BUILD' WORKSPACE | xargs ./buildifier -mode=diff - name: Lint workflow files run: | go install github.com/rhysd/actionlint/cmd/actionlint@latest diff --git a/.ruby-version b/.ruby-version index 37c2961c24..1f7da99d4e 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.7.2 +2.7.7 diff --git a/.sorbet-buildkite/build-sorbet-runtime.sh b/.sorbet-buildkite/build-sorbet-runtime.sh index fcab182dc4..cd4806ec2d 100755 --- a/.sorbet-buildkite/build-sorbet-runtime.sh +++ b/.sorbet-buildkite/build-sorbet-runtime.sh @@ -7,7 +7,7 @@ pushd gems/sorbet-runtime echo "--- setup :ruby:" eval "$(rbenv init -)" -runtime_versions=(2.7.2 3.1.2) +runtime_versions=(2.7.7 3.1.2) for runtime_version in "${runtime_versions[@]}"; do rbenv install --skip-existing "$runtime_version" @@ -23,7 +23,7 @@ for runtime_version in "${runtime_versions[@]}"; do failed= - if [ "$runtime_version" = "2.7.2" ]; then + if [ "$runtime_version" = "2.7.7" ]; then # Our Rubocop version doesn't understand Ruby 3.1 as a valid Ruby version echo "+++ rubocop ($runtime_version)" if ! rbenv exec bundle exec rake rubocop; then @@ -36,6 +36,17 @@ for runtime_version in "${runtime_versions[@]}"; do failed=1 fi + pushd test/wholesome + + rbenv exec bundle config set path 'vendor/bundle' + rbenv exec bundle install + + if ! rbenv exec bundle exec rake test; then + failed=1 + fi + + popd + if [ "$failed" != "" ]; then exit 1 fi diff --git a/.sorbet-buildkite/build-static-release.sh b/.sorbet-buildkite/build-static-release.sh index ebfebe8d1f..133352d3a8 100755 --- a/.sorbet-buildkite/build-static-release.sh +++ b/.sorbet-buildkite/build-static-release.sh @@ -5,20 +5,24 @@ set -euo pipefail export JOB_NAME=build-static-release source .buildkite/tools/setup-bazel.sh -unameOut="$(uname -s)" -case "${unameOut}" in - Linux*) platform="linux";; - Darwin*) platform="mac";; - *) exit 1 +kernel_name="$(uname -s | tr 'A-Z' 'a-z')" +processor_name="$(uname -m)" + +platform="${kernel_name}-${processor_name}" +case "$platform" in + linux-x86_64|linux-aarch64) + CONFIG_OPTS="--config=release-linux" + ;; + darwin-x86_64|darwin-arm64) + CONFIG_OPTS="--config=release-mac" + command -v autoconf >/dev/null 2>&1 || brew install autoconf + ;; + *) + echo >&2 "Building on $platform is not implemented" + exit 1 + ;; esac -if [[ "linux" == "$platform" ]]; then - CONFIG_OPTS="--config=release-linux" -elif [[ "mac" == "$platform" ]]; then - CONFIG_OPTS="--config=release-mac" - command -v autoconf >/dev/null 2>&1 || brew install autoconf -fi - echo will run with $CONFIG_OPTS ./bazel build //main:sorbet --strip=always $CONFIG_OPTS @@ -32,7 +36,7 @@ pushd gems/sorbet-static git_commit_count=$(git rev-list --count HEAD) release_version="0.5.${git_commit_count}" sed -i.bak "s/0\\.0\\.0/${release_version}/" sorbet-static.gemspec -if [[ "mac" == "$platform" ]]; then +if [[ "darwin" == "$kernel_name" ]]; then # Our binary should work on almost all OSes. The oldest v8 publishes is -14 # so I'm going with that for now. for i in {14..22}; do @@ -70,11 +74,11 @@ rbenv exec gem uninstall --all --executables --ignore-dependencies minitest moch rbenv exec gem uninstall --all --executables --ignore-dependencies sorbet sorbet-static trap 'rbenv exec gem uninstall --all --executables --ignore-dependencies sorbet sorbet-static' EXIT -if [[ "mac" == "$platform" ]]; then +if [[ "darwin" == "$kernel_name" ]]; then gem_platform="$(ruby -e "(platform = Gem::Platform.local).cpu = 'universal'; puts(platform.to_s)")" rbenv exec gem install ../../gems/sorbet-static/sorbet-static-*-"$gem_platform".gem else - rbenv exec gem install ../../gems/sorbet-static/sorbet-static-*-x86_64-linux.gem + rbenv exec gem install ../../gems/sorbet-static/sorbet-static-*-"$processor_name"-linux.gem fi rbenv exec gem install sorbet-*.gem @@ -104,7 +108,7 @@ rm -rf _out_ mkdir -p _out_/gems mv gems/sorbet-static/sorbet-static-*.gem _out_/gems/ -if [[ "linux" == "$platform" ]]; then +if [[ "$kernel_name" == "linux" ]]; then mv gems/sorbet/sorbet*.gem _out_/gems/ fi diff --git a/.sorbet-buildkite/publish-ruby-gems.sh b/.sorbet-buildkite/publish-ruby-gems.sh index 2a6494240e..5499b39afe 100755 --- a/.sorbet-buildkite/publish-ruby-gems.sh +++ b/.sorbet-buildkite/publish-ruby-gems.sh @@ -26,46 +26,52 @@ source .buildkite/tools/with_backoff.sh rbenv install --skip-existing +publish_sorbet_static_gem() { + gem_archive=$1 + platform=$2 + + if gem list --remote rubygems.org --exact 'sorbet-static' | grep -q "${release_version}[^,]*${platform}"; then + echo "$gem_archive already published." + return + fi + + # This is last so the exit code is used as the status code for with_backoff + gem push --verbose "$gem_archive" +} + # Push the sorbet-static gems first, in case they fail. We don't want to end # up in a weird state where 'sorbet' requires a pinned version of # sorbet-static, but the sorbet-static gem push failed. # # (By failure here, we mean that RubyGems.org 502'd for some reason.) for gem_archive in "_out_/gems/sorbet-static-$release_version"-*.gem; do - echo "Attempting to publish $gem_archive" - if [[ "$gem_archive" =~ _out_/gems/sorbet-static-([^-]*)-([^.]*).gem ]]; then - platform="${BASH_REMATCH[2]}" - if ! gem list --remote rubygems.org --exact 'sorbet-static' | grep -q "${release_version}[^,]*${platform}"; then - with_backoff gem push --verbose "$gem_archive" - else - echo "$gem_archive already published." - fi - else + if ! [[ "$gem_archive" =~ _out_/gems/sorbet-static-([^-]*)-([^.]*).gem ]]; then echo "Regex match failed. This should never happen." exit 1 fi + + platform="${BASH_REMATCH[2]}" + + echo "Attempting to publish $gem_archive" + with_backoff publish_sorbet_static_gem "$gem_archive" "$platform" done -gem_archive="_out_/gems/sorbet-runtime-$release_version.gem" -echo "Attempting to publish $gem_archive" -if ! gem list --remote rubygems.org --exact 'sorbet-runtime' | grep -q "$release_version"; then - with_backoff gem push --verbose "$gem_archive" -else - echo "$gem_archive already published." -fi +# Sometimes the 'gem push' times out, but after the connection dies, the server +# decides to finish publishing the gem. So we have to interleave 'gem list' and +# 'gem push' calls--it's not enough to just check whether it exists once. +publish_gem() { + gem_name=$1 + gem_archive="_out_/gems/$gem_name-$release_version.gem" -gem_archive="_out_/gems/sorbet-$release_version.gem" -echo "Attempting to publish $gem_archive" -if ! gem list --remote rubygems.org --exact 'sorbet' | grep -q "$release_version"; then - with_backoff gem push --verbose "$gem_archive" -else - echo "$gem_archive already published." -fi + if gem list --remote rubygems.org --exact "$gem_name" | grep -q "$release_version"; then + echo "$gem_archive already published." + return + fi -gem_archive="_out_/gems/sorbet-static-and-runtime-$release_version.gem" -echo "Attempting to publish $gem_archive" -if ! gem list --remote rubygems.org --exact 'sorbet-static-and-runtime' | grep -q "$release_version"; then - with_backoff gem push --verbose "$gem_archive" -else - echo "$gem_archive already published." -fi + # This is last so the exit code is used as the status code for with_backoff + gem push --verbose "$gem_archive" +} + +with_backoff publish_gem "sorbet-runtime" +with_backoff publish_gem "sorbet" +with_backoff publish_gem "sorbet-static-and-runtime" diff --git a/.sorbet-buildkite/test-compiler.sh b/.sorbet-buildkite/test-compiler.sh index fba8daf6bc..eea288f241 100755 --- a/.sorbet-buildkite/test-compiler.sh +++ b/.sorbet-buildkite/test-compiler.sh @@ -10,6 +10,7 @@ case "${unameOut}" in esac if [[ "linux" == "$platform" ]]; then + curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - apt-get update apt-get install -yy libncurses5-dev libncursesw5-dev xxd elif [[ "mac" == "$platform" ]]; then @@ -38,6 +39,10 @@ mkdir -p _out_ test_args=( "//test:compiler" "//test/cli/compiler" + # These are two tests that depend on sorbet_ruby, and it's annoying to have + # to build sorbet_ruby on the static sanitized job (delays test start time) + "//test:end_to_end_rbi_test" + "//test:single_package_runner" "-c" "opt" "--config=forcedebug" diff --git a/.sorbet-buildkite/test-vscode-extension.sh b/.sorbet-buildkite/test-vscode-extension.sh index 42e8a8896c..37f44e3a05 100755 --- a/.sorbet-buildkite/test-vscode-extension.sh +++ b/.sorbet-buildkite/test-vscode-extension.sh @@ -4,6 +4,7 @@ set -euo pipefail # TODO: We probably want to move this into https://github.com/sorbet/sorbet-build-image eventually +curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - apt-get update apt-get install -y libxshmfence-dev libnss3-dev libatk1.0-0 libatk-bridge2.0-0 \ libdrm2 xvfb libgdk-pixbuf2.0-0 libgtk-3-0 libgbm1 libasound2 diff --git a/.sorbet-buildkite/tools/with_backoff.sh b/.sorbet-buildkite/tools/with_backoff.sh index 4eea861ca7..370f2b851c 100644 --- a/.sorbet-buildkite/tools/with_backoff.sh +++ b/.sorbet-buildkite/tools/with_backoff.sh @@ -6,7 +6,7 @@ echo "--- Loading with_backoff helper" # https://stackoverflow.com/a/8351489 with_backoff() { - local attempts=5 + local attempts=8 local timeout=1 # doubles each failure local attempt=0 diff --git a/.vscode/settings.json b/.vscode/settings.json index 2655d6b4ef..91e812f5b9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,6 @@ { + "files.insertFinalNewline": true, + "editor.renderFinalNewline": "off", "editor.insertSpaces": true, "editor.rulers": [120], // Clangd 9.0 has a new semantic highlighting feature, but it's buggy and causes diff --git a/README.md b/README.md index 3b1b799e24..f72623471c 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ Otherwise, add this line to your `Gemfile`: ```ruby gem 'scip-ruby', require: false, :group => :development ``` + After either of those steps, run `bundle install` to download and install fetch `scip-ruby`. diff --git a/WORKSPACE b/WORKSPACE index c0da1a0984..71e4d07932 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -61,6 +61,8 @@ BAZEL_VERSION = "5.2.0" BAZEL_INSTALLER_VERSION_LINUX_X86_64_SHA = "7d9ef51beab5726c55725fb36675c6fed0518576d3ba51fb4067580ddf7627c4" +BAZEL_INSTALLER_VERSION_LINUX_ARM64_SHA = "ae50cb7d64aebee986287134ff8ca0335651a0c1685348b3216f3fdfa20ff7e7" + BAZEL_INSTALLER_VERSION_DARWIN_X86_64_SHA = "645e7c335efc3207905e98f0c56a598b7cb0282d54d9470e80f38fb698064fb3" BAZEL_INSTALLER_VERSION_DARWIN_ARM64_SHA = "bc018ee7980cdf1c3f0099ec1568847a1756a3c00f1f9440bca44c26ceb3d90f" diff --git a/ast/Helpers.cc b/ast/Helpers.cc index 276be355f8..453141c9d2 100644 --- a/ast/Helpers.cc +++ b/ast/Helpers.cc @@ -28,7 +28,9 @@ bool definesBehavior(const ExpressionPtr &expr) { }, [&](const ast::Assign &asgn) { - if (ast::isa_tree(asgn.lhs)) { + // this check can fire before the namer converts lhs constants in assignments from UnresolvedConstantLit -> + // ConstantLit, so we have to allow for both types. + if (ast::isa_tree(asgn.lhs) || ast::isa_tree(asgn.lhs)) { result = false; } else { result = true; diff --git a/ast/Helpers.h b/ast/Helpers.h index 1c99c89480..ae85373ab3 100644 --- a/ast/Helpers.h +++ b/ast/Helpers.h @@ -375,6 +375,11 @@ class MK { std::move(type)); } + static ExpressionPtr AssumeType(core::LocOffsets loc, ExpressionPtr value, ExpressionPtr type) { + return ast::make_expression(loc, core::Types::todo(), std::move(value), core::Names::assumeType(), + std::move(type)); + } + static ExpressionPtr ClassOf(core::LocOffsets loc, ExpressionPtr value) { return Send1(loc, T(loc), core::Names::classOf(), loc, std::move(value)); } @@ -426,11 +431,6 @@ class MK { return Constant(loc, core::Symbols::Magic()); } - static ExpressionPtr SelfNew(core::LocOffsets loc, core::LocOffsets funLoc, int numPosArgs, - ast::Send::ARGS_store args, Send::Flags flags = {}) { - return Send(loc, Magic(loc), core::Names::selfNew(), funLoc, numPosArgs, std::move(args), flags); - } - static ExpressionPtr DefineTopClassOrModule(core::LocOffsets loc, core::ClassOrModuleRef klass) { Send::Flags flags; flags.isRewriterSynthesized = true; @@ -452,6 +452,14 @@ class MK { return ret; } + static ExpressionPtr RaiseTypedUnimplemented(core::LocOffsets loc) { + auto kernel = Constant(loc, core::Symbols::Kernel()); + auto msg = String(loc, core::Names::rewriterRaiseUnimplemented()); + auto ret = Send1(loc, std::move(kernel), core::Names::raise(), loc, std::move(msg)); + cast_tree(ret)->flags.isRewriterSynthesized = true; + return ret; + } + static bool isRootScope(const ast::ExpressionPtr &scope) { if (ast::isa_tree(scope)) { return true; @@ -469,11 +477,7 @@ class MK { } static bool isSelfNew(ast::Send *send) { - if (send->fun != core::Names::selfNew()) { - return false; - } - - return isMagicClass(send->recv); + return send->fun == core::Names::new_() && send->recv.isSelfReference(); } static core::NameRef arg2Name(const ExpressionPtr &arg) { diff --git a/ast/TreeSanityChecks.cc b/ast/TreeSanityChecks.cc index 4f33c699b1..020bad2768 100644 --- a/ast/TreeSanityChecks.cc +++ b/ast/TreeSanityChecks.cc @@ -79,7 +79,8 @@ void Cast::_sanityCheck() { ENFORCE(arg); ENFORCE(type); ENFORCE(cast == core::Names::cast() || cast == core::Names::assertType() || cast == core::Names::let() || - cast == core::Names::uncheckedLet() || cast == core::Names::bind() || cast == core::Names::syntheticBind()); + cast == core::Names::uncheckedLet() || cast == core::Names::bind() || + cast == core::Names::syntheticBind() || cast == core::Names::assumeType()); ENFORCE(typeExpr); } diff --git a/ast/Trees.cc b/ast/Trees.cc index 6850da9af8..e240795c7e 100644 --- a/ast/Trees.cc +++ b/ast/Trees.cc @@ -2,7 +2,7 @@ #include "absl/synchronization/blocking_counter.h" #include "common/concurrency/ConcurrentQueue.h" #include "common/concurrency/WorkerPool.h" -#include "common/formatting.h" +#include "common/strings/formatting.h" #include "common/typecase.h" #include "core/Symbols.h" #include diff --git a/ast/Trees.h b/ast/Trees.h index 2052caf32a..21dc847367 100644 --- a/ast/Trees.h +++ b/ast/Trees.h @@ -841,6 +841,10 @@ EXPRESSION(Send) { return numPosArgs_; } + bool onlyPosArgs() const { + return numPosArgs_ == args.size(); + } + // The number of keyword arguments in the Send. uint16_t numKwArgs() const { uint16_t range = args.size() - numPosArgs_ - (hasKwSplat() ? 1 : 0) - (hasBlock() ? 1 : 0); diff --git a/ast/desugar/BUILD b/ast/desugar/BUILD index bbfdc3d0b6..7858bfb90f 100644 --- a/ast/desugar/BUILD +++ b/ast/desugar/BUILD @@ -37,7 +37,7 @@ cc_test( visibility = ["//tools:__pkg__"], deps = [ "//ast/desugar", - "@doctest", - "@doctest//:doctest_main", + "@doctest//doctest", + "@doctest//doctest:main", ], ) diff --git a/ast/desugar/Desugar.cc b/ast/desugar/Desugar.cc index 3b0040e189..1aee80422c 100644 --- a/ast/desugar/Desugar.cc +++ b/ast/desugar/Desugar.cc @@ -7,7 +7,7 @@ #include "ast/desugar/Desugar.h" #include "ast/verifier/verifier.h" #include "common/common.h" -#include "common/formatting.h" +#include "common/strings/formatting.h" #include "core/Names.h" #include "core/errors/desugar.h" #include "core/errors/internal.h" @@ -667,7 +667,8 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) if (absl::c_any_of(send->args, [](auto &arg) { return parser::isa_node(arg.get()) || - parser::isa_node(arg.get()); + parser::isa_node(arg.get()) || + parser::isa_node(arg.get()); })) { // Build up an array that represents the keyword args for the send. When there is a Kwsplat, treat // all keyword arguments as a single argument. @@ -694,11 +695,14 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) // skip inlining the kwargs if there are any kwsplat nodes present if (absl::c_any_of(hash->pairs, [](auto &node) { - // the parser guarantees that if we see a kwargs hash it only contains pair or - // kwsplat nodes + // the parser guarantees that if we see a kwargs hash it only contains pair, + // kwsplat, or forwarded kwrest arg nodes ENFORCE(parser::isa_node(node.get()) || - parser::isa_node(node.get())); - return parser::isa_node(node.get()); + parser::isa_node(node.get()) || + parser::isa_node(node.get())); + + return parser::isa_node(node.get()) || + parser::isa_node(node.get()); })) { elts.emplace_back(std::move(node)); } else { @@ -736,7 +740,7 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) // synthesize a call to // Magic.callWithSplat(receiver, method, argArray, [&blk]) // The callWithSplat implementation (in C++) will unpack a - // tuple type and call into the normal call merchanism. + // tuple type and call into the normal call mechanism. unique_ptr block; auto argnodes = std::move(send->args); bool anonymousBlockPass = false; @@ -763,6 +767,14 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) argnodes.erase(fwdIt); } + auto hasFwdRestArg = false; + auto fwdRestIt = absl::c_find_if( + argnodes, [](auto &arg) { return parser::isa_node(arg.get()); }); + if (fwdRestIt != argnodes.end()) { + hasFwdRestArg = true; + argnodes.erase(fwdRestIt); + } + auto array = make_unique(locZeroLen, std::move(argnodes)); auto args = node2TreeImpl(dctx, std::move(array)); @@ -783,6 +795,14 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) argsConcat = MK::Send1(loc, std::move(argsConcat), core::Names::concat(), locZeroLen, std::move(kwargsArray)); + args = std::move(argsConcat); + } else if (hasFwdRestArg) { + auto fwdArgs = MK::Local(loc, core::Names::fwdArgs()); + auto argsSplat = MK::Send0(loc, std::move(fwdArgs), core::Names::toA(), locZeroLen); + auto tUnsafe = MK::Unsafe(loc, std::move(argsSplat)); + auto argsConcat = + MK::Send1(loc, std::move(args), core::Names::concat(), locZeroLen, std::move(tUnsafe)); + args = std::move(argsConcat); } @@ -826,22 +846,31 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) } else { int numPosArgs = send->args.size(); if (numPosArgs > 0) { - // Deconstruct the kwargs hash in the last argument if it's present. - if (auto *hash = parser::cast_node(send->args.back().get())) { + // The keyword arguments hash can be the last argument if there is no block + // or second to last argument otherwise + int kwargsHashIndex = send->args.size() - 1; + if (!parser::isa_node(send->args[kwargsHashIndex].get())) { + kwargsHashIndex = max(0, kwargsHashIndex - 1); + } + + // Deconstruct the kwargs hash if it's present. + if (auto *hash = parser::cast_node(send->args[kwargsHashIndex].get())) { if (hash->kwargs) { numPosArgs--; // skip inlining the kwargs if there are any non-key/value pairs present if (!absl::c_any_of(hash->pairs, [](auto &node) { - // the parser guarantees that if we see a kwargs hash it only contains pair or - // kwsplat nodes + // the parser guarantees that if we see a kwargs hash it only contains pair, + // kwsplat, or forwarded kwrest nodes ENFORCE(parser::isa_node(node.get()) || + parser::isa_node(node.get()) || parser::isa_node(node.get())); - return parser::isa_node(node.get()); + return parser::isa_node(node.get()) || + parser::isa_node(node.get()); })) { // hold a reference to the node, and remove it from the back fo the send list - auto node = std::move(send->args.back()); - send->args.pop_back(); + auto node = std::move(send->args[kwargsHashIndex]); + send->args.erase(send->args.begin() + kwargsHashIndex); // inline the hash into the send args for (auto &entry : hash->pairs) { @@ -997,7 +1026,17 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) } auto *splat = parser::cast_node(pairAsExpression.get()); - ENFORCE(splat != nullptr, "kwsplat cast failed"); + + ExpressionPtr expr; + if (splat != nullptr) { + expr = node2TreeImpl(dctx, std::move(splat->expr)); + } else { + auto *fwdKwrestArg = parser::cast_node(pairAsExpression.get()); + ENFORCE(fwdKwrestArg != nullptr, "kwsplat and fwdkwrestarg cast failed"); + + auto fwdKwargs = MK::Local(loc, core::Names::fwdKwargs()); + expr = MK::Unsafe(loc, std::move(fwdKwargs)); + } if (havePairsToMerge) { havePairsToMerge = false; @@ -1017,8 +1056,6 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) mergeValues.emplace_back(MK::Local(loc, acc)); } - auto expr = node2TreeImpl(dctx, std::move(splat->expr)); - // If this is the first argument to `.`, it needs to be duplicated as that // intrinsic is assumed to mutate its first argument. if (updateStmts.empty()) { @@ -2030,7 +2067,11 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) } if (isa_tree(varExpr)) { - varLoc = loc; + // In `rescue; ...; end`, we don't want the magic variable to look + // as if its loc is the entire `rescue; ...; end` span. Better to just point at + // the `rescue` keyword. + varLoc = (loc.endPos() - loc.beginPos()) > 6 ? core::LocOffsets{loc.beginPos(), loc.beginPos() + 6} + : loc.copyWithZeroLength(); } else if (varExpr != nullptr) { body = MK::InsSeq1(varLoc, MK::Assign(varLoc, std::move(varExpr), MK::Local(varLoc, var)), std::move(body)); diff --git a/ast/desugar/test/desugar_test.cc b/ast/desugar/test/desugar_test.cc index d3b911cfcc..cc3f95d2f8 100644 --- a/ast/desugar/test/desugar_test.cc +++ b/ast/desugar/test/desugar_test.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" // has to go first as it violates our requirements #include "ast/ast.h" #include "ast/desugar/Desugar.h" diff --git a/bazel b/bazel index f407ff6f00..fc59b5698d 100755 --- a/bazel +++ b/bazel @@ -55,6 +55,9 @@ processor_name="$(uname -m)" bazel_installer_platform="${kernel_name}-${processor_name}" case "$bazel_installer_platform" in linux-x86_64) ;; + linux-aarch64) + bazel_installer_platform="linux-arm64" + ;; darwin-x86_64) ;; darwin-arm64) # Pseudo Apple Silicon support by forcing x86_64 (Rosetta) for now @@ -85,24 +88,44 @@ mkdir -p "$BUILD_DIR" cd "$BUILD_DIR" echo "$PWD" - installer_name="bazel-${BAZEL_VERSION}-installer-${bazel_installer_platform}.sh" + check_sha () { + if [ "$actual_sha" != "$expected_sha" ]; then + echo >&2 "Installer checksum mismatch:" + echo >&2 " Expected: $expected_sha" + echo >&2 " Actual: $actual_sha" + echo >&2 "To accept this mismatch, update $bazel_installer_sha_variable in the WORKSPACE file and re-run." + exit 1 + fi + } BAZEL_REMOTE_SOURCE="${BAZEL_REMOTE_SOURCE:-https://github.com/bazelbuild/bazel/releases/download}" - BAZEL_INSTALLER_PATH="${BAZEL_INSTALLER_PATH:-$BAZEL_REMOTE_SOURCE/${BAZEL_VERSION}/$installer_name}" - curl -O -L "$BAZEL_INSTALLER_PATH" + # Bazel for linux-arm64 doesn't have a binary installer + if [ "$bazel_installer_platform" == "linux-arm64" ]; then + bazel_name="bazel-${BAZEL_VERSION}-linux-arm64" + BAZEL_PATH="${BAZEL_PATH:-$BAZEL_REMOTE_SOURCE/${BAZEL_VERSION}/${bazel_name}}" - actual_sha="$(shasum -a 256 "$installer_name" | awk '{print $1}')" - if [ "$actual_sha" != "$expected_sha" ]; then - echo >&2 "Installer checksum mismatch:" - echo >&2 " Expected: $expected_sha" - echo >&2 " Actual: $actual_sha" - echo >&2 "To accept this mismatch, update $bazel_installer_sha_variable in the WORKSPACE file and re-run." - exit 1 + curl -O -L "$BAZEL_PATH" + + actual_sha="$(shasum -a 256 "$bazel_name" | awk '{print $1}')" + check_sha + + mkdir -p "$bazel_bin_loc/${BAZEL_VERSION}/bin" + cp "$bazel_name" "$bazel_bin_loc/${BAZEL_VERSION}/bin/bazel-real" + chmod +x "$bazel_bin_loc/${BAZEL_VERSION}/bin/bazel-real" + else + installer_name="bazel-${BAZEL_VERSION}-installer-${bazel_installer_platform}.sh" + BAZEL_INSTALLER_PATH="${BAZEL_INSTALLER_PATH:-$BAZEL_REMOTE_SOURCE/${BAZEL_VERSION}/$installer_name}" + + curl -O -L "$BAZEL_INSTALLER_PATH" + + actual_sha="$(shasum -a 256 "$installer_name" | awk '{print $1}')" + check_sha + + chmod +x "$installer_name" + mkdir -p "$bazel_bin_loc" + "./${installer_name}" --base="${bazel_bin_loc}/${BAZEL_VERSION}" --bin="${bazel_bin_loc}/${BAZEL_VERSION}/bin_t" fi - chmod +x "$installer_name" - mkdir -p "$bazel_bin_loc" - "./${installer_name}" --base="${bazel_bin_loc}/${BAZEL_VERSION}" --bin="${bazel_bin_loc}/${BAZEL_VERSION}/bin_t" ) rm -rf "$BUILD_DIR" diff --git a/cfg/CFG.cc b/cfg/CFG.cc index 90a8bc3496..62d6cfb37f 100644 --- a/cfg/CFG.cc +++ b/cfg/CFG.cc @@ -1,10 +1,10 @@ #include "cfg/CFG.h" #include "absl/strings/escaping.h" #include "absl/strings/str_split.h" -#include "common/Timer.h" #include "common/UIntSetForEach.h" -#include "common/formatting.h" -#include "common/sort.h" +#include "common/sort/sort.h" +#include "common/strings/formatting.h" +#include "common/timers/Timer.h" // helps debugging template class std::unique_ptr; diff --git a/cfg/Instructions.cc b/cfg/Instructions.cc index 77b868df74..43cc6cd753 100644 --- a/cfg/Instructions.cc +++ b/cfg/Instructions.cc @@ -1,6 +1,6 @@ #include "Instructions.h" -#include "common/formatting.h" +#include "common/strings/formatting.h" #include "common/typecase.h" #include "core/Names.h" #include "core/TypeConstraint.h" @@ -123,7 +123,7 @@ string LoadSelf::showRaw(const core::GlobalState &gs, const CFG &cfg, int tabs) // NOTE(varun): loc copying Send::Send(LocalRef recv, core::LocOffsets receiverLoc, core::NameRef fun, core::LocOffsets funLoc, uint16_t numPosArgs, - const InlinedVector &args, InlinedVector argLocs, bool isPrivateOk, + const InlinedVector &args, InlinedVector &&argLocs, bool isPrivateOk, const shared_ptr &link) : isPrivateOk(isPrivateOk), numPosArgs(numPosArgs), fun(fun), recv(recv, receiverLoc), funLoc(funLoc), receiverLoc(receiverLoc), argLocs(std::move(argLocs)), link(move(link)) { @@ -154,7 +154,9 @@ core::LocOffsets Send::locWithoutBlock(core::LocOffsets bindLoc) { } if (!this->argLocs.empty()) { - return this->receiverLoc.join(this->argLocs.back()); + // For sig, the arg will often be a reference to an implicit self, + // so the back of argLocs is a zero width loc. + return this->receiverLoc.join(this->funLoc).join(this->argLocs.back()); } return this->receiverLoc.join(this->funLoc); diff --git a/cfg/Instructions.h b/cfg/Instructions.h index c8b1ae3bb2..f820264006 100644 --- a/cfg/Instructions.h +++ b/cfg/Instructions.h @@ -154,7 +154,7 @@ INSN(Send) : public Instruction { std::shared_ptr link; Send(LocalRef recv, core::LocOffsets receiverLoc, core::NameRef fun, core::LocOffsets funLoc, uint16_t numPosArgs, - const InlinedVector &args, InlinedVector argLocs, bool isPrivateOk = false, + const InlinedVector &args, InlinedVector &&argLocs, bool isPrivateOk = false, const std::shared_ptr &link = nullptr); core::LocOffsets locWithoutBlock(core::LocOffsets bindLoc); diff --git a/cfg/builder/builder_entry.cc b/cfg/builder/builder_entry.cc index 6cad14a713..5497f7858a 100644 --- a/cfg/builder/builder_entry.cc +++ b/cfg/builder/builder_entry.cc @@ -1,6 +1,6 @@ #include "ast/Helpers.h" #include "cfg/builder/builder.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "core/Names.h" using namespace std; diff --git a/cfg/builder/builder_finalize.cc b/cfg/builder/builder_finalize.cc index ff0b4e3be7..468c54628b 100644 --- a/cfg/builder/builder_finalize.cc +++ b/cfg/builder/builder_finalize.cc @@ -1,7 +1,7 @@ #include "cfg/builder/builder.h" -#include "common/Timer.h" #include "common/UIntSetForEach.h" -#include "common/sort.h" +#include "common/sort/sort.h" +#include "common/timers/Timer.h" #include "core/Names.h" #include // sort, remove, unique diff --git a/cfg/builder/builder_walk.cc b/cfg/builder/builder_walk.cc index 5089168c3e..e2ad3cfc5f 100644 --- a/cfg/builder/builder_walk.cc +++ b/cfg/builder/builder_walk.cc @@ -212,9 +212,9 @@ BasicBlock *CFGBuilder::walkHash(CFGContext cctx, ast::Hash &h, BasicBlock *curr synthesizeExpr(current, magic, core::LocOffsets::none(), make_insn(core::Symbols::Magic())); auto isPrivateOk = false; - current->exprs.emplace_back( - cctx.target, h.loc, - make_insn(magic, h.loc, method, core::LocOffsets::none(), vars.size(), vars, locs, isPrivateOk)); + current->exprs.emplace_back(cctx.target, h.loc, + make_insn(magic, h.loc, method, core::LocOffsets::none(), vars.size(), vars, + std::move(locs), isPrivateOk)); return current; } @@ -581,8 +581,8 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo argFlags.emplace_back(e.flags); } auto link = make_shared(s.fun, move(argFlags), newRubyRegionId); - auto send = make_insn(recv, s.recv.loc(), s.fun, s.funLoc, s.numPosArgs(), args, argLocs, - !!s.flags.isPrivateOk, link); + auto send = make_insn(recv, s.recv.loc(), s.fun, s.funLoc, s.numPosArgs(), args, + std::move(argLocs), !!s.flags.isPrivateOk, link); LocalRef sendTemp = cctx.newTemporary(core::Names::blockPreCallTemp()); auto solveConstraint = make_insn(link, sendTemp); current->exprs.emplace_back(LocalOccurrence::synthetic(sendTemp), s.loc, move(send)); @@ -598,49 +598,52 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo auto bodyLoops = cctx.loops + 1; auto bodyBlock = cctx.inWhat.freshBlock(bodyLoops, newRubyRegionId); - auto argTemp = cctx.newTemporaryOccurrence(core::Names::blkArg()); bodyBlock->exprs.emplace_back(LocalOccurrence::synthetic(LocalRef::selfVariable()), s.loc, make_insn(link, LocalRef::selfVariable())); - bodyBlock->exprs.emplace_back(argTemp, s.block()->loc, make_insn(link)); auto *argBlock = bodyBlock; - for (int i = 0; i < blockArgFlags.size(); ++i) { - auto &arg = blockArgFlags[i]; - auto argLoc = LocalOccurrence{cctx.inWhat.enterLocal(arg.local), arg.loc}; - - if (arg.flags.isRepeated) { - // Mixing positional and rest args in blocks is - // not currently supported, but we'll handle that in - // inference. - argBlock->exprs.emplace_back(argLoc, arg.loc, - make_insn(i, arg.flags, argTemp)); - continue; - } + if (!blockArgFlags.empty()) { + auto argTemp = LocalOccurrence::synthetic(cctx.newTemporary(core::Names::blkArg())); + bodyBlock->exprs.emplace_back(argTemp, s.block()->loc, make_insn(link)); + + for (int i = 0; i < blockArgFlags.size(); ++i) { + auto &arg = blockArgFlags[i]; + auto argLoc = LocalOccurrence{cctx.inWhat.enterLocal(arg.local), arg.loc}; + + if (arg.flags.isRepeated) { + // Mixing positional and rest args in blocks is + // not currently supported, but we'll handle that in + // inference. + argBlock->exprs.emplace_back(argLoc, arg.loc, + make_insn(i, arg.flags, argTemp)); + continue; + } - if (auto *opt = ast::cast_tree(blockArgs[i])) { - auto *presentBlock = cctx.inWhat.freshBlock(bodyLoops, newRubyRegionId); - auto *missingBlock = cctx.inWhat.freshBlock(bodyLoops, newRubyRegionId); + if (auto *opt = ast::cast_tree(blockArgs[i])) { + auto *presentBlock = cctx.inWhat.freshBlock(bodyLoops, newRubyRegionId); + auto *missingBlock = cctx.inWhat.freshBlock(bodyLoops, newRubyRegionId); - // add a test for YieldParamPresent - auto present = cctx.newTemporary(core::Names::argPresent()); - synthesizeExpr(argBlock, present, arg.loc, - make_insn(static_cast(i))); - conditionalJump(argBlock, present, presentBlock, missingBlock, cctx.inWhat, arg.loc); + // add a test for YieldParamPresent + auto present = cctx.newTemporary(core::Names::argPresent()); + synthesizeExpr(argBlock, present, arg.loc, + make_insn(static_cast(i))); + conditionalJump(argBlock, present, presentBlock, missingBlock, cctx.inWhat, arg.loc); - // make a new block for the present and missing blocks to join - argBlock = cctx.inWhat.freshBlock(bodyLoops, newRubyRegionId); + // make a new block for the present and missing blocks to join + argBlock = cctx.inWhat.freshBlock(bodyLoops, newRubyRegionId); - // compile the argument fetch in the present block - presentBlock->exprs.emplace_back(argLoc, arg.loc, - make_insn(i, arg.flags, argTemp)); - unconditionalJump(presentBlock, argBlock, cctx.inWhat, arg.loc); + // compile the argument fetch in the present block + presentBlock->exprs.emplace_back(argLoc, arg.loc, + make_insn(i, arg.flags, argTemp)); + unconditionalJump(presentBlock, argBlock, cctx.inWhat, arg.loc); - // compile the default expr in `missingBlock` - auto *missingLast = walk(cctx.withTarget(argLoc), opt->default_, missingBlock); - unconditionalJump(missingLast, argBlock, cctx.inWhat, arg.loc); - } else { - argBlock->exprs.emplace_back(argLoc, arg.loc, - make_insn(i, arg.flags, argTemp)); + // compile the default expr in `missingBlock` + auto *missingLast = walk(cctx.withTarget(argLoc), opt->default_, missingBlock); + unconditionalJump(missingLast, argBlock, cctx.inWhat, arg.loc); + } else { + argBlock->exprs.emplace_back(argLoc, arg.loc, + make_insn(i, arg.flags, argTemp)); + } } } @@ -657,7 +660,23 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo s.block()->body, argBlock); if (blockLast != cctx.inWhat.deadBlock()) { LocalRef dead = cctx.newTemporary(core::Names::blockReturnTemp()); - synthesizeExpr(blockLast, dead, s.block()->loc, make_insn(link, blockrv)); + + core::LocOffsets blockReturnLoc = s.block()->loc; + if (blockLast->exprs.empty() || isa_instruction(blockLast->exprs.back().value) || + isa_instruction(blockLast->exprs.back().value)) { + auto blockEndPos = blockReturnLoc.copyEndWithZeroLength(); + auto endKwLoc = cctx.ctx.locAt(blockEndPos).adjustLen(cctx.ctx, -3, 3); + auto endBraceLoc = cctx.ctx.locAt(blockEndPos).adjustLen(cctx.ctx, -1, 1); + if (endKwLoc.source(cctx.ctx) == "end") { + blockReturnLoc = endKwLoc.offsets(); + } else if (endBraceLoc.source(cctx.ctx) == "}") { + blockReturnLoc = endBraceLoc.offsets(); + } + } else { + blockReturnLoc = blockLast->exprs.back().loc; + } + + synthesizeExpr(blockLast, dead, blockReturnLoc, make_insn(link, blockrv)); } unconditionalJump(blockLast, headerBlock, cctx.inWhat, s.loc); @@ -689,7 +708,7 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo } else { current->exprs.emplace_back(cctx.target, s.loc, make_insn(recv, s.recv.loc(), s.fun, s.funLoc, s.numPosArgs(), - args, argLocs, !!s.flags.isPrivateOk)); + args, std::move(argLocs), !!s.flags.isPrivateOk)); } ret = current; @@ -757,7 +776,8 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo // that the break unwinds through. synthesizeExpr(afterBreak, ignored, core::LocOffsets::none(), make_insn(magic, core::LocOffsets::none(), core::Names::blockBreak(), - core::LocOffsets::none(), args.size(), args, locs, isPrivateOk)); + core::LocOffsets::none(), args.size(), args, std::move(locs), + isPrivateOk)); } afterBreak->exprs.emplace_back(LocalOccurrence::synthetic(cctx.blockBreakTarget), a.loc, @@ -791,7 +811,7 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo auto isPrivateOk = false; synthesizeExpr(current, retryTemp, core::LocOffsets::none(), make_insn(magic, what.loc(), core::Names::retry(), core::LocOffsets::none(), - args.size(), args, argLocs, isPrivateOk)); + args.size(), args, std::move(argLocs), isPrivateOk)); unconditionalJump(current, cctx.rescueScope, cctx.inWhat, a.loc); } ret = cctx.inWhat.deadBlock(); @@ -819,16 +839,29 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo auto rescueHandlersBlock = cctx.inWhat.freshBlock(cctx.loops, handlersRubyRegionId); auto bodyBlock = cctx.inWhat.freshBlock(cctx.loops, bodyRubyRegionId); auto exceptionValue = cctx.newTemporary(core::Names::exceptionValue()); - synthesizeExpr(rescueHeaderBlock, exceptionValue, what.loc(), make_insn()); - conditionalJump(rescueHeaderBlock, exceptionValue, rescueHandlersBlock, bodyBlock, cctx.inWhat, a.loc); + // In `rescue; ...; end`, we don't want the conditional jumps' variables nor the + // GetCurrentException calls to look like they blame to the whole `rescue; ...; end` + // body. Better to just point at the `rescue` keyword. + // + // Might be better to point at exception variable like the "e" in `rescue => e`, but + // that involves picking one (of possibly many) rescueCases. + auto rescueKeywordLoc = (!a.rescueCases.empty() && (a.rescueCases.front().loc().endPos() - + a.rescueCases.front().loc().beginPos()) > 6) + ? core::LocOffsets{a.rescueCases.front().loc().beginPos(), + a.rescueCases.front().loc().beginPos() + 6} + : a.loc.copyWithZeroLength(); + synthesizeExpr(rescueHeaderBlock, exceptionValue, rescueKeywordLoc, make_insn()); + conditionalJump(rescueHeaderBlock, exceptionValue, rescueHandlersBlock, bodyBlock, cctx.inWhat, + rescueKeywordLoc); // cctx.loops += 1; // should formally be here but this makes us report a lot of false errors bodyBlock = walk(cctx, a.body, bodyBlock); // else is only executed if body didn't raise an exception auto elseBody = cctx.inWhat.freshBlock(cctx.loops, elseRubyRegionId); - synthesizeExpr(bodyBlock, exceptionValue, what.loc(), make_insn()); - conditionalJump(bodyBlock, exceptionValue, rescueHandlersBlock, elseBody, cctx.inWhat, a.loc); + synthesizeExpr(bodyBlock, exceptionValue, rescueKeywordLoc, make_insn()); + conditionalJump(bodyBlock, exceptionValue, rescueHandlersBlock, elseBody, cctx.inWhat, + rescueKeywordLoc); elseBody = walk(cctx, a.else_, elseBody); auto ensureBody = cctx.inWhat.freshBlock(cctx.loops, ensureRubyRegionId); @@ -859,7 +892,8 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo auto argLocs = {what.loc()}; synthesizeExpr(caseBody, res, rescueCase->loc, make_insn(magic, rescueCase->loc, core::Names::keepForCfg(), - core::LocOffsets::none(), args.size(), args, argLocs, isPrivateOk)); + core::LocOffsets::none(), args.size(), args, std::move(argLocs), + isPrivateOk)); if (exceptions.empty()) { // rescue without a class catches StandardError @@ -876,13 +910,13 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo auto isaCheck = cctx.newTemporary(core::Names::isaCheckTemp()); InlinedVector args; InlinedVector argLocs = {loc}; - args.emplace_back(exceptionClass); + args.emplace_back(localVar); auto isPrivateOk = false; - rescueHandlersBlock->exprs.emplace_back(LocalOccurrence::synthetic(isaCheck), loc, - make_insn(localVar, loc, core::Names::isA_p(), - core::LocOffsets::none(), args.size(), - args, argLocs, isPrivateOk)); + rescueHandlersBlock->exprs.emplace_back( + LocalOccurrence::synthetic(isaCheck), loc, + make_insn(exceptionClass, loc, core::Names::tripleEq(), loc.copyWithZeroLength(), + args.size(), args, std::move(argLocs), isPrivateOk)); auto otherHandlerBlock = cctx.inWhat.freshBlock(cctx.loops, handlersRubyRegionId); conditionalJump(rescueHandlersBlock, isaCheck, caseBody, otherHandlerBlock, cctx.inWhat, loc); @@ -925,8 +959,8 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo auto isPrivateOk = false; current->exprs.emplace_back(cctx.target, a.loc, make_insn(magic, a.loc, core::Names::buildArray(), - core::LocOffsets::none(), vars.size(), vars, locs, - isPrivateOk)); + core::LocOffsets::none(), vars.size(), vars, + std::move(locs), isPrivateOk)); ret = current; }, diff --git a/common/BUILD b/common/BUILD index 6f1ae92d06..d40a949bbf 100644 --- a/common/BUILD +++ b/common/BUILD @@ -19,7 +19,7 @@ cc_library( "typecase.h", ], linkopts = select({ - "//tools/config:linux": ["-lm"], + "@platforms//os:linux": ["-lm"], "//conditions:default": [], }), linkstatic = select({ @@ -29,9 +29,13 @@ cc_library( visibility = ["//visibility:public"], deps = [ "//common/concurrency", + "//common/counters", "//common/enforce_no_timer", "//common/exception", "//common/os", + "//common/sort", + "//common/strings", + "//common/timers", "//sorbet_version", "//third_party/progressbar", "@com_google_absl//absl/algorithm:container", @@ -41,7 +45,6 @@ cc_library( "@com_google_absl//absl/debugging:symbolize", "@com_google_absl//absl/strings", "@com_google_absl//absl/types:span", - "@pdqsort", "@spdlog", ], ) @@ -57,7 +60,7 @@ cc_test( visibility = ["//tools:__pkg__"], deps = [ ":common", - "@doctest", - "@doctest//:doctest_main", + "@doctest//doctest", + "@doctest//doctest:main", ], ) diff --git a/common/common.cc b/common/common.cc index 3f50f6da3d..816160340a 100644 --- a/common/common.cc +++ b/common/common.cc @@ -3,7 +3,7 @@ #include "common/concurrency/ConcurrentQueue.h" #include "common/concurrency/WorkerPool.h" #include "common/exception/Exception.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "os/os.h" #include "spdlog/sinks/stdout_color_sinks.h" #include @@ -50,11 +50,13 @@ string sorbet::FileOps::read(const string &filename) { fclose(fp); if (readBytes != contents.size()) { // Error reading file? - throw sorbet::FileNotFoundException(fmt::format("Error reading file: `{}`: {}", filename, errno)); + auto msg = fmt::format("Error reading file: `{}`: {}", filename, errno); + throw sorbet::FileNotFoundException(msg); } return contents; } - throw sorbet::FileNotFoundException(fmt::format("Cannot open file `{}`", filename)); + auto msg = fmt::format("Cannot open file `{}`", filename); + throw sorbet::FileNotFoundException(msg); } void sorbet::FileOps::write(const string &filename, const vector &data) { @@ -64,7 +66,8 @@ void sorbet::FileOps::write(const string &filename, const vector &data) fclose(fp); return; } - throw sorbet::FileNotFoundException(fmt::format("Cannot open file `{}` for writing", filename)); + auto msg = fmt::format("Cannot open file `{}` for writing", filename); + throw sorbet::FileNotFoundException(msg); } bool sorbet::FileOps::dirExists(const string &path) { @@ -75,7 +78,8 @@ bool sorbet::FileOps::dirExists(const string &path) { void sorbet::FileOps::createDir(const string &path) { auto err = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); if (err) { - throw sorbet::CreateDirException(fmt::format("Error in createDir('{}'): {}", path, errno)); + auto msg = fmt::format("Error in createDir('{}'): {}", path, errno); + throw sorbet::CreateDirException(msg); } } @@ -86,7 +90,8 @@ bool sorbet::FileOps::ensureDir(const string &path) { return false; } - throw sorbet::CreateDirException(fmt::format("Error in createDir('{}'): {}", path, errno)); + auto msg = fmt::format("Error in createDir('{}'): {}", path, errno); + throw sorbet::CreateDirException(msg); } return true; @@ -99,7 +104,8 @@ std::string sorbet::FileOps::getCurrentDir() { void sorbet::FileOps::removeDir(const string &path) { auto err = rmdir(path.c_str()); if (err) { - throw sorbet::CreateDirException(fmt::format("Error in removeDir('{}'): {}", path, errno)); + auto msg = fmt::format("Error in removeDir('{}'): {}", path, errno); + throw sorbet::RemoveDirException(msg); } } @@ -109,7 +115,8 @@ bool sorbet::FileOps::removeEmptyDir(const string &path) { if (errno == ENOTEMPTY) { return false; } - throw sorbet::CreateDirException(fmt::format("Error in removeEmptyDir('{}'): {}", path, errno)); + auto msg = fmt::format("Error in removeEmptyDir('{}'): {}", path, errno); + throw sorbet::RemoveDirException(msg); } return true; @@ -118,7 +125,8 @@ bool sorbet::FileOps::removeEmptyDir(const string &path) { void sorbet::FileOps::removeFile(const string &path) { auto err = remove(path.c_str()); if (err) { - throw sorbet::RemoveFileException(fmt::format("Error in removeFile('{}'): {}", path, errno)); + auto msg = fmt::format("Error in removeFile('{}'): {}", path, errno); + throw sorbet::RemoveFileException(msg); } } @@ -129,7 +137,8 @@ void sorbet::FileOps::write(const string &filename, string_view text) { fclose(fp); return; } - throw sorbet::FileNotFoundException(fmt::format("Cannot open file `{}` for writing", filename)); + auto msg = fmt::format("Cannot open file `{}` for writing", filename); + throw sorbet::FileNotFoundException(msg); } bool sorbet::FileOps::writeIfDifferent(const string &filename, string_view text) { @@ -147,7 +156,8 @@ void sorbet::FileOps::append(const string &filename, string_view text) { fclose(fp); return; } - throw sorbet::FileNotFoundException(fmt::format("Cannot open file `{}` for writing", filename)); + auto msg = fmt::format("Cannot open file `{}` for writing", filename); + throw sorbet::FileNotFoundException(msg); } string_view sorbet::FileOps::getFileName(string_view path) { @@ -332,7 +342,8 @@ void appendFilesInDir(string_view basePath, const string &path, const sorbet::Un } default: // Mirrors other FileOps functions: Assume other errors are from FileNotFound. - throw sorbet::FileNotFoundException(fmt::format("Couldn't open directory `{}`", path)); + auto msg = fmt::format("Couldn't open directory `{}`", path); + throw sorbet::FileNotFoundException(msg); } } diff --git a/common/common.h b/common/common.h index cf9fc0f1e0..e2973e64ce 100644 --- a/common/common.h +++ b/common/common.h @@ -65,6 +65,11 @@ constexpr bool skip_slow_enforce = false; X; \ } +#define SLOW_DEBUG_ONLY(X) \ + if constexpr (!::sorbet::skip_slow_enforce && debug_mode) { \ + X; \ + } + constexpr bool skip_check_memory_layout = debug_mode || emscripten_build; template struct check_size { @@ -153,7 +158,7 @@ std::string demangle(const char *mangled); #pragma GCC poison cuserid #pragma GCC poison rexec rexec_af -#include "Timer.h" #include "enforce_no_timer/EnforceNoTimer.h" #include "exception/Exception.h" +#include "timers/Timer.h" #endif diff --git a/common/concurrency/BUILD b/common/concurrency/BUILD index f5f3c7acef..427782db08 100644 --- a/common/concurrency/BUILD +++ b/common/concurrency/BUILD @@ -14,7 +14,7 @@ cc_library( "WorkerPool.h", ], linkopts = select({ - "//tools/config:linux": ["-lm"], + "@platforms//os:linux": ["-lm"], "//conditions:default": [], }), linkstatic = select({ diff --git a/common/concurrency/ConcurrentQueue.h b/common/concurrency/ConcurrentQueue.h index a230de6964..8231526c5b 100644 --- a/common/concurrency/ConcurrentQueue.h +++ b/common/concurrency/ConcurrentQueue.h @@ -9,8 +9,8 @@ */ #include "blockingconcurrentqueue.h" -#include "common/Timer.h" #include "common/common.h" +#include "common/timers/Timer.h" #include #include #include diff --git a/common/counters/BUILD b/common/counters/BUILD new file mode 100644 index 0000000000..465c602a27 --- /dev/null +++ b/common/counters/BUILD @@ -0,0 +1,33 @@ +cc_library( + name = "counters", + srcs = glob( + [ + "*.cc", + "*.h", + ], + # workaround https://github.com/flycheck/flycheck/issues/248 in emacs + exclude = ["flycheck_*"], + ), + hdrs = [ + "Counters.h", + "Counters_impl.h", + ], + linkopts = select({ + "@platforms//os:linux": ["-lm"], + "//conditions:default": [], + }), + linkstatic = select({ + "//tools/config:linkshared": 0, + "//conditions:default": 1, + }), + visibility = ["//visibility:public"], + deps = [ + "//common/exception", + "//common/sort", + "//common/strings", + "//sorbet_version", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/strings", + "@spdlog", + ], +) diff --git a/common/Counters.cc b/common/counters/Counters.cc similarity index 97% rename from common/Counters.cc rename to common/counters/Counters.cc index 0e5931c551..7ad20c569b 100644 --- a/common/Counters.cc +++ b/common/counters/Counters.cc @@ -1,8 +1,9 @@ -#include "common/Counters.h" +#include "common/counters/Counters.h" #include "absl/strings/str_cat.h" -#include "common/Counters_impl.h" -#include "common/formatting.h" -#include "common/sort.h" +#include "common/counters/Counters_impl.h" +#include "common/exception/Exception.h" +#include "common/sort/sort.h" +#include "common/strings/formatting.h" #include #include #include // set @@ -114,9 +115,9 @@ void CounterImpl::clear() { this->countersByCategory.clear(); } -UnorderedMap getAndClearHistogram(ConstExprStr histogram) { +absl::flat_hash_map getAndClearHistogram(ConstExprStr histogram) { counterState.canonicalize(); - UnorderedMap ret; + absl::flat_hash_map ret; auto fnd = counterState.histograms.find(counterState.internKey(histogram.str)); if (fnd != counterState.histograms.end()) { for (auto e : fnd->second) { @@ -391,7 +392,7 @@ string getCounterStatistics() { { fmt::format_to(std::back_inserter(buf), "Timings: \n"); vector> sortedTimings; - UnorderedMap> timings; + absl::flat_hash_map> timings; for (const auto &e : counterState.timings) { int64_t durationMs = (e.end.usec - e.start.usec) / 1'000; timings[e.measure].emplace_back(durationMs); diff --git a/common/Counters.h b/common/counters/Counters.h similarity index 99% rename from common/Counters.h rename to common/counters/Counters.h index 1c8ddfaca8..6d61b2f3c5 100644 --- a/common/Counters.h +++ b/common/counters/Counters.h @@ -1,7 +1,7 @@ #ifndef SORBET_COUNTERS_H #define SORBET_COUNTERS_H #include "absl/container/flat_hash_map.h" -#include "common/ConstExprStr.h" +#include "common/strings/ConstExprStr.h" #include "sorbet_version/sorbet_version.h" #include "spdlog/spdlog.h" #include diff --git a/common/Counters_impl.h b/common/counters/Counters_impl.h similarity index 80% rename from common/Counters_impl.h rename to common/counters/Counters_impl.h index b1b1dbd5cd..1dd6abf705 100644 --- a/common/Counters_impl.h +++ b/common/counters/Counters_impl.h @@ -1,7 +1,7 @@ #ifndef SORBET_COUNTERS_IMPL_H #define SORBET_COUNTERS_IMPL_H -#include "common/common.h" +#include "common/counters/Counters.h" #include namespace sorbet { @@ -29,8 +29,8 @@ struct CounterImpl { // std::string_view isn't hashable, so we use an unordered map. We could // implement hash ourselves, but this is the slowpath anyways. - UnorderedMap strings_by_value; - UnorderedMap stringsByPtr; + absl::flat_hash_map strings_by_value; + absl::flat_hash_map stringsByPtr; struct Timing { // see https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit // and https://docs.google.com/document/d/1La_0PPfsTqHJihazYhff96thhjPtvq1KjAUOJu0dvEg/edit @@ -44,10 +44,10 @@ struct CounterImpl { FlowId prev; }; void timingAdd(Timing timing); - UnorderedMap> histograms; - UnorderedMap counters; + absl::flat_hash_map> histograms; + absl::flat_hash_map counters; std::vector timings; - UnorderedMap> countersByCategory; + absl::flat_hash_map> countersByCategory; }; } // namespace sorbet diff --git a/common/crypto_hashing/BUILD b/common/crypto_hashing/BUILD index 0c27a10d00..0e7670aef4 100644 --- a/common/crypto_hashing/BUILD +++ b/common/crypto_hashing/BUILD @@ -8,8 +8,8 @@ cc_library( visibility = ["//visibility:public"], deps = ["//common"] + select({ "//tools/config:webasm": ["@com_github_blake2_blake2"], - "//tools/config:darwin": ["@com_github_blake2_libb2"], - "//tools/config:linux": ["@com_github_blake2_libb2"], + "//tools/config:darwin_x86_64": ["@com_github_blake2_libb2"], + "//tools/config:linux_x86_64": ["@com_github_blake2_libb2"], "//conditions:default": ["@com_github_blake2_blake2"], }), ) diff --git a/common/exception/Exception.h b/common/exception/Exception.h index 34cfed24a1..12a1aa3a7a 100644 --- a/common/exception/Exception.h +++ b/common/exception/Exception.h @@ -31,6 +31,11 @@ class CreateDirException : SorbetException { CreateDirException(const std::string &message) : SorbetException(message) {} }; +class RemoveDirException : SorbetException { +public: + RemoveDirException(const std::string &message) : SorbetException(message) {} +}; + class RemoveFileException : SorbetException { public: RemoveFileException(const std::string &message) : SorbetException(message) {} diff --git a/common/json2msgpack/BUILD b/common/json2msgpack/BUILD index 550207f231..0730d5fbb5 100644 --- a/common/json2msgpack/BUILD +++ b/common/json2msgpack/BUILD @@ -13,7 +13,7 @@ cc_library( "json2msgpack.h", ], linkopts = select({ - "//tools/config:linux": ["-lm"], + "@platforms//os:linux": ["-lm"], "//conditions:default": [], }), linkstatic = select({ diff --git a/common/kvstore/BUILD b/common/kvstore/BUILD index d534414fb0..11404227ca 100644 --- a/common/kvstore/BUILD +++ b/common/kvstore/BUILD @@ -8,7 +8,7 @@ cc_library( "KeyValueStore.h", ], linkopts = select({ - "//tools/config:linux": ["-lm"], + "@platforms//os:linux": ["-lm"], "//conditions:default": [], }), linkstatic = select({ @@ -39,7 +39,7 @@ cc_test( visibility = ["//tools:__pkg__"], deps = [ "kvstore", - "@doctest", - "@doctest//:doctest_main", + "@doctest//doctest", + "@doctest//doctest:main", ], ) diff --git a/common/kvstore/KeyValueStore.cc b/common/kvstore/KeyValueStore.cc index 03ac809f94..928659a41b 100644 --- a/common/kvstore/KeyValueStore.cc +++ b/common/kvstore/KeyValueStore.cc @@ -1,7 +1,7 @@ #include "common/kvstore/KeyValueStore.h" #include "common/EarlyReturnWithCode.h" -#include "common/Timer.h" -#include "common/formatting.h" +#include "common/strings/formatting.h" +#include "common/timers/Timer.h" #include "lmdb.h" #include "spdlog/spdlog.h" diff --git a/common/kvstore/test/kvstore_test.cc b/common/kvstore/test/kvstore_test.cc index db1330d5cc..56ab0f4a96 100644 --- a/common/kvstore/test/kvstore_test.cc +++ b/common/kvstore/test/kvstore_test.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" // has to go first as it violates our requirements #include "spdlog/spdlog.h" // has to go above stdout_sinks.h; this comment prevents reordering. diff --git a/common/os/BUILD b/common/os/BUILD index a0ccbcd236..5c3af96263 100644 --- a/common/os/BUILD +++ b/common/os/BUILD @@ -4,7 +4,7 @@ cc_library( "os.h", "os.cc", ] + select({ - "//tools/config:darwin": ["mac.cc"], + "@platforms//os:osx": ["mac.cc"], "//tools/config:webasm": ["emscripten.cc"], "//conditions:default": ["linux.cc"], }), diff --git a/common/sort/BUILD b/common/sort/BUILD new file mode 100644 index 0000000000..5db7bcf763 --- /dev/null +++ b/common/sort/BUILD @@ -0,0 +1,26 @@ +cc_library( + name = "sort", + srcs = glob( + [ + "*.cc", + "*.h", + ], + # workaround https://github.com/flycheck/flycheck/issues/248 in emacs + exclude = ["flycheck_*"], + ), + hdrs = [ + "sort.h", + ], + linkopts = select({ + "@platforms//os:linux": ["-lm"], + "//conditions:default": [], + }), + linkstatic = select({ + "//tools/config:linkshared": 0, + "//conditions:default": 1, + }), + visibility = ["//visibility:public"], + deps = [ + "@pdqsort", + ], +) diff --git a/common/sort.h b/common/sort/sort.h similarity index 100% rename from common/sort.h rename to common/sort/sort.h diff --git a/common/statsd/statsd.cc b/common/statsd/statsd.cc index 398b8e52c6..c44e0570c0 100644 --- a/common/statsd/statsd.cc +++ b/common/statsd/statsd.cc @@ -1,6 +1,6 @@ #include "common/statsd/statsd.h" -#include "common/Counters_impl.h" -#include "common/formatting.h" +#include "common/counters/Counters_impl.h" +#include "common/strings/formatting.h" #include "sorbet_version/sorbet_version.h" extern "C" { @@ -108,6 +108,9 @@ bool StatsD::submitCounters(const CounterState &counters, string_view host, int } for (auto &hist : counters.counters->histograms) { + if (std::find(ignoredHistograms.begin(), ignoredHistograms.end(), hist.first) != ignoredHistograms.end()) { + continue; + } CounterImpl::CounterType sum = 0; for (auto &e : hist.second) { sum += e.second; diff --git a/common/statsd/statsd.h b/common/statsd/statsd.h index e01d5acac9..12604854e6 100644 --- a/common/statsd/statsd.h +++ b/common/statsd/statsd.h @@ -9,6 +9,10 @@ class StatsD { public: StatsD() = delete; + // these are histograms which we do not want to report to statsd/SignalFX under any + // circumstances. + static constexpr std::array ignoredHistograms{{"untyped.usages", "untyped.blames"}}; + /** Adds standard process and sorbet-related metrics (RSS, faults, Sorbet version, etc). */ static void addStandardMetrics(); static bool submitCounters(const CounterState &counters, std::string_view host, int port, std::string_view prefix); diff --git a/common/strings/BUILD b/common/strings/BUILD new file mode 100644 index 0000000000..a1f24921e5 --- /dev/null +++ b/common/strings/BUILD @@ -0,0 +1,25 @@ +cc_library( + name = "strings", + srcs = glob( + [ + "*.cc", + "*.h", + ], + # workaround https://github.com/flycheck/flycheck/issues/248 in emacs + exclude = ["flycheck_*"], + ), + hdrs = [ + "ConstExprStr.h", + "formatting.h", + ], + linkopts = select({ + "@platforms//os:linux": ["-lm"], + "//conditions:default": [], + }), + linkstatic = select({ + "//tools/config:linkshared": 0, + "//conditions:default": 1, + }), + visibility = ["//visibility:public"], + deps = [], +) diff --git a/common/ConstExprStr.h b/common/strings/ConstExprStr.h similarity index 100% rename from common/ConstExprStr.h rename to common/strings/ConstExprStr.h diff --git a/common/formatting.h b/common/strings/formatting.h similarity index 98% rename from common/formatting.h rename to common/strings/formatting.h index f0826247a0..40c7dc527a 100644 --- a/common/formatting.h +++ b/common/strings/formatting.h @@ -1,7 +1,6 @@ #ifndef SORBET_COMMON_FORMATTING_HPP #define SORBET_COMMON_FORMATTING_HPP -#include "common/common.h" #include "spdlog/fmt/fmt.h" namespace fmt { diff --git a/common/test/common_test.cc b/common/test/common_test.cc index ac1326b249..2b39b3df9e 100644 --- a/common/test/common_test.cc +++ b/common/test/common_test.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" // violates our requirements, thus has to go first #include "common/FileOps.h" #include "common/Levenstein.h" diff --git a/common/timers/BUILD b/common/timers/BUILD new file mode 100644 index 0000000000..5cf70191f6 --- /dev/null +++ b/common/timers/BUILD @@ -0,0 +1,26 @@ +cc_library( + name = "timers", + srcs = glob( + [ + "*.cc", + "*.h", + ], + # workaround https://github.com/flycheck/flycheck/issues/248 in emacs + exclude = ["flycheck_*"], + ), + hdrs = [ + "Timer.h", + ], + linkopts = select({ + "@platforms//os:linux": ["-lm"], + "//conditions:default": [], + }), + linkstatic = select({ + "//tools/config:linkshared": 0, + "//conditions:default": 1, + }), + visibility = ["//visibility:public"], + deps = [ + "//common/counters", + ], +) diff --git a/common/Timer.cc b/common/timers/Timer.cc similarity index 99% rename from common/Timer.cc rename to common/timers/Timer.cc index e09a596072..cde11a3405 100644 --- a/common/Timer.cc +++ b/common/timers/Timer.cc @@ -1,4 +1,4 @@ -#include "common/Timer.h" +#include "common/timers/Timer.h" using namespace std; namespace sorbet { diff --git a/common/Timer.h b/common/timers/Timer.h similarity index 98% rename from common/Timer.h rename to common/timers/Timer.h index 19266dd70e..b4d9a2dc68 100644 --- a/common/Timer.h +++ b/common/timers/Timer.h @@ -1,6 +1,6 @@ #ifndef SORBET_TIMER_H #define SORBET_TIMER_H -#include "common/Counters.h" +#include "common/counters/Counters.h" #include #include #include diff --git a/common/web_tracer_framework/tracing.cc b/common/web_tracer_framework/tracing.cc index f3da9b3179..1cbcc6805e 100644 --- a/common/web_tracer_framework/tracing.cc +++ b/common/web_tracer_framework/tracing.cc @@ -1,11 +1,11 @@ -#include "common/Counters.h" #include "common/FileOps.h" +#include "common/counters/Counters.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_replace.h" -#include "common/Counters_impl.h" #include "common/JSON.h" -#include "common/formatting.h" +#include "common/counters/Counters_impl.h" +#include "common/strings/formatting.h" #include "common/web_tracer_framework/tracing.h" #include "rapidjson/writer.h" #include "sorbet_version/sorbet_version.h" diff --git a/compiler/Core/CompilerState.cc b/compiler/Core/CompilerState.cc index faf45c99b0..59786e55d9 100644 --- a/compiler/Core/CompilerState.cc +++ b/compiler/Core/CompilerState.cc @@ -7,8 +7,8 @@ #include "llvm/Transforms/IPO.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" // ^^^ violate poisons -#include "common/formatting.h" -#include "common/sort.h" +#include "common/sort/sort.h" +#include "common/strings/formatting.h" #include "compiler/Core/AbortCompilation.h" #include "compiler/Core/CompilerState.h" #include "compiler/Errors/Errors.h" diff --git a/compiler/IREmitter/IREmitter.cc b/compiler/IREmitter/IREmitter.cc index 0ee558b1e1..629b2a565e 100644 --- a/compiler/IREmitter/IREmitter.cc +++ b/compiler/IREmitter/IREmitter.cc @@ -10,8 +10,8 @@ #include "ast/ast.h" #include "cfg/CFG.h" #include "common/FileOps.h" -#include "common/Timer.h" -#include "common/sort.h" +#include "common/sort/sort.h" +#include "common/timers/Timer.h" #include "common/typecase.h" #include "compiler/Core/CompilerState.h" #include "compiler/Core/FailCompilation.h" @@ -780,14 +780,16 @@ void emitUserBody(CompilerState &base, cfg::CFG &cfg, const IREmitterContext &ir // These instructions only exist in the CFG for the purpose of type checking. // The Ruby VM already checks that self is a valid type when calling `.bind()` // on an UnboundMethod object. - auto skipTypeTest = bind.bind.variable.data(cfg) == core::LocalVariable::selfVariable(); + auto skipTypeTest = bind.bind.variable.data(cfg) == core::LocalVariable::selfVariable() || + i.cast == core::Names::assumeType(); if (!skipTypeTest) { IREmitterHelpers::emitTypeTest(cs, builder, val, bind.bind.type, fmt::format("T.{}", i.cast.shortName(cs))); } - if (i.cast == core::Names::let() || i.cast == core::Names::cast()) { + if (i.cast == core::Names::let() || i.cast == core::Names::cast() || + i.cast == core::Names::assumeType()) { Payload::varSet(cs, bind.bind.variable, val, builder, irctx, bb->rubyRegionId); } else if (i.cast == core::Names::assertType()) { Payload::varSet(cs, bind.bind.variable, Payload::rubyFalse(cs, builder), builder, irctx, diff --git a/compiler/IREmitter/IREmitterContext.cc b/compiler/IREmitter/IREmitterContext.cc index 16454cd43c..5d3ed9a379 100644 --- a/compiler/IREmitter/IREmitterContext.cc +++ b/compiler/IREmitter/IREmitterContext.cc @@ -10,7 +10,7 @@ #include "ast/Helpers.h" #include "ast/ast.h" #include "cfg/CFG.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "compiler/Core/CompilerState.h" #include "compiler/IREmitter/CFGHelpers.h" #include "compiler/IREmitter/IREmitterContext.h" diff --git a/compiler/IREmitter/IREmitterHelpers.cc b/compiler/IREmitter/IREmitterHelpers.cc index 5b456ad8ce..ecbfe69cfa 100644 --- a/compiler/IREmitter/IREmitterHelpers.cc +++ b/compiler/IREmitter/IREmitterHelpers.cc @@ -11,7 +11,7 @@ #include "ast/Helpers.h" #include "ast/ast.h" #include "cfg/CFG.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "compiler/Core/CompilerState.h" #include "compiler/IREmitter/IREmitterContext.h" #include "compiler/IREmitter/IREmitterHelpers.h" diff --git a/compiler/IREmitter/NameBasedIntrinsics.cc b/compiler/IREmitter/NameBasedIntrinsics.cc index 2d18b68636..d554a6e9a5 100644 --- a/compiler/IREmitter/NameBasedIntrinsics.cc +++ b/compiler/IREmitter/NameBasedIntrinsics.cc @@ -9,7 +9,7 @@ #include "ast/ast.h" #include "cfg/CFG.h" #include "common/FileOps.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "compiler/Core/CompilerState.h" #include "compiler/Core/FailCompilation.h" #include "compiler/Errors/Errors.h" @@ -891,7 +891,6 @@ static const vector knownCMethods{ core::ClassOrModuleRef()}, {core::Names::stringInterpolate(), "sorbet_stringInterpolate", NoReceiver, Intrinsics::HandleBlock::Unhandled, core::Symbols::String()}, - {core::Names::selfNew(), "sorbet_selfNew", NoReceiver, Intrinsics::HandleBlock::Unhandled}, {core::Names::blockBreak(), "sorbet_block_break", NoReceiver, Intrinsics::HandleBlock::Unhandled}, {core::Names::nil_p(), "sorbet_nil_p", TakesReceiver, Intrinsics::HandleBlock::Unhandled}, {core::Names::checkMatchArray(), "sorbet_check_match_array", NoReceiver, Intrinsics::HandleBlock::Unhandled, diff --git a/compiler/IREmitter/Payload.cc b/compiler/IREmitter/Payload.cc index 906c16b0c4..678dd9b4aa 100644 --- a/compiler/IREmitter/Payload.cc +++ b/compiler/IREmitter/Payload.cc @@ -5,7 +5,7 @@ #include "IREmitterHelpers.h" #include "Payload.h" #include "ast/Trees.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "common/typecase.h" #include "compiler/Core/CompilerState.h" #include "compiler/IREmitter/IREmitterContext.h" diff --git a/compiler/IREmitter/Payload/codegen-payload.c b/compiler/IREmitter/Payload/codegen-payload.c index 8507de83b2..814ffc8afb 100644 --- a/compiler/IREmitter/Payload/codegen-payload.c +++ b/compiler/IREmitter/Payload/codegen-payload.c @@ -2300,13 +2300,6 @@ VALUE sorbet_nil_p(VALUE recv, ID fun, int argc, const VALUE *const restrict arg return sorbet_isa_NilClass(recv) ? Qtrue : Qfalse; } -SORBET_INLINE -VALUE sorbet_selfNew(VALUE recv, ID fun, int argc, VALUE *argv, BlockFFIType blk, VALUE closure) { - rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS); - VALUE obj = argv[0]; - return rb_funcallv(obj, rb_intern("new"), argc - 1, argv + 1); -} - SORBET_INLINE VALUE sorbet_returnRecv(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, VALUE closure) { diff --git a/compiler/IREmitter/SymbolBasedIntrinsics.cc b/compiler/IREmitter/SymbolBasedIntrinsics.cc index 073c00710a..fdc99f7af6 100644 --- a/compiler/IREmitter/SymbolBasedIntrinsics.cc +++ b/compiler/IREmitter/SymbolBasedIntrinsics.cc @@ -9,7 +9,7 @@ #include "ast/ast.h" #include "cfg/CFG.h" #include "common/FileOps.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "compiler/Core/CompilerState.h" #include "compiler/Core/FailCompilation.h" #include "compiler/Errors/Errors.h" diff --git a/compiler/IREmitter/sends.cc b/compiler/IREmitter/sends.cc index 7910d2b28b..6a270ae6e3 100644 --- a/compiler/IREmitter/sends.cc +++ b/compiler/IREmitter/sends.cc @@ -9,7 +9,7 @@ #include "ast/ast.h" #include "cfg/CFG.h" #include "common/FileOps.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "compiler/Core/CompilerState.h" #include "compiler/Core/FailCompilation.h" #include "compiler/Errors/Errors.h" diff --git a/compiler/Linker/Linker.cc b/compiler/Linker/Linker.cc index 54bbc229a6..32067245c0 100644 --- a/compiler/Linker/Linker.cc +++ b/compiler/Linker/Linker.cc @@ -1,6 +1,6 @@ #include "compiler/Linker/Linker.h" #include "common/Subprocess.h" -#include "common/Timer.h" +#include "common/timers/Timer.h" using namespace std; namespace sorbet::compiler { diff --git a/compiler/ObjectFileEmitter/ObjectFileEmitter.cc b/compiler/ObjectFileEmitter/ObjectFileEmitter.cc index 30e39268a5..dd05ad9f8e 100644 --- a/compiler/ObjectFileEmitter/ObjectFileEmitter.cc +++ b/compiler/ObjectFileEmitter/ObjectFileEmitter.cc @@ -38,7 +38,7 @@ #include "llvm/Transforms/Vectorize.h" #include "common/FileOps.h" -#include "common/Timer.h" +#include "common/timers/Timer.h" #include "compiler/Linker/Linker.h" #include "compiler/ObjectFileEmitter/ObjectFileEmitter.h" #include "compiler/Passes/Passes.h" diff --git a/core/AutocorrectSuggestion.cc b/core/AutocorrectSuggestion.cc index 2ef6e10209..bf84eb7d44 100644 --- a/core/AutocorrectSuggestion.cc +++ b/core/AutocorrectSuggestion.cc @@ -1,6 +1,6 @@ #include "core/AutocorrectSuggestion.h" #include "absl/strings/str_cat.h" -#include "common/sort.h" +#include "common/sort/sort.h" using namespace std; diff --git a/core/BUILD b/core/BUILD index 4dc84dce53..9c6fb821b4 100644 --- a/core/BUILD +++ b/core/BUILD @@ -4,6 +4,7 @@ cc_library( [ "*.cc", "*.h", + "errors/*.cc", "types/*.cc", "lsp/*.cc", "packages/*.cc", @@ -58,8 +59,8 @@ cc_test( visibility = ["//tools:__pkg__"], deps = [ ":core", - "@doctest", - "@doctest//:doctest_main", + "@doctest//doctest", + "@doctest//doctest:main", ], ) diff --git a/core/Error.cc b/core/Error.cc index 644a3b822f..66dca03211 100644 --- a/core/Error.cc +++ b/core/Error.cc @@ -171,9 +171,9 @@ void ErrorBuilder::addAutocorrect(AutocorrectSuggestion &&autocorrect) { sectionTitle = "Autocorrect: Done"; } else if (autocorrect.isDidYouMean && autocorrect.edits.size() == 1) { sectionTitle = - ErrorColors::format("Did you mean `{}`? Use `-a` to autocorrect", autocorrect.edits[0].replacement); + ErrorColors::format("Did you mean `{}`? Use `{}` to autocorrect", autocorrect.edits[0].replacement, "-a"); } else { - sectionTitle = "Autocorrect: Use `-a` to autocorrect"; + sectionTitle = ErrorColors::format("Autocorrect: Use `{}` to autocorrect", "-a"); } std::vector messages; diff --git a/core/ErrorQueue.cc b/core/ErrorQueue.cc index 0efa124e6c..2034a8f7f0 100644 --- a/core/ErrorQueue.cc +++ b/core/ErrorQueue.cc @@ -1,6 +1,6 @@ #include "core/ErrorQueue.h" #include "common/FileSystem.h" -#include "common/Timer.h" +#include "common/timers/Timer.h" #include "core/Error.h" #include "core/ErrorFlusherStdout.h" diff --git a/core/FileHash.cc b/core/FileHash.cc index 09deed06d8..0c9dd901f2 100644 --- a/core/FileHash.cc +++ b/core/FileHash.cc @@ -1,5 +1,5 @@ #include "core/FileHash.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "core/FoundDefinitions.h" #include "core/GlobalState.h" #include "core/Names.h" @@ -85,6 +85,7 @@ WithoutUniqueNameHash::WithoutUniqueNameHash(const GlobalState &gs, NameRef nm) void WithoutUniqueNameHash::sortAndDedupe(std::vector &hashes) { fast_sort(hashes); hashes.resize(std::distance(hashes.begin(), std::unique(hashes.begin(), hashes.end()))); + hashes.shrink_to_fit(); } FullNameHash::FullNameHash(const GlobalState &gs, NameRef nm) : _hashValue(incZero(hashFullNameRef(gs, nm))) {} @@ -92,6 +93,7 @@ FullNameHash::FullNameHash(const GlobalState &gs, NameRef nm) : _hashValue(incZe void FullNameHash::sortAndDedupe(std::vector &hashes) { fast_sort(hashes); hashes.resize(std::distance(hashes.begin(), std::unique(hashes.begin(), hashes.end()))); + hashes.shrink_to_fit(); } FoundDefinitionRef FoundStaticFieldHash::owner() const { diff --git a/core/FileHash.h b/core/FileHash.h index 02d65410bd..ebcd987afc 100644 --- a/core/FileHash.h +++ b/core/FileHash.h @@ -35,6 +35,7 @@ class WithoutUniqueNameHash { uint32_t _hashValue; }; +CheckSize(WithoutUniqueNameHash, 4, 4); template H AbslHashValue(H h, const WithoutUniqueNameHash &m) { return H::combine(std::move(h), m._hashValue); @@ -66,6 +67,7 @@ class FullNameHash { uint32_t _hashValue; }; +CheckSize(FullNameHash, 4, 4); template H AbslHashValue(H h, const FullNameHash &m) { return H::combine(std::move(h), m._hashValue); @@ -89,6 +91,7 @@ struct SymbolHash { return this->nameHash < h.nameHash || (!(h.nameHash < this->nameHash) && this->symbolHash < h.symbolHash); } }; +CheckSize(SymbolHash, 8, 4); // 28 is the same as the size of an ID in FoundDefinitionRef::_storage // @@ -246,8 +249,6 @@ struct LocalSymbolTableHashes { uint32_t hierarchyHash = HASH_STATE_NOT_COMPUTED; // A fingerprint for the classes and modules contained in the file. uint32_t classModuleHash = HASH_STATE_NOT_COMPUTED; - // A fingerprint for the type argument symbols contained in the file. - uint32_t typeArgumentHash = HASH_STATE_NOT_COMPUTED; // A fingerprint for the type member symbols contained in the file. uint32_t typeMemberHash = HASH_STATE_NOT_COMPUTED; // A fingerprint for the fields contained in the file. @@ -287,7 +288,6 @@ struct LocalSymbolTableHashes { LocalSymbolTableHashes ret; ret.hierarchyHash = HASH_STATE_INVALID_PARSE; ret.classModuleHash = HASH_STATE_INVALID_PARSE; - ret.typeArgumentHash = HASH_STATE_INVALID_PARSE; ret.typeMemberHash = HASH_STATE_INVALID_PARSE; ret.fieldHash = HASH_STATE_INVALID_PARSE; ret.staticFieldHash = HASH_STATE_INVALID_PARSE; @@ -300,7 +300,6 @@ struct LocalSymbolTableHashes { DEBUG_ONLY( if (hierarchyHash == HASH_STATE_INVALID_PARSE) { ENFORCE(classModuleHash == core::LocalSymbolTableHashes::HASH_STATE_INVALID_PARSE); - ENFORCE(typeArgumentHash == core::LocalSymbolTableHashes::HASH_STATE_INVALID_PARSE); ENFORCE(typeMemberHash == core::LocalSymbolTableHashes::HASH_STATE_INVALID_PARSE); ENFORCE(fieldHash == core::LocalSymbolTableHashes::HASH_STATE_INVALID_PARSE); ENFORCE(staticFieldHash == core::LocalSymbolTableHashes::HASH_STATE_INVALID_PARSE); @@ -308,7 +307,6 @@ struct LocalSymbolTableHashes { ENFORCE(methodHash == core::LocalSymbolTableHashes::HASH_STATE_INVALID_PARSE); } else { ENFORCE(classModuleHash != core::LocalSymbolTableHashes::HASH_STATE_INVALID_PARSE); - ENFORCE(typeArgumentHash != core::LocalSymbolTableHashes::HASH_STATE_INVALID_PARSE); ENFORCE(typeMemberHash != core::LocalSymbolTableHashes::HASH_STATE_INVALID_PARSE); ENFORCE(fieldHash != core::LocalSymbolTableHashes::HASH_STATE_INVALID_PARSE); ENFORCE(staticFieldHash != core::LocalSymbolTableHashes::HASH_STATE_INVALID_PARSE); @@ -318,6 +316,7 @@ struct LocalSymbolTableHashes { return hierarchyHash == HASH_STATE_INVALID_PARSE; } }; +CheckSize(LocalSymbolTableHashes, 56, 8); // This structure represents every time a name was used in a place where it could be referencing the // name of a (Sorbet) symbol. For example, this program: @@ -353,6 +352,7 @@ struct FileHash { FileHash() = default; FileHash(LocalSymbolTableHashes &&localSymbolTableHashes, UsageHash &&usages, FoundDefHashes &&foundHashes); }; +CheckSize(FileHash, 176, 8); }; // namespace sorbet::core diff --git a/core/FoundDefinitions.h b/core/FoundDefinitions.h index 461c805b1e..22d1349910 100644 --- a/core/FoundDefinitions.h +++ b/core/FoundDefinitions.h @@ -107,6 +107,7 @@ struct FoundClass final { core::NameRef name; core::LocOffsets loc; core::LocOffsets declLoc; + bool definesBehavior = false; enum class Kind : uint8_t { Unknown, @@ -211,7 +212,7 @@ CheckSize(FoundField, 20, 4); class FoundDefinitions final { // Contains references to items in _staticFields and _typeMembers. // Used so there is a consistent definition & redefinition ordering. - std::vector _deletableDefinitions; + std::vector _nonClassConstants; // Contains all classes defined in the file. std::vector _klasses; // Contains all methods defined in the file. @@ -225,7 +226,7 @@ class FoundDefinitions final { // Contains all class and instance variables defined in the file. std::vector _fields; - FoundDefinitionRef addDefinition(FoundDefinitionRef ref) { + FoundDefinitionRef addNonClassConstant(FoundDefinitionRef ref) { DEBUG_ONLY(switch (ref.kind()) { case FoundDefinitionRef::Kind::StaticField: case FoundDefinitionRef::Kind::TypeMember: @@ -237,7 +238,7 @@ class FoundDefinitions final { case FoundDefinitionRef::Kind::Symbol: ENFORCE(false, "Attempted to give unexpected FoundDefinitionRef kind to addDefinition"); }); - _deletableDefinitions.emplace_back(ref); + _nonClassConstants.emplace_back(ref); return ref; } @@ -262,13 +263,13 @@ class FoundDefinitions final { FoundDefinitionRef addStaticField(FoundStaticField &&staticField) { const uint32_t idx = _staticFields.size(); _staticFields.emplace_back(std::move(staticField)); - return addDefinition(FoundDefinitionRef(FoundDefinitionRef::Kind::StaticField, idx)); + return addNonClassConstant(FoundDefinitionRef(FoundDefinitionRef::Kind::StaticField, idx)); } FoundDefinitionRef addTypeMember(FoundTypeMember &&typeMember) { const uint32_t idx = _typeMembers.size(); _typeMembers.emplace_back(std::move(typeMember)); - return addDefinition(FoundDefinitionRef(FoundDefinitionRef::Kind::TypeMember, idx)); + return addNonClassConstant(FoundDefinitionRef(FoundDefinitionRef::Kind::TypeMember, idx)); } FoundDefinitionRef addField(FoundField &&field) { @@ -285,9 +286,9 @@ class FoundDefinitions final { _modifiers.emplace_back(std::move(mod)); } - // See documentation on _deletableDefinitions - const std::vector &deletableDefinitions() const { - return _deletableDefinitions; + // See documentation on _nonClassConstants + const std::vector &nonClassConstants() const { + return _nonClassConstants; } // See documentation on _klasses diff --git a/core/GlobalState.cc b/core/GlobalState.cc index ef2bb3999f..8663621bda 100644 --- a/core/GlobalState.cc +++ b/core/GlobalState.cc @@ -1,7 +1,7 @@ #include "GlobalState.h" -#include "common/Timer.h" -#include "common/sort.h" +#include "common/sort/sort.h" +#include "common/timers/Timer.h" #include "core/Error.h" #include "core/FileHash.h" #include "core/Names.h" @@ -14,12 +14,14 @@ #include "core/lsp/Task.h" #include "core/lsp/TypecheckEpochManager.h" #include +#include #include #include "absl/strings/str_cat.h" #include "absl/strings/str_split.h" #include "core/ErrorQueue.h" #include "core/errors/infer.h" +#include "core/packages/MangledName.h" #include "main/pipeline/semantic_extension/SemanticExtension.h" template class std::vector>; @@ -350,6 +352,8 @@ void GlobalState::initEmpty() { ENFORCE(klass == Symbols::untyped()); klass = synthesizeClass(core::Names::Constants::T(), Symbols::todo().id(), true); ENFORCE(klass == Symbols::T()); + klass = klass.data(*this)->singletonClass(*this); + ENFORCE(klass == Symbols::TSingleton()); klass = synthesizeClass(core::Names::Constants::Class(), 0); ENFORCE(klass == Symbols::Class()); klass = synthesizeClass(core::Names::Constants::BasicObject(), 0); @@ -366,6 +370,8 @@ void GlobalState::initEmpty() { ENFORCE(klass == Symbols::MagicSingleton()); klass = synthesizeClass(core::Names::Constants::Module()); ENFORCE(klass == Symbols::Module()); + klass = synthesizeClass(core::Names::Constants::Exception()); + ENFORCE(klass == Symbols::Exception()); klass = synthesizeClass(core::Names::Constants::StandardError()); ENFORCE(klass == Symbols::StandardError()); klass = synthesizeClass(core::Names::Constants::Complex()); @@ -393,6 +399,7 @@ void GlobalState::initEmpty() { klass = enterClassSymbol(Loc::none(), Symbols::Sorbet(), core::Names::Constants::Private()); ENFORCE(klass == Symbols::Sorbet_Private()); klass = enterClassSymbol(Loc::none(), Symbols::Sorbet_Private(), core::Names::Constants::Static()); + klass.data(*this)->setIsModule(true); // explicitly set isModule so we can immediately call singletonClass ENFORCE(klass == Symbols::Sorbet_Private_Static()); klass = Symbols::Sorbet_Private_Static().data(*this)->singletonClass(*this); ENFORCE(klass == Symbols::Sorbet_Private_StaticSingleton()); @@ -420,16 +427,20 @@ void GlobalState::initEmpty() { klass = enterClassSymbol(Loc::none(), Symbols::T(), core::Names::Constants::Generic()); ENFORCE(klass == Symbols::T_Generic()); klass = enterClassSymbol(Loc::none(), Symbols::Sorbet_Private_Static(), core::Names::Constants::Tuple()); + klass.data(*this)->setIsModule(false); ENFORCE(klass == Symbols::Tuple()); klass = enterClassSymbol(Loc::none(), Symbols::Sorbet_Private_Static(), core::Names::Constants::Shape()); + klass.data(*this)->setIsModule(false); ENFORCE(klass == Symbols::Shape()); klass = enterClassSymbol(Loc::none(), Symbols::Sorbet_Private_Static(), core::Names::Constants::Subclasses()); ENFORCE(klass == Symbols::Subclasses()); klass = enterClassSymbol(Loc::none(), Symbols::Sorbet_Private_Static(), core::Names::Constants::ImplicitModuleSuperclass()); + klass.data(*this)->setIsModule(false); ENFORCE(klass == Symbols::Sorbet_Private_Static_ImplicitModuleSuperClass()); klass = enterClassSymbol(Loc::none(), Symbols::Sorbet_Private_Static(), core::Names::Constants::ReturnTypeInference()); + klass.data(*this)->setIsModule(false); ENFORCE(klass == Symbols::Sorbet_Private_Static_ReturnTypeInference()); method = enterMethod(*this, Symbols::Sorbet_Private_Static(), core::Names::guessedTypeTypeParameterHolder()).build(); @@ -483,6 +494,7 @@ void GlobalState::initEmpty() { Symbols::Net_Protocol().data(*this)->setIsModule(false); klass = enterClassSymbol(Loc::none(), Symbols::T_Sig(), core::Names::Constants::WithoutRuntime()); + klass.data(*this)->setIsModule(true); // explicitly set isModule so we can immediately call singletonClass ENFORCE(klass == Symbols::T_Sig_WithoutRuntime()); klass = synthesizeClass(core::Names::Constants::Enumerator()); @@ -491,8 +503,11 @@ void GlobalState::initEmpty() { ENFORCE(klass == Symbols::T_Enumerator()); klass = enterClassSymbol(Loc::none(), Symbols::T_Enumerator(), core::Names::Constants::Lazy()); ENFORCE(klass == Symbols::T_Enumerator_Lazy()); + klass = enterClassSymbol(Loc::none(), Symbols::T_Enumerator(), core::Names::Constants::Chain()); + ENFORCE(klass == Symbols::T_Enumerator_Chain()); klass = enterClassSymbol(Loc::none(), Symbols::T(), core::Names::Constants::Struct()); + klass.data(*this)->setIsModule(false); ENFORCE(klass == Symbols::T_Struct()); klass = synthesizeClass(core::Names::Constants::Singleton(), 0, true); @@ -508,8 +523,14 @@ void GlobalState::initEmpty() { // Enumerator::Lazy klass = enterClassSymbol(Loc::none(), Symbols::Enumerator(), core::Names::Constants::Lazy()); + klass.data(*this)->setIsModule(false); ENFORCE(klass == Symbols::Enumerator_Lazy()); + // Enumerator::Chain + klass = enterClassSymbol(Loc::none(), Symbols::Enumerator(), core::Names::Constants::Chain()); + klass.data(*this)->setIsModule(false); + ENFORCE(klass == Symbols::Enumerator_Chain()); + klass = enterClassSymbol(Loc::none(), Symbols::T(), Names::Constants::Private()); ENFORCE(klass == Symbols::T_Private()); klass = enterClassSymbol(Loc::none(), Symbols::T_Private(), Names::Constants::Types()); @@ -518,6 +539,7 @@ void GlobalState::initEmpty() { klass.data(*this)->setIsModule(false); ENFORCE(klass == Symbols::T_Private_Types_Void()); klass = enterClassSymbol(Loc::none(), Symbols::T_Private_Types_Void(), Names::Constants::VOID()); + klass.data(*this)->setIsModule(true); // explicitly set isModule so we can immediately call singletonClass ENFORCE(klass == Symbols::T_Private_Types_Void_VOID()); klass = klass.data(*this)->singletonClass(*this); ENFORCE(klass == Symbols::T_Private_Types_Void_VOIDSingleton()); @@ -594,12 +616,20 @@ void GlobalState::initEmpty() { .build(); ENFORCE(method == Symbols::PackageSpec_autoloader_compatibility()); + method = enterMethod(*this, Symbols::PackageSpecSingleton(), Names::visible_to()).arg(Names::arg0()).build(); + ENFORCE(method == Symbols::PackageSpec_visible_to()); + + method = enterMethod(*this, Symbols::PackageSpecSingleton(), Names::exportAll()).build(); + ENFORCE(method == Symbols::PackageSpec_export_all()); + klass = enterClassSymbol(Loc::none(), Symbols::Sorbet_Private_Static(), core::Names::Constants::ResolvedSig()); + klass.data(*this)->setIsModule(true); // explicitly set isModule so we can immediately call singletonClass ENFORCE(klass == Symbols::Sorbet_Private_Static_ResolvedSig()); klass = Symbols::Sorbet_Private_Static_ResolvedSig().data(*this)->singletonClass(*this); ENFORCE(klass == Symbols::Sorbet_Private_Static_ResolvedSigSingleton()); klass = enterClassSymbol(Loc::none(), Symbols::T_Private(), core::Names::Constants::Compiler()); + klass.data(*this)->setIsModule(true); // explicitly set isModule so we can immediately call singletonClass ENFORCE(klass == Symbols::T_Private_Compiler()); klass = Symbols::T_Private_Compiler().data(*this)->singletonClass(*this); ENFORCE(klass == Symbols::T_Private_CompilerSingleton()); @@ -615,8 +645,16 @@ void GlobalState::initEmpty() { ENFORCE(klass == Symbols::T_Types()); klass = enterClassSymbol(Loc::none(), Symbols::T_Types(), core::Names::Constants::Base()); + klass.data(*this)->setIsModule(false); ENFORCE(klass == Symbols::T_Types_Base()); + klass = enterClassSymbol(Loc::none(), Symbols::root(), core::Names::Constants::Data()); + klass.data(*this)->setIsModule(false); + ENFORCE(klass == Symbols::Data()); + + klass = enterClassSymbol(Loc::none(), Symbols::T(), core::Names::Constants::Class()); + ENFORCE(klass == Symbols::T_Class()); + typeArgument = enterTypeArgument(Loc::none(), Symbols::noMethod(), Names::Constants::TodoTypeArgument(), Variance::CoVariant); ENFORCE(typeArgument == Symbols::todoTypeArgument()); @@ -695,10 +733,6 @@ void GlobalState::initEmpty() { .untypedArg(Names::arg2()) // method name where assign is .untypedArg(Names::arg3()) // name of variable .buildWithResultUntyped(); - // Synthesize .(arg: *T.untyped) => T.untyped - method = enterMethod(*this, Symbols::MagicSingleton(), Names::selfNew()) - .repeatedUntypedArg(Names::arg0()) - .buildWithResultUntyped(); // Synthesize .attachedClass(arg: *T.untyped) => T.untyped // (accept any args to avoid repeating errors that would otherwise be reported by type syntax parsing) method = enterMethod(*this, Symbols::MagicSingleton(), Names::attachedClass()) @@ -819,6 +853,9 @@ void GlobalState::initEmpty() { // Collect size prior to loop since singletons will cause vector to grow. size_t classAndModulesSize = classAndModules.size(); for (uint32_t i = 1; i < classAndModulesSize; i++) { + if (!classAndModules[i].isClassModuleSet()) { + classAndModules[i].setIsModule(true); + } classAndModules[i].singletonClass(*this); } @@ -869,7 +906,6 @@ void GlobalState::initEmpty() { Symbols::Symbol().data(*this)->resultType = Types::Symbol(); Symbols::Float().data(*this)->resultType = Types::Float(); Symbols::Object().data(*this)->resultType = Types::Object(); - Symbols::Class().data(*this)->resultType = Types::classClass(); // First file is used to indicate absence of a file files.emplace_back(); @@ -1655,8 +1691,8 @@ NameRef GlobalState::freshNameUnique(UniqueNameKind uniqueNameKind, NameRef orig FileRef GlobalState::enterFile(const shared_ptr &file) { ENFORCE(!fileTableFrozen); - DEBUG_ONLY(for (auto &f - : this->files) { + SLOW_DEBUG_ONLY(for (auto &f + : this->files) { if (f) { if (f->path() == file->path()) { Exception::raise("Request to `enterFile` for already-entered file path?"); @@ -1782,6 +1818,8 @@ void GlobalState::mangleRenameForOverload(MethodRef what, NameRef origName) { // similar to mangleRenameMethod, so it's nice to have the implementation in the same file). But in // spirit, this is a private Namer helper function. void GlobalState::deleteMethodSymbol(MethodRef what) { + ENFORCE(!symbolTableFrozen); + const auto &whatData = what.data(*this); auto owner = whatData->owner; auto &ownerMembers = owner.data(*this)->members(); @@ -1800,6 +1838,8 @@ void GlobalState::deleteMethodSymbol(MethodRef what) { // // NOTE: This method does double duty, deleting both static-field and field symbols. void GlobalState::deleteFieldSymbol(FieldRef what) { + ENFORCE(!symbolTableFrozen); + const auto &whatData = what.data(*this); auto owner = whatData->owner; auto &ownerMembers = owner.data(*this)->members(); @@ -1812,6 +1852,8 @@ void GlobalState::deleteFieldSymbol(FieldRef what) { // Before using this method, double check the disclaimer on GlobalState::deleteMethodSymbol above. void GlobalState::deleteTypeMemberSymbol(TypeMemberRef what) { + ENFORCE(!symbolTableFrozen); + const auto &whatData = what.data(*this); // Should always be a class or module for type members, but we use core::TypeParameter to model both // `type_members` and `type_parameters` (which are owned by `Method` symbols). @@ -2010,7 +2052,8 @@ unique_ptr GlobalState::deepCopy(bool keepId) const { result->sleepInSlowPathSeconds = this->sleepInSlowPathSeconds; result->requiresAncestorEnabled = this->requiresAncestorEnabled; result->ruby3KeywordArgs = this->ruby3KeywordArgs; - result->lspExperimentalFastPathEnabled = this->lspExperimentalFastPathEnabled; + result->trackUntyped = this->trackUntyped; + result->printingFileTable = this->printingFileTable; result->isSCIPRuby = this->isSCIPRuby; if (keepId) { @@ -2106,11 +2149,11 @@ unique_ptr GlobalState::copyForIndex() const { result->ensureCleanStrings = this->ensureCleanStrings; result->runningUnderAutogen = this->runningUnderAutogen; result->censorForSnapshotTests = this->censorForSnapshotTests; - result->lspExperimentalFastPathEnabled = this->lspExperimentalFastPathEnabled; result->isSCIPRuby = this->isSCIPRuby; result->sleepInSlowPathSeconds = this->sleepInSlowPathSeconds; result->requiresAncestorEnabled = this->requiresAncestorEnabled; result->ruby3KeywordArgs = this->ruby3KeywordArgs; + result->trackUntyped = this->trackUntyped; result->kvstoreUuid = this->kvstoreUuid; result->errorUrlBase = this->errorUrlBase; result->suppressedErrorClasses = this->suppressedErrorClasses; @@ -2222,8 +2265,7 @@ bool GlobalState::shouldReportErrorOn(Loc loc, ErrorClass what) const { } } else if (level == StrictLevel::Stdlib) { level = StrictLevel::Strict; - if (what == errors::Resolver::OverloadNotAllowed || what == errors::Resolver::VariantTypeMemberInClass || - what == errors::Infer::UntypedMethod) { + if (what == errors::Resolver::OverloadNotAllowed || what == errors::Infer::UntypedMethod) { return false; } } @@ -2275,6 +2317,7 @@ void GlobalState::setPackagerOptions(const std::vector &secondaryTe const std::vector &extraPackageFilesDirectoryUnderscorePrefixes, const std::vector &extraPackageFilesDirectorySlashPrefixes, const std::vector &packageSkipRBIExportEnforcementDirs, + const std::vector &skipImportVisibilityCheckFor, std::string errorHint) { ENFORCE(packageDB_.secondaryTestPackageNamespaceRefs_.size() == 0); ENFORCE(!packageDB_.frozen); @@ -2286,6 +2329,14 @@ void GlobalState::setPackagerOptions(const std::vector &secondaryTe packageDB_.extraPackageFilesDirectoryUnderscorePrefixes_ = extraPackageFilesDirectoryUnderscorePrefixes; packageDB_.extraPackageFilesDirectorySlashPrefixes_ = extraPackageFilesDirectorySlashPrefixes; packageDB_.skipRBIExportEnforcementDirs_ = packageSkipRBIExportEnforcementDirs; + + std::vector skipImportVisibilityCheckFor_; + for (const string &pkgName : skipImportVisibilityCheckFor) { + std::vector pkgNameParts = absl::StrSplit(pkgName, "::"); + auto mangledName = core::packages::MangledName::mangledNameFromParts(*this, pkgNameParts); + skipImportVisibilityCheckFor_.emplace_back(mangledName); + } + packageDB_.skipImportVisibilityCheckFor_ = skipImportVisibilityCheckFor_; packageDB_.errorHint_ = errorHint; } @@ -2303,7 +2354,6 @@ unique_ptr GlobalState::hash() const { constexpr bool DEBUG_HASHING_TAIL = false; uint32_t hierarchyHash = 0; uint32_t classModuleHash = 0; - uint32_t typeArgumentHash = 0; // TODO(jez) Delete at same time as lspExperimentalFastPathEnabled uint32_t typeMemberHash = 0; uint32_t fieldHash = 0; uint32_t staticFieldHash = 0; @@ -2319,14 +2369,9 @@ unique_ptr GlobalState::hash() const { uint32_t symhash = sym.hash(*this, skipTypeMemberNames); target = mix(target, symhash); - if (this->lspExperimentalFastPathEnabled) { - uint32_t classOrModuleShapeHash = sym.classOrModuleShapeHash(*this); - hierarchyHash = mix(hierarchyHash, classOrModuleShapeHash); - classModuleHash = mix(classModuleHash, classOrModuleShapeHash); - } else { - hierarchyHash = mix(hierarchyHash, symhash); - classModuleHash = mix(classModuleHash, symhash); - } + uint32_t classOrModuleShapeHash = sym.classOrModuleShapeHash(*this); + hierarchyHash = mix(hierarchyHash, classOrModuleShapeHash); + classModuleHash = mix(classModuleHash, classOrModuleShapeHash); counter++; if (DEBUG_HASHING_TAIL && counter > this->classAndModules.size() - 15) { @@ -2338,19 +2383,6 @@ unique_ptr GlobalState::hash() const { // Type arguments are included in Method::hash. If only a type argument changes, the method's // hash will change but the hierarchyHash will not change, so Sorbet will take the fast path and // delete the method and all its arguments - if (!this->lspExperimentalFastPathEnabled) { - counter = 0; - for (const auto &typeArg : this->typeArguments) { - counter++; - // No type arguments are ignored in hashing. - uint32_t symhash = typeArg.hash(*this); - hierarchyHash = mix(hierarchyHash, symhash); - typeArgumentHash = mix(typeArgumentHash, symhash); - if (DEBUG_HASHING_TAIL && counter > this->typeArguments.size() - 15) { - errorQueue->logger.info("Hashing symbols: {}, {}", hierarchyHash, typeArg.name.show(*this)); - } - } - } counter = 0; for (const auto &typeMember : this->typeMembers) { @@ -2359,10 +2391,6 @@ unique_ptr GlobalState::hash() const { uint32_t symhash = typeMember.hash(*this); auto &target = retypecheckableSymbolHashesMap[WithoutUniqueNameHash(*this, typeMember.name)]; target = mix(target, symhash); - if (!this->lspExperimentalFastPathEnabled) { - hierarchyHash = mix(hierarchyHash, symhash); - typeMemberHash = mix(typeMemberHash, symhash); - } if (DEBUG_HASHING_TAIL && counter > this->typeMembers.size() - 15) { errorQueue->logger.info("Hashing symbols: {}, {}", hierarchyHash, typeMember.name.show(*this)); } @@ -2377,15 +2405,9 @@ unique_ptr GlobalState::hash() const { // Either normal static-field or static-field-type-alias auto &target = retypecheckableSymbolHashesMap[WithoutUniqueNameHash(*this, field.name)]; target = mix(target, symhash); - if (!this->lspExperimentalFastPathEnabled) { - uint32_t staticFieldShapeHash = field.fieldShapeHash(*this); - hierarchyHash = mix(hierarchyHash, staticFieldShapeHash); - staticFieldHash = mix(staticFieldHash, staticFieldShapeHash); - } } else if (field.flags.isStaticField) { const auto &dealiased = field.dealias(*this); - if (this->lspExperimentalFastPathEnabled && dealiased.isTypeMember() && - field.name == dealiased.name(*this) && + if (dealiased.isTypeMember() && field.name == dealiased.name(*this) && dealiased.owner(*this) == field.owner.data(*this)->lookupSingletonClass(*this)) { // This is a static field class alias that forwards to a type_template on the singleton class // (in service of constant literal resolution). Treat this as a type member (which we can @@ -2399,11 +2421,6 @@ unique_ptr GlobalState::hash() const { ENFORCE(field.flags.isField); auto &target = retypecheckableSymbolHashesMap[WithoutUniqueNameHash(*this, field.name)]; target = mix(target, symhash); - if (!this->lspExperimentalFastPathEnabled) { - uint32_t fieldShapeHash = field.fieldShapeHash(*this); - hierarchyHash = mix(hierarchyHash, fieldShapeHash); - fieldHash = mix(fieldHash, fieldShapeHash); - } } if (DEBUG_HASHING_TAIL && counter > this->fields.size() - 15) { @@ -2416,22 +2433,15 @@ unique_ptr GlobalState::hash() const { if (!sym.ignoreInHashing(*this)) { auto &target = retypecheckableSymbolHashesMap[WithoutUniqueNameHash(*this, sym.name)]; target = mix(target, sym.hash(*this)); - auto needMethodShapeHash = - this->lspExperimentalFastPathEnabled - ? (sym.name == Names::unresolvedAncestors() || sym.name == Names::requiredAncestors() || - sym.name == Names::requiredAncestorsLin()) - : true; + auto needMethodShapeHash = sym.name == Names::unresolvedAncestors() || + sym.name == Names::requiredAncestors() || + sym.name == Names::requiredAncestorsLin(); if (needMethodShapeHash) { uint32_t methodShapeHash = sym.methodShapeHash(*this); hierarchyHash = mix(hierarchyHash, methodShapeHash); - if (this->lspExperimentalFastPathEnabled) { - // With this feature enabled, the only three methods that trigger a method - // change anymore all relate to inheritance. Let's blame this to a change to - // class symbols, not to methods - classModuleHash = mix(classModuleHash, methodShapeHash); - } else { - methodHash = mix(methodHash, methodShapeHash); - } + // The only three methods that trigger a method change anymore all relate to inheritance. + // Let's blame this to a change to class symbols, not to methods + classModuleHash = mix(classModuleHash, methodShapeHash); } counter++; @@ -2451,7 +2461,6 @@ unique_ptr GlobalState::hash() const { result->hierarchyHash = LocalSymbolTableHashes::patchHash(hierarchyHash); result->classModuleHash = LocalSymbolTableHashes::patchHash(classModuleHash); - result->typeArgumentHash = LocalSymbolTableHashes::patchHash(typeArgumentHash); result->typeMemberHash = LocalSymbolTableHashes::patchHash(typeMemberHash); result->fieldHash = LocalSymbolTableHashes::patchHash(fieldHash); result->staticFieldHash = LocalSymbolTableHashes::patchHash(staticFieldHash); diff --git a/core/GlobalState.h b/core/GlobalState.h index 502a0da6d7..61f15b1dbb 100644 --- a/core/GlobalState.h +++ b/core/GlobalState.h @@ -164,7 +164,8 @@ class GlobalState final { void setPackagerOptions(const std::vector &secondaryTestPackageNamespaces, const std::vector &extraPackageFilesDirectoryUnderscorePrefixes, const std::vector &extraPackageFilesDirectorySlashPrefixes, - const std::vector &packageSkipRBIExportEnforcementDirs, std::string errorHint); + const std::vector &packageSkipRBIExportEnforcementDirs, + const std::vector &skipImportVisibilityCheckFor, std::string errorHint); packages::UnfreezePackages unfreezePackages(); NameRef nextMangledName(ClassOrModuleRef owner, NameRef origName); @@ -232,6 +233,8 @@ class GlobalState final { bool unsilenceErrors = false; bool logRecordedFilepaths = false; bool autocorrect = false; + bool trackUntyped = false; + bool printingFileTable = false; // We have a lot of internal names of form `` that's chosen with `<` and `>` as you can't make // this into a valid ruby identifier without suffering. @@ -296,9 +299,6 @@ class GlobalState final { // If 'true', enforce use of Ruby 3.0-style keyword args. bool ruby3KeywordArgs = false; - // If 'true', enable the experimental, symbol-deletion-based fast path mode - bool lspExperimentalFastPathEnabled = false; - // If 'true', we're running in scip-ruby mode. bool isSCIPRuby = true; diff --git a/core/NameSubstitution.cc b/core/NameSubstitution.cc index bbb7fe4f94..02b1a3a99e 100644 --- a/core/NameSubstitution.cc +++ b/core/NameSubstitution.cc @@ -8,7 +8,7 @@ namespace sorbet::core { NameSubstitution::NameSubstitution(const GlobalState &from, GlobalState &to) : toGlobalStateId(to.globalStateId) { Timer timeit(to.tracer(), "NameSubstitution.new", from.creation); - from.sanityCheck(); + SLOW_DEBUG_ONLY(from.sanityCheck()); { UnfreezeNameTable unfreezeNames(to); @@ -53,7 +53,7 @@ NameSubstitution::NameSubstitution(const GlobalState &from, GlobalState &to) : t extension->merge(from, to, *this); } - to.sanityCheck(); + SLOW_DEBUG_ONLY(to.sanityCheck()); } LazyNameSubstitution::LazyNameSubstitution(const GlobalState &fromGS, GlobalState &toGS) : fromGS(fromGS), toGS(toGS) { diff --git a/core/SymbolRef.h b/core/SymbolRef.h index 7b95af21ef..d9cbd9bae0 100644 --- a/core/SymbolRef.h +++ b/core/SymbolRef.h @@ -161,6 +161,8 @@ class ClassOrModuleRef final { }; std::string show(const GlobalState &gs, ShowOptions options) const; + bool isOnlyDefinedInFile(const GlobalState &gs, core::FileRef file) const; + // Given a symbol like ::Project::Foo, returns true. // Given any other symbol, returns false. // Also returns false if called on core::Symbols::noClassOrModule(). @@ -440,6 +442,7 @@ class SymbolRef final { bool isTypeAlias(const GlobalState &gs) const; bool isField(const GlobalState &gs) const; bool isStaticField(const GlobalState &gs) const; + bool isClassAlias(const GlobalState &gs) const; uint32_t classOrModuleIndex() const { ENFORCE_NO_TIMER(kind() == Kind::ClassOrModule); @@ -492,6 +495,8 @@ class SymbolRef final { return unsafeTableIndex() != 0; } + bool isOnlyDefinedInFile(const GlobalState &gs, core::FileRef file) const; + bool isSynthetic() const; // If Kind is ClassOrModule, returns a ClassOrModuleRef. @@ -564,6 +569,12 @@ class SymbolRef final { return show(gs, {}); }; std::string show(const GlobalState &gs, ShowOptions options) const; + + /* + * Returns true if symbol is under the namespace of otherClass. Foo::Bar::A is under its own namespace, also is + * under the namespace of Foo::Bar and Foo. + */ + bool isUnderNamespace(const GlobalState &gs, core::ClassOrModuleRef otherClass) const; }; CheckSize(SymbolRef, 4, 4); @@ -647,169 +658,177 @@ class Symbols { return ClassOrModuleRef::fromRaw(17); } - static ClassOrModuleRef Class() { + static ClassOrModuleRef TSingleton() { return ClassOrModuleRef::fromRaw(18); } - static ClassOrModuleRef BasicObject() { + static ClassOrModuleRef Class() { return ClassOrModuleRef::fromRaw(19); } - static ClassOrModuleRef Kernel() { + static ClassOrModuleRef BasicObject() { return ClassOrModuleRef::fromRaw(20); } - static ClassOrModuleRef Range() { + static ClassOrModuleRef Kernel() { return ClassOrModuleRef::fromRaw(21); } - static ClassOrModuleRef Regexp() { + static ClassOrModuleRef Range() { return ClassOrModuleRef::fromRaw(22); } - static ClassOrModuleRef Magic() { + static ClassOrModuleRef Regexp() { return ClassOrModuleRef::fromRaw(23); } - static ClassOrModuleRef MagicSingleton() { + static ClassOrModuleRef Magic() { return ClassOrModuleRef::fromRaw(24); } - static ClassOrModuleRef Module() { + static ClassOrModuleRef MagicSingleton() { return ClassOrModuleRef::fromRaw(25); } - static ClassOrModuleRef StandardError() { + static ClassOrModuleRef Module() { return ClassOrModuleRef::fromRaw(26); } - static ClassOrModuleRef Complex() { + static ClassOrModuleRef Exception() { return ClassOrModuleRef::fromRaw(27); } - static ClassOrModuleRef Rational() { + static ClassOrModuleRef StandardError() { return ClassOrModuleRef::fromRaw(28); } - static ClassOrModuleRef T_Array() { + static ClassOrModuleRef Complex() { return ClassOrModuleRef::fromRaw(29); } - static ClassOrModuleRef T_Hash() { + static ClassOrModuleRef Rational() { return ClassOrModuleRef::fromRaw(30); } - static ClassOrModuleRef T_Proc() { + static ClassOrModuleRef T_Array() { return ClassOrModuleRef::fromRaw(31); } - static ClassOrModuleRef Proc() { + static ClassOrModuleRef T_Hash() { return ClassOrModuleRef::fromRaw(32); } - static ClassOrModuleRef Enumerable() { + static ClassOrModuleRef T_Proc() { return ClassOrModuleRef::fromRaw(33); } - static ClassOrModuleRef Set() { + static ClassOrModuleRef Proc() { return ClassOrModuleRef::fromRaw(34); } - static ClassOrModuleRef Struct() { + static ClassOrModuleRef Enumerable() { return ClassOrModuleRef::fromRaw(35); } - static ClassOrModuleRef File() { + static ClassOrModuleRef Set() { return ClassOrModuleRef::fromRaw(36); } - static ClassOrModuleRef Sorbet() { + static ClassOrModuleRef Struct() { return ClassOrModuleRef::fromRaw(37); } - static ClassOrModuleRef Sorbet_Private() { + static ClassOrModuleRef File() { return ClassOrModuleRef::fromRaw(38); } - static ClassOrModuleRef Sorbet_Private_Static() { + static ClassOrModuleRef Sorbet() { return ClassOrModuleRef::fromRaw(39); } - static ClassOrModuleRef Sorbet_Private_StaticSingleton() { + static ClassOrModuleRef Sorbet_Private() { return ClassOrModuleRef::fromRaw(40); } + static ClassOrModuleRef Sorbet_Private_Static() { + return ClassOrModuleRef::fromRaw(41); + } + + static ClassOrModuleRef Sorbet_Private_StaticSingleton() { + return ClassOrModuleRef::fromRaw(42); + } + // Used as the superclass for symbols created to populate unresolvable ruby // constants static ClassOrModuleRef StubModule() { - return ClassOrModuleRef::fromRaw(41); + return ClassOrModuleRef::fromRaw(43); } // Used to mark the presence of a mixin that we were unable to // statically resolve to a module static ClassOrModuleRef StubMixin() { - return ClassOrModuleRef::fromRaw(42); + return ClassOrModuleRef::fromRaw(44); } // Used to mark the presence of a mixin that will be replaced with a real // ClassOrModuleRef or StubMixin once resolution completes. static ClassOrModuleRef PlaceholderMixin() { - return ClassOrModuleRef::fromRaw(43); + return ClassOrModuleRef::fromRaw(45); } // Used to mark the presence of a superclass that we were unable to // statically resolve to a class static ClassOrModuleRef StubSuperClass() { - return ClassOrModuleRef::fromRaw(44); + return ClassOrModuleRef::fromRaw(46); } static ClassOrModuleRef T_Enumerable() { - return ClassOrModuleRef::fromRaw(45); + return ClassOrModuleRef::fromRaw(47); } static ClassOrModuleRef T_Range() { - return ClassOrModuleRef::fromRaw(46); + return ClassOrModuleRef::fromRaw(48); } static ClassOrModuleRef T_Set() { - return ClassOrModuleRef::fromRaw(47); + return ClassOrModuleRef::fromRaw(49); } static ClassOrModuleRef void_() { - return ClassOrModuleRef::fromRaw(48); + return ClassOrModuleRef::fromRaw(50); } // Synthetic symbol used by resolver to mark type alias assignments. static ClassOrModuleRef typeAliasTemp() { - return ClassOrModuleRef::fromRaw(49); + return ClassOrModuleRef::fromRaw(51); } static ClassOrModuleRef T_Configuration() { - return ClassOrModuleRef::fromRaw(50); + return ClassOrModuleRef::fromRaw(52); } static ClassOrModuleRef T_Generic() { - return ClassOrModuleRef::fromRaw(51); + return ClassOrModuleRef::fromRaw(53); } static ClassOrModuleRef Tuple() { - return ClassOrModuleRef::fromRaw(52); + return ClassOrModuleRef::fromRaw(54); } static ClassOrModuleRef Shape() { - return ClassOrModuleRef::fromRaw(53); + return ClassOrModuleRef::fromRaw(55); } static ClassOrModuleRef Subclasses() { - return ClassOrModuleRef::fromRaw(54); + return ClassOrModuleRef::fromRaw(56); } static ClassOrModuleRef Sorbet_Private_Static_ImplicitModuleSuperClass() { - return ClassOrModuleRef::fromRaw(55); + return ClassOrModuleRef::fromRaw(57); } static ClassOrModuleRef Sorbet_Private_Static_ReturnTypeInference() { - return ClassOrModuleRef::fromRaw(56); + return ClassOrModuleRef::fromRaw(58); } static MethodRef noMethod() { @@ -847,7 +866,7 @@ class Symbols { } static ClassOrModuleRef T_Sig() { - return ClassOrModuleRef::fromRaw(57); + return ClassOrModuleRef::fromRaw(59); } static FieldRef Magic_undeclaredFieldStub() { @@ -859,55 +878,59 @@ class Symbols { } static ClassOrModuleRef T_Helpers() { - return ClassOrModuleRef::fromRaw(58); + return ClassOrModuleRef::fromRaw(60); } static ClassOrModuleRef DeclBuilderForProcs() { - return ClassOrModuleRef::fromRaw(59); + return ClassOrModuleRef::fromRaw(61); } static ClassOrModuleRef DeclBuilderForProcsSingleton() { - return ClassOrModuleRef::fromRaw(60); + return ClassOrModuleRef::fromRaw(62); } static ClassOrModuleRef Net() { - return ClassOrModuleRef::fromRaw(61); + return ClassOrModuleRef::fromRaw(63); } static ClassOrModuleRef Net_IMAP() { - return ClassOrModuleRef::fromRaw(62); + return ClassOrModuleRef::fromRaw(64); } static ClassOrModuleRef Net_Protocol() { - return ClassOrModuleRef::fromRaw(63); + return ClassOrModuleRef::fromRaw(65); } static ClassOrModuleRef T_Sig_WithoutRuntime() { - return ClassOrModuleRef::fromRaw(64); + return ClassOrModuleRef::fromRaw(66); } static ClassOrModuleRef Enumerator() { - return ClassOrModuleRef::fromRaw(65); + return ClassOrModuleRef::fromRaw(67); } static ClassOrModuleRef T_Enumerator() { - return ClassOrModuleRef::fromRaw(66); + return ClassOrModuleRef::fromRaw(68); } static ClassOrModuleRef T_Enumerator_Lazy() { - return ClassOrModuleRef::fromRaw(67); + return ClassOrModuleRef::fromRaw(69); + } + + static ClassOrModuleRef T_Enumerator_Chain() { + return ClassOrModuleRef::fromRaw(70); } static ClassOrModuleRef T_Struct() { - return ClassOrModuleRef::fromRaw(68); + return ClassOrModuleRef::fromRaw(71); } static ClassOrModuleRef Singleton() { - return ClassOrModuleRef::fromRaw(69); + return ClassOrModuleRef::fromRaw(72); } static ClassOrModuleRef T_Enum() { - return ClassOrModuleRef::fromRaw(70); + return ClassOrModuleRef::fromRaw(73); } static MethodRef sig() { @@ -915,39 +938,43 @@ class Symbols { } static ClassOrModuleRef Enumerator_Lazy() { - return ClassOrModuleRef::fromRaw(71); + return ClassOrModuleRef::fromRaw(74); + } + + static ClassOrModuleRef Enumerator_Chain() { + return ClassOrModuleRef::fromRaw(75); } static ClassOrModuleRef T_Private() { - return ClassOrModuleRef::fromRaw(72); + return ClassOrModuleRef::fromRaw(76); } static ClassOrModuleRef T_Private_Types() { - return ClassOrModuleRef::fromRaw(73); + return ClassOrModuleRef::fromRaw(77); } static ClassOrModuleRef T_Private_Types_Void() { - return ClassOrModuleRef::fromRaw(74); + return ClassOrModuleRef::fromRaw(78); } static ClassOrModuleRef T_Private_Types_Void_VOID() { - return ClassOrModuleRef::fromRaw(75); + return ClassOrModuleRef::fromRaw(79); } static ClassOrModuleRef T_Private_Types_Void_VOIDSingleton() { - return ClassOrModuleRef::fromRaw(76); + return ClassOrModuleRef::fromRaw(80); } static ClassOrModuleRef T_Private_Methods() { - return ClassOrModuleRef::fromRaw(77); + return ClassOrModuleRef::fromRaw(81); } static ClassOrModuleRef T_Private_Methods_DeclBuilder() { - return ClassOrModuleRef::fromRaw(78); + return ClassOrModuleRef::fromRaw(82); } static ClassOrModuleRef T_Sig_WithoutRuntimeSingleton() { - return ClassOrModuleRef::fromRaw(79); + return ClassOrModuleRef::fromRaw(83); } static MethodRef sigWithoutRuntime() { @@ -955,7 +982,7 @@ class Symbols { } static ClassOrModuleRef T_NonForcingConstants() { - return ClassOrModuleRef::fromRaw(80); + return ClassOrModuleRef::fromRaw(84); } static MethodRef SorbetPrivateStaticSingleton_sig() { @@ -963,15 +990,15 @@ class Symbols { } static ClassOrModuleRef PackageSpecRegistry() { - return ClassOrModuleRef::fromRaw(81); + return ClassOrModuleRef::fromRaw(85); } static ClassOrModuleRef PackageSpec() { - return ClassOrModuleRef::fromRaw(82); + return ClassOrModuleRef::fromRaw(86); } static ClassOrModuleRef PackageSpecSingleton() { - return ClassOrModuleRef::fromRaw(83); + return ClassOrModuleRef::fromRaw(87); } static MethodRef PackageSpec_import() { @@ -991,11 +1018,11 @@ class Symbols { } static ClassOrModuleRef Encoding() { - return ClassOrModuleRef::fromRaw(84); + return ClassOrModuleRef::fromRaw(88); } static ClassOrModuleRef Thread() { - return ClassOrModuleRef::fromRaw(85); + return ClassOrModuleRef::fromRaw(89); } static MethodRef Class_new() { @@ -1014,36 +1041,52 @@ class Symbols { return MethodRef::fromRaw(13); } + static MethodRef PackageSpec_visible_to() { + return MethodRef::fromRaw(14); + } + + static MethodRef PackageSpec_export_all() { + return MethodRef::fromRaw(15); + } + static ClassOrModuleRef Sorbet_Private_Static_ResolvedSig() { - return ClassOrModuleRef::fromRaw(86); + return ClassOrModuleRef::fromRaw(90); } static ClassOrModuleRef Sorbet_Private_Static_ResolvedSigSingleton() { - return ClassOrModuleRef::fromRaw(87); + return ClassOrModuleRef::fromRaw(91); } static ClassOrModuleRef T_Private_Compiler() { - return ClassOrModuleRef::fromRaw(88); + return ClassOrModuleRef::fromRaw(92); } static ClassOrModuleRef T_Private_CompilerSingleton() { - return ClassOrModuleRef::fromRaw(89); + return ClassOrModuleRef::fromRaw(93); } static ClassOrModuleRef MagicBindToAttachedClass() { - return ClassOrModuleRef::fromRaw(90); + return ClassOrModuleRef::fromRaw(94); } static ClassOrModuleRef MagicBindToSelfType() { - return ClassOrModuleRef::fromRaw(91); + return ClassOrModuleRef::fromRaw(95); } static ClassOrModuleRef T_Types() { - return ClassOrModuleRef::fromRaw(92); + return ClassOrModuleRef::fromRaw(96); } static ClassOrModuleRef T_Types_Base() { - return ClassOrModuleRef::fromRaw(93); + return ClassOrModuleRef::fromRaw(97); + } + + static ClassOrModuleRef Data() { + return ClassOrModuleRef::fromRaw(98); + } + + static ClassOrModuleRef T_Class() { + return ClassOrModuleRef::fromRaw(99); } static constexpr int MAX_PROC_ARITY = 10; @@ -1068,11 +1111,11 @@ class Symbols { return ClassOrModuleRef::fromRaw(MAX_SYNTHETIC_CLASS_SYMBOLS - 1); } - static constexpr int MAX_SYNTHETIC_CLASS_SYMBOLS = 207; - static constexpr int MAX_SYNTHETIC_METHOD_SYMBOLS = 48; - static constexpr int MAX_SYNTHETIC_FIELD_SYMBOLS = 4; - static constexpr int MAX_SYNTHETIC_TYPEARGUMENT_SYMBOLS = 4; - static constexpr int MAX_SYNTHETIC_TYPEMEMBER_SYMBOLS = 105; + static const int MAX_SYNTHETIC_CLASS_SYMBOLS; + static const int MAX_SYNTHETIC_METHOD_SYMBOLS; + static const int MAX_SYNTHETIC_FIELD_SYMBOLS; + static const int MAX_SYNTHETIC_TYPEARGUMENT_SYMBOLS; + static const int MAX_SYNTHETIC_TYPEMEMBER_SYMBOLS; }; template H AbslHashValue(H h, const SymbolRef &m) { diff --git a/core/Symbols.cc b/core/Symbols.cc index d3dc568430..a840ac9d6e 100644 --- a/core/Symbols.cc +++ b/core/Symbols.cc @@ -4,8 +4,8 @@ #include "absl/strings/str_replace.h" #include "common/JSON.h" #include "common/Levenstein.h" -#include "common/formatting.h" -#include "common/sort.h" +#include "common/sort/sort.h" +#include "common/strings/formatting.h" #include "core/Context.h" #include "core/GlobalState.h" #include "core/Names.h" @@ -22,6 +22,12 @@ namespace sorbet::core { using namespace std; +const int Symbols::MAX_SYNTHETIC_CLASS_SYMBOLS = 213; +const int Symbols::MAX_SYNTHETIC_METHOD_SYMBOLS = 49; +const int Symbols::MAX_SYNTHETIC_FIELD_SYMBOLS = 4; +const int Symbols::MAX_SYNTHETIC_TYPEARGUMENT_SYMBOLS = 4; +const int Symbols::MAX_SYNTHETIC_TYPEMEMBER_SYMBOLS = 73; + namespace { constexpr string_view COLON_SEPARATOR = "::"sv; constexpr string_view HASH_SEPARATOR = "#"sv; @@ -131,34 +137,23 @@ TypePtr ClassOrModule::unsafeComputeExternalType(GlobalState &gs) { vector targs; targs.reserve(typeMembers().size()); - // Special-case covariant stdlib generics to have their types - // defaulted to `T.untyped`. This set *should not* grow over time. - bool isStdlibGeneric = ref == core::Symbols::Hash() || ref == core::Symbols::Array() || - ref == core::Symbols::Set() || ref == core::Symbols::Range() || - ref == core::Symbols::Enumerable() || ref == core::Symbols::Enumerator() || - ref == core::Symbols::Enumerator_Lazy(); - for (auto &tm : typeMembers()) { auto tmData = tm.data(gs); auto *lambdaParam = cast_type(tmData->resultType); ENFORCE(lambdaParam != nullptr); - if (isStdlibGeneric) { - // For backwards compatibility, instantiate stdlib generics - // with T.untyped. + if (ref.isLegacyStdlibGeneric()) { + // Instantiate certain covariant stdlib generics with T.untyped, instead of targs.emplace_back(Types::untyped(gs, ref)); } else if (tmData->flags.isFixed || tmData->flags.isCovariant) { - // Default fixed or covariant parameters to their upper - // bound. + // Default fixed or covariant parameters to their upper bound. targs.emplace_back(lambdaParam->upperBound); } else if (tmData->flags.isInvariant) { - // We instantiate Invariant type members as T.untyped as - // this will behave a bit like a unification variable with - // Types::glb. + // We instantiate Invariant type members as T.untyped as this will behave a bit like + // a unification variable with Types::glb. targs.emplace_back(Types::untyped(gs, ref)); } else { - // The remaining case is a contravariant parameter, which - // gets defaulted to its lower bound. + // The remaining case is a contravariant parameter, which gets defaulted to its lower bound. targs.emplace_back(lambdaParam->lowerBound); } } @@ -226,6 +221,19 @@ bool SymbolRef::isStaticField(const GlobalState &gs) const { return isFieldOrStaticField() && asFieldRef().dataAllowingNone(gs)->flags.isStaticField; } +bool SymbolRef::isClassAlias(const GlobalState &gs) const { + if (!isFieldOrStaticField()) { + return false; + } + + const auto &data = asFieldRef().dataAllowingNone(gs); + if (!data->flags.isStaticField) { + return false; + } + + return data->isClassAlias(); +} + ClassOrModuleData ClassOrModuleRef::dataAllowingNone(GlobalState &gs) const { ENFORCE_NO_TIMER(_id < gs.classAndModulesUsed()); return ClassOrModuleData(gs.classAndModules[_id], gs); @@ -426,9 +434,12 @@ string TypeArgumentRef::show(const GlobalState &gs, ShowOptions options) const { string TypeMemberRef::show(const GlobalState &gs, ShowOptions options) const { auto sym = data(gs); if (sym->name == core::Names::Constants::AttachedClass()) { - auto attached = sym->owner.asClassOrModuleRef().data(gs)->attachedClass(gs); - ENFORCE(attached.exists()); - if (options.showForRBI) { + auto owner = sym->owner.asClassOrModuleRef(); + auto attached = owner.data(gs)->attachedClass(gs); + if (options.showForRBI || !attached.exists()) { + // Attached wont exist for a number of cases: + // - owner is a module that doesn't use has_attached_class! + // - owner is a singleton class of a module return "T.attached_class"; } return fmt::format("T.attached_class (of {})", attached.show(gs, options)); @@ -565,6 +576,42 @@ MethodRef ClassOrModule::findMethodTransitive(const GlobalState &gs, NameRef nam return Symbols::noMethod(); } +bool singleFileDefinition(const GlobalState &gs, const core::SymbolRef::LOC_store &locs, core::FileRef file) { + bool result = false; + + for (auto &loc : locs) { + if (loc.file().data(gs).isRBI()) { + continue; + } + + if (loc.file() != file) { + return false; + } + + result = true; + } + + return result; +} + +// Returns true if the given symbol is only defined in a given file (not accounting for RBIs). +bool SymbolRef::isOnlyDefinedInFile(const GlobalState &gs, core::FileRef file) const { + if (file.data(gs).isRBI()) { + return false; + } + + return singleFileDefinition(gs, locs(gs), file); +} + +// Returns true if the given class/module is only defined in a given file (not accounting for RBIs). +bool ClassOrModuleRef::isOnlyDefinedInFile(const GlobalState &gs, core::FileRef file) const { + if (file.data(gs).isRBI()) { + return false; + } + + return singleFileDefinition(gs, data(gs)->locs(), file); +} + // Documented in SymbolRef.h bool ClassOrModuleRef::isPackageSpecSymbol(const GlobalState &gs) const { auto sym = *this; @@ -581,8 +628,9 @@ bool ClassOrModuleRef::isPackageSpecSymbol(const GlobalState &gs) const { bool ClassOrModuleRef::isBuiltinGenericForwarder() const { return *this == Symbols::T_Hash() || *this == Symbols::T_Array() || *this == Symbols::T_Set() || - *this == Symbols::T_Range() || *this == Symbols::T_Enumerable() || *this == Symbols::T_Enumerator() || - *this == Symbols::T_Enumerator_Lazy(); + *this == Symbols::T_Range() || *this == Symbols::T_Class() || *this == Symbols::T_Enumerable() || + *this == Symbols::T_Enumerator() || *this == Symbols::T_Enumerator_Lazy() || + *this == Symbols::T_Enumerator_Chain(); } ClassOrModuleRef ClassOrModuleRef::maybeUnwrapBuiltinGenericForwarder() const { @@ -596,10 +644,14 @@ ClassOrModuleRef ClassOrModuleRef::maybeUnwrapBuiltinGenericForwarder() const { return Symbols::Enumerator(); } else if (*this == Symbols::T_Enumerator_Lazy()) { return Symbols::Enumerator_Lazy(); + } else if (*this == Symbols::T_Enumerator_Chain()) { + return Symbols::Enumerator_Chain(); } else if (*this == Symbols::T_Range()) { return Symbols::Range(); } else if (*this == Symbols::T_Set()) { return Symbols::Set(); + } else if (*this == Symbols::T_Class()) { + return Symbols::Class(); } else { return *this; } @@ -616,10 +668,14 @@ ClassOrModuleRef ClassOrModuleRef::forwarderForBuiltinGeneric() const { return Symbols::T_Enumerator(); } else if (*this == Symbols::Enumerator_Lazy()) { return Symbols::T_Enumerator_Lazy(); + } else if (*this == Symbols::Enumerator_Chain()) { + return Symbols::T_Enumerator_Chain(); } else if (*this == Symbols::Range()) { return Symbols::T_Range(); } else if (*this == Symbols::Set()) { return Symbols::T_Set(); + } else if (*this == Symbols::Class()) { + return Symbols::T_Class(); } else { return Symbols::noClassOrModule(); } @@ -630,7 +686,7 @@ ClassOrModuleRef ClassOrModuleRef::forwarderForBuiltinGeneric() const { bool ClassOrModuleRef::isLegacyStdlibGeneric() const { return *this == Symbols::Hash() || *this == Symbols::Array() || *this == Symbols::Set() || *this == Symbols::Range() || *this == Symbols::Enumerable() || *this == Symbols::Enumerator() || - *this == Symbols::Enumerator_Lazy(); + *this == Symbols::Enumerator_Lazy() || *this == Symbols::Enumerator_Chain(); } namespace { @@ -785,6 +841,28 @@ vector ClassOrModule::findMemberFuzzyMatch(con return res; } +bool SymbolRef::isUnderNamespace(const GlobalState &gs, ClassOrModuleRef otherClass) const { + if (isClassOrModule() && otherClass == asClassOrModuleRef()) { + return true; + } + + // if we are checking for nesting under root itself, which is always true + if (otherClass == core::Symbols::root()) { + return true; + } + + auto curOwner = owner(gs).asClassOrModuleRef(); + while (curOwner != core::Symbols::root()) { + if (curOwner == otherClass) { + return true; + } + + curOwner = curOwner.data(gs)->owner; + } + + return false; +} + vector ClassOrModule::findMemberFuzzyMatchConstant(const GlobalState &gs, NameRef name, int betterThan) const { // Performance of this method is bad, to say the least. @@ -794,6 +872,7 @@ ClassOrModule::findMemberFuzzyMatchConstant(const GlobalState &gs, NameRef name, // - best candidate per every outer scope if it's better than all the candidates in inner scope // - globally best candidate in ALL scopes. vector result; + FuzzySearchResult best; best.symbol = Symbols::noSymbol(); best.name = NameRef::noName(); @@ -1050,7 +1129,7 @@ string toStringFullNameInternal(const GlobalState &gs, core::SymbolRef owner, co string_view separator) { bool includeOwner = owner.exists() && owner != Symbols::root(); string ownerStr = includeOwner ? owner.toStringFullName(gs) : ""; - return absl::StrCat(ownerStr, includeOwner ? separator : "", name.showRaw(gs)); // TODO(jez) includeOwner required? + return absl::StrCat(ownerStr, includeOwner ? separator : "", name.showRaw(gs)); } } // namespace @@ -1678,24 +1757,30 @@ ClassOrModuleRef ClassOrModule::singletonClass(GlobalState &gs) { } ClassOrModuleRef selfRef = this->ref(gs); - // avoid using `this` after the call to gs.enterTypeMember - auto selfLoc = this->loc(); - NameRef singletonName = gs.freshNameUnique(UniqueNameKind::Singleton, this->name, 1); singleton = gs.enterClassSymbol(this->loc(), this->owner, singletonName); ClassOrModuleData singletonInfo = singleton.data(gs); + // -------- + // Call to enterClassSymbol might have reallocated the memory that `*this` pointed to + // It's not safe to use `this` anymore. + // -------- + const auto &self = selfRef.data(gs); + singletonInfo->members()[Names::attached()] = selfRef; singletonInfo->setSuperClass(Symbols::todo()); singletonInfo->setIsModule(false); - auto tp = gs.enterTypeMember(selfLoc, singleton, Names::Constants::AttachedClass(), Variance::CoVariant); + ENFORCE(self->isClassModuleSet(), "{}", selfRef.show(gs)); + if (self->isClass()) { + auto tp = gs.enterTypeMember(self->loc(), singleton, Names::Constants::AttachedClass(), Variance::CoVariant); - // Initialize the bounds of AttachedClass as todo, as they will be updated - // to the externalType of the attached class for the upper bound, and bottom - // for the lower bound in the ResolveSignaturesWalk pass of the resolver. - auto todo = make_type(Symbols::todo()); - tp.data(gs)->resultType = make_type(tp, todo, todo); + // Initialize the bounds of AttachedClass as todo, as they will be updated + // to the externalType of the attached class for the upper bound, and bottom + // for the lower bound in the ResolveSignaturesWalk pass of the resolver. + auto todo = make_type(Symbols::todo()); + tp.data(gs)->resultType = make_type(tp, todo, todo); + } selfRef.data(gs)->members()[Names::singleton()] = singleton; return singleton; @@ -1735,23 +1820,24 @@ ClassOrModuleRef ClassOrModule::topAttachedClass(const GlobalState &gs) const { return classSymbol; } -void ClassOrModule::recordSealedSubclass(MutableContext ctx, ClassOrModuleRef subclass) { - ENFORCE(this->flags.isSealed, "Class is not marked sealed: {}", ref(ctx).show(ctx)); - ENFORCE(subclass.exists(), "Can't record sealed subclass for {} when subclass doesn't exist", ref(ctx).show(ctx)); +void ClassOrModule::recordSealedSubclass(GlobalState &gs, ClassOrModuleRef subclass) { + ENFORCE(this->flags.isSealed, "Class is not marked sealed: {}", ref(gs).show(gs)); + ENFORCE(subclass.exists(), "Can't record sealed subclass for {} when subclass doesn't exist", ref(gs).show(gs)); // Avoid using a clobbered `this` pointer, as `singletonClass` can cause the symbol table to move. - ClassOrModuleRef selfRef = this->ref(ctx); - - // We record sealed subclasses on a magical method called core::Names::sealedSubclasses(). This is so we don't - // bloat the `sizeof class Symbol` with an extra field that most class sybmols will never use. - // Note: We had hoped to ALSO implement this method in the runtime, but we couldn't think of a way to make it work - // that didn't require running with the help of Stripe's autoloader, specifically because we might want to allow - // subclassing a sealed class across multiple files, not just one file. - auto classOfSubclass = subclass.data(ctx)->singletonClass(ctx); + ClassOrModuleRef selfRef = this->ref(gs); + + // We record sealed subclasses on the method called core::Names::sealedSubclasses(). + // + // This is so we don't bloat the `sizeof ClassOrModule` with an extra field that all non-sealed + // classes will never use. + // + // Note: this method actually exists at runtime as well--the name is not magic. + auto classOfSubclass = subclass.data(gs)->singletonClass(gs); auto sealedSubclasses = - selfRef.data(ctx)->lookupSingletonClass(ctx).data(ctx)->findMethod(ctx, core::Names::sealedSubclasses()); + selfRef.data(gs)->lookupSingletonClass(gs).data(gs)->findMethod(gs, core::Names::sealedSubclasses()); - auto data = sealedSubclasses.data(ctx); + auto data = sealedSubclasses.data(gs); ENFORCE(data->resultType != nullptr, "Should have been populated in namer"); auto appliedType = cast_type(data->resultType); ENFORCE(appliedType != nullptr, "sealedSubclasses should always be AppliedType"); @@ -1761,20 +1847,37 @@ void ClassOrModule::recordSealedSubclass(MutableContext ctx, ClassOrModuleRef su const TypePtr *iter = ¤tClasses; const OrType *orT = nullptr; while ((orT = cast_type(*iter))) { - auto right = cast_type_nonnull(orT->right); - ENFORCE(left); - if (right.symbol == classOfSubclass) { + core::ClassOrModuleRef subclass; + if (auto *right = cast_type(orT->right)) { + subclass = right->klass; + } else if (isa_type(orT->right)) { + auto right = cast_type_nonnull(orT->right); + subclass = right.symbol; + } else { + ENFORCE(false, "Unexpected type in sealedSubclasses!") + } + if (subclass == classOfSubclass) { return; } iter = &orT->left; } - if (cast_type_nonnull(*iter).symbol == classOfSubclass) { + + core::ClassOrModuleRef lastClass; + if (auto *lastType = cast_type(*iter)) { + lastClass = lastType->klass.data(gs)->attachedClass(gs); + } else if (isa_type(*iter)) { + auto lastType = cast_type_nonnull(*iter); + lastClass = lastType.symbol.data(gs)->attachedClass(gs); + } else { + ENFORCE(false, "Last element of sealedSubclasses must be AppliedType") + } + if (lastClass == classOfSubclass) { return; } if (currentClasses != core::Types::bottom()) { - appliedType->targs[0] = OrType::make_shared(currentClasses, make_type(classOfSubclass)); + appliedType->targs[0] = OrType::make_shared(currentClasses, classOfSubclass.data(gs)->externalType()); } else { - appliedType->targs[0] = make_type(classOfSubclass); + appliedType->targs[0] = classOfSubclass.data(gs)->externalType(); } } @@ -1793,11 +1896,11 @@ TypePtr ClassOrModule::sealedSubclassesToUnion(const GlobalState &gs) const { auto data = sealedSubclasses.data(gs); ENFORCE(data->resultType != nullptr, "Should have been populated in namer"); - auto appliedType = cast_type(data->resultType); - ENFORCE(appliedType != nullptr, "sealedSubclasses should always be AppliedType"); - ENFORCE(appliedType->klass == core::Symbols::Set(), "sealedSubclasses should always be Set"); + auto setAppliedType = cast_type(data->resultType); + ENFORCE(setAppliedType != nullptr, "sealedSubclasses should always be AppliedType"); + ENFORCE(setAppliedType->klass == core::Symbols::Set(), "sealedSubclasses should always be Set"); - auto currentClasses = appliedType->targs[0]; + auto currentClasses = setAppliedType->targs[0]; if (currentClasses.isBottom()) { // Declared sealed parent class, but never saw any children. return Types::bottom(); @@ -1805,17 +1908,30 @@ TypePtr ClassOrModule::sealedSubclassesToUnion(const GlobalState &gs) const { auto result = Types::bottom(); while (auto orType = cast_type(currentClasses)) { - ENFORCE(isa_type(orType->right), "Something in sealedSubclasses that's not a ClassType"); - auto classType = cast_type_nonnull(orType->right); - auto subclass = classType.symbol.data(gs)->attachedClass(gs); + core::ClassOrModuleRef subclass; + if (auto *right = cast_type(orType->right)) { + subclass = right->klass.data(gs)->attachedClass(gs); + } else if (isa_type(orType->right)) { + auto right = cast_type_nonnull(orType->right); + subclass = right.symbol.data(gs)->attachedClass(gs); + } else { + ENFORCE(false, "Unexpected type in sealedSubclasses!") + } + ENFORCE(subclass.exists()); result = Types::any(gs, subclass.data(gs)->externalType(), result); currentClasses = orType->left; } - ENFORCE(isa_type(currentClasses), "Last element of sealedSubclasses must be ClassType"); - auto lastClassType = cast_type_nonnull(currentClasses); - auto subclass = lastClassType.symbol.data(gs)->attachedClass(gs); + core::ClassOrModuleRef subclass; + if (auto *lastType = cast_type(currentClasses)) { + subclass = lastType->klass.data(gs)->attachedClass(gs); + } else if (isa_type(currentClasses)) { + auto lastType = cast_type_nonnull(currentClasses); + subclass = lastType.symbol.data(gs)->attachedClass(gs); + } else { + ENFORCE(false, "Last element of sealedSubclasses must be AppliedType") + } ENFORCE(subclass.exists()); result = Types::any(gs, subclass.data(gs)->externalType(), result); @@ -2262,14 +2378,13 @@ ClassOrModuleRef SymbolRef::enclosingClass(const GlobalState &gs) const { uint32_t ClassOrModule::hash(const GlobalState &gs, bool skipTypeMemberNames) const { uint32_t result = _hash(name.shortName(gs)); - if (!gs.lspExperimentalFastPathEnabled) { - // resultType on a ClassOrModule is just externalType(), which is a function of this class - // (including singletons and attached classes) and its type members. If any of those things - // change, either they will be reflected elsewhere in the hash, or they don't need to be - // included in the hash at all. - result = mix(result, !this->resultType ? 0 : this->resultType.hash(gs)); - } - result = mix(result, this->flags.serialize()); + + // Bit of a hack to ensure that the isBehaviorDefining flag is not serialized. + // We do not want behavior changes to trigger the slow path. + auto flagsCopy = this->flags; + flagsCopy.isBehaviorDefining = false; + result = mix(result, std::move(flagsCopy).serialize()); + result = mix(result, this->owner.id()); result = mix(result, this->superClass_.id()); // argumentsOrMixins, typeParams, typeAliases @@ -2283,12 +2398,11 @@ uint32_t ClassOrModule::hash(const GlobalState &gs, bool skipTypeMemberNames) co continue; } - if (e.second.isMethod() && - (gs.lspExperimentalFastPathEnabled || e.second.asMethodRef().data(gs)->ignoreInHashing(gs))) { + if (e.second.isMethod()) { continue; } - if (gs.lspExperimentalFastPathEnabled && e.second.isFieldOrStaticField()) { + if (e.second.isFieldOrStaticField()) { const auto &field = e.second.asFieldRef().data(gs); if (field->flags.isStaticField && !field->isClassAlias()) { continue; @@ -2309,7 +2423,7 @@ uint32_t ClassOrModule::hash(const GlobalState &gs, bool skipTypeMemberNames) co } } - if (skipTypeMemberNames && gs.lspExperimentalFastPathEnabled && e.second.isTypeMember()) { + if (skipTypeMemberNames && e.second.isTypeMember()) { // skipTypeMemberNames is currently the difference between `hash` and `classOrModuleShapeHash` // (It felt wasteful to dupe this whole method.) Type member names have to be in the // full hash so that a change to a type member name causes the right downstream @@ -2327,7 +2441,11 @@ uint32_t ClassOrModule::hash(const GlobalState &gs, bool skipTypeMemberNames) co } fast_sort(membersToHash, [](const auto &a, const auto &b) -> bool { return a.rawId() < b.rawId(); }); for (auto member : membersToHash) { - result = mix(result, _hash(member.name(gs).shortName(gs))); + if (member.isTypeMember()) { + result = mix(result, member.asTypeMemberRef().data(gs)->hash(gs)); + } else { + result = mix(result, _hash(member.name(gs).shortName(gs))); + } } } for (const auto &e : mixins_) { @@ -2335,13 +2453,6 @@ uint32_t ClassOrModule::hash(const GlobalState &gs, bool skipTypeMemberNames) co result = mix(result, _hash(e.data(gs)->name.shortName(gs))); } } - if (!gs.lspExperimentalFastPathEnabled) { - for (const auto &e : typeMembers()) { - if (e.exists()) { - result = mix(result, _hash(e.data(gs)->name.shortName(gs))); - } - } - } return result; } diff --git a/core/Symbols.h b/core/Symbols.h index e7cb9e07dd..500aca0440 100644 --- a/core/Symbols.h +++ b/core/Symbols.h @@ -377,15 +377,17 @@ class ClassOrModule final { bool isFinal : 1; bool isSealed : 1; bool isPrivate : 1; - bool isUndeclared : 1; + bool isDeclared : 1; bool isExported : 1; + bool isBehaviorDefining : 1; - constexpr static uint16_t NUMBER_OF_FLAGS = 10; + constexpr static uint16_t NUMBER_OF_FLAGS = 11; constexpr static uint16_t VALID_BITS_MASK = (1 << NUMBER_OF_FLAGS) - 1; Flags() noexcept : isClass(false), isModule(false), isAbstract(false), isInterface(false), isLinearizationComputed(false), - isFinal(false), isSealed(false), isPrivate(false), isUndeclared(false), isExported(false) {} + isFinal(false), isSealed(false), isPrivate(false), isDeclared(false), isExported(false), + isBehaviorDefining(false) {} uint16_t serialize() const { // Can replace this with std::bit_cast in C++20 @@ -498,11 +500,16 @@ class ClassOrModule final { } } - inline bool isUndeclared() const { - if (!isClassModuleSet()) { - return true; + inline bool isDeclared() const { + return flags.isDeclared; + } + + inline void setDeclared() { + ENFORCE(isClassModuleSet()); + + if (!flags.isDeclared) { + flags.isDeclared = true; } - return flags.isUndeclared; } SymbolRef findMember(const GlobalState &gs, NameRef name) const; @@ -539,7 +546,7 @@ class ClassOrModule final { ClassOrModuleRef topAttachedClass(const GlobalState &gs) const; - void recordSealedSubclass(MutableContext ctx, ClassOrModuleRef subclass); + void recordSealedSubclass(GlobalState &gs, ClassOrModuleRef subclass); // Returns the locations that are allowed to subclass the sealed class. const SymbolRef::LOC_store &sealedLocs(const GlobalState &gs) const; diff --git a/core/TypeConstraint.cc b/core/TypeConstraint.cc index f9a35c23b5..d459717789 100644 --- a/core/TypeConstraint.cc +++ b/core/TypeConstraint.cc @@ -1,5 +1,5 @@ #include "core/TypeConstraint.h" -#include "common/formatting.h" +#include "common/strings/formatting.h" #include "core/GlobalState.h" #include "core/Symbols.h" @@ -142,7 +142,6 @@ TypePtr TypeConstraint::getInstantiation(TypeArgumentRef sym) const { } unique_ptr TypeConstraint::deepCopy() const { - ENFORCE(!wasSolved); auto res = make_unique(); res->lowerBounds = this->lowerBounds; res->upperBounds = this->upperBounds; diff --git a/core/TypeErrorDiagnostics.cc b/core/TypeErrorDiagnostics.cc index ece786b64c..78af95bd1e 100644 --- a/core/TypeErrorDiagnostics.cc +++ b/core/TypeErrorDiagnostics.cc @@ -1,5 +1,7 @@ #include "core/TypeErrorDiagnostics.h" +#include "absl/algorithm/container.h" #include "absl/strings/str_join.h" +#include "core/errors/infer.h" using namespace std; @@ -56,8 +58,9 @@ void TypeErrorDiagnostics::explainTypeMismatch(const GlobalState &gs, ErrorBuild // TODO(jez) Add more cases } -void TypeErrorDiagnostics::maybeAutocorrect(const GlobalState &gs, ErrorBuilder &e, Loc loc, TypeConstraint &constr, - const TypePtr &expectedType, const TypePtr &actualType) { +void TypeErrorDiagnostics::maybeAutocorrect(const GlobalState &gs, ErrorBuilder &e, Loc loc, + const TypeConstraint &constrOrig, const TypePtr &expectedType, + const TypePtr &actualType) { if (!loc.exists()) { return; } @@ -66,21 +69,22 @@ void TypeErrorDiagnostics::maybeAutocorrect(const GlobalState &gs, ErrorBuilder e.replaceWith(fmt::format("Wrap in `{}`", *gs.suggestUnsafe), loc, "{}({})", *gs.suggestUnsafe, loc.source(gs).value()); } else { + // Duplicate the current constraint, so that we don't record any additional constraints + // simply from trying to generate autocorrects, and then solve it so that each additional + // heuristic here doesn't interfere with later heuristics. + auto constr = constrOrig.deepCopy(); + if (!constr->solve(gs)) { + // Constraint already doesn't solve. This will be an error later in infer. + // For now, let's just give up on our type-driven autocorrects. + return; + } + auto withoutNil = Types::dropNil(gs, actualType); if (!withoutNil.isBottom() && - Types::isSubTypeUnderConstraint(gs, constr, withoutNil, expectedType, UntypedMode::AlwaysCompatible)) { + Types::isSubTypeUnderConstraint(gs, *constr, withoutNil, expectedType, UntypedMode::AlwaysCompatible)) { e.replaceWith("Wrap in `T.must`", loc, "T.must({})", loc.source(gs).value()); - } else if (Types::isSubTypeUnderConstraint(gs, constr, expectedType, Types::Boolean(), - UntypedMode::AlwaysCompatible)) { - if (core::isa_type(actualType)) { - auto classSymbol = core::cast_type_nonnull(actualType).symbol; - if (classSymbol.exists() && classSymbol.data(gs)->owner == core::Symbols::root() && - classSymbol.data(gs)->name == core::Names::Constants::Boolean()) { - e.replaceWith("Prepend `!!`", loc, "!!({})", loc.source(gs).value()); - } - } } else if (isa_type(actualType) && !isa_type(expectedType) && - core::Types::isSubTypeUnderConstraint(gs, constr, + core::Types::isSubTypeUnderConstraint(gs, *constr, core::Symbols::T_Types_Base().data(gs)->externalType(), expectedType, UntypedMode::AlwaysCompatible)) { e.replaceWith("Wrap in `T::Utils.coerce`", loc, "T::Utils.coerce({})", loc.source(gs).value()); @@ -88,8 +92,8 @@ void TypeErrorDiagnostics::maybeAutocorrect(const GlobalState &gs, ErrorBuilder } } -void TypeErrorDiagnostics::insertUntypedTypeArguments(const GlobalState &gs, ErrorBuilder &e, ClassOrModuleRef klass, - core::Loc replaceLoc) { +void TypeErrorDiagnostics::insertTypeArguments(const GlobalState &gs, ErrorBuilder &e, ClassOrModuleRef klass, + core::Loc replaceLoc) { // if we're looking at `Array`, we want the autocorrect to include `T::`, but we don't need to // if we're already looking at `T::Array` instead. klass = klass.maybeUnwrapBuiltinGenericForwarder(); @@ -105,12 +109,126 @@ void TypeErrorDiagnostics::insertUntypedTypeArguments(const GlobalState &gs, Err e.replaceWith("Add type arguments", loc, "{}[T.untyped, T.untyped]", typePrefixSym.show(gs)); } else { auto numTypeArgs = klass.data(gs)->typeArity(gs); + auto arg = (klass == Symbols::Class() || klass == Symbols::T_Class()) ? "T.anything" : "T.untyped"; vector untypeds; for (int i = 0; i < numTypeArgs; i++) { - untypeds.emplace_back("T.untyped"); + untypeds.emplace_back(arg); } e.replaceWith("Add type arguments", loc, "{}[{}]", typePrefixSym.show(gs), absl::StrJoin(untypeds, ", ")); } } } + +void TypeErrorDiagnostics::explainUntyped(const GlobalState &gs, ErrorBuilder &e, ErrorClass what, + const TypeAndOrigins &untyped, Loc originForUninitialized) { + e.addErrorSection(untyped.explainGot(gs, originForUninitialized)); + if (what == core::errors::Infer::UntypedValue) { + e.addErrorNote("Support for `{}` is minimal. Consider using `{}` instead.", "typed: strong", "typed: strict"); + } +} + +void TypeErrorDiagnostics::explainUntyped(const GlobalState &gs, ErrorBuilder &e, ErrorClass what, TypePtr untyped, + Loc origin, Loc originForUninitialized) { + auto untypedTpo = TypeAndOrigins{untyped, origin}; + e.addErrorSection(untypedTpo.explainGot(gs, originForUninitialized)); + if (what == core::errors::Infer::UntypedValue) { + e.addErrorNote("Support for `{}` is minimal. Consider using `{}` instead.", "typed: strong", "typed: strict"); + } +} + +namespace { +optional autocorrectEditForDSLMethod(const GlobalState &gs, Loc insertLoc, + string_view prefix, ClassOrModuleRef dslOwner, + string_view dsl, bool needsDslOwner) { + if (needsDslOwner) { + if (dsl == "") { + return core::AutocorrectSuggestion::Edit{ + insertLoc, + fmt::format("{}extend {}\n", prefix, dslOwner.show(gs)), + }; + } else { + return core::AutocorrectSuggestion::Edit{ + insertLoc, + fmt::format("{}extend {}\n{}{}\n", prefix, dslOwner.show(gs), prefix, dsl), + }; + } + } else if (dsl != "") { + return core::AutocorrectSuggestion::Edit{ + insertLoc, + fmt::format("{}{}\n", prefix, dsl), + }; + } else { + return nullopt; + } +} +} // namespace + +// dslOwner can be noClassOrModule() to simply unconditionally insert the `dsl` string +// dsl can be `""` to simply insert `extend {dslOwner}` if dslOwner is not already an ancestor +optional +TypeErrorDiagnostics::editForDSLMethod(const GlobalState &gs, FileRef fileToEdit, Loc defaultInsertLoc, + ClassOrModuleRef inWhatRef, ClassOrModuleRef dslOwner, string_view dsl) { + auto inWhat = inWhatRef.data(gs); + auto inWhatSingleton = inWhat->lookupSingletonClass(gs); + + auto needsDslOwner = false; + if (dslOwner.exists()) { + needsDslOwner = !inWhatSingleton.data(gs)->derivesFrom(gs, dslOwner); + } + + auto inCurrentFile = [&](const auto &loc) { return loc.file() == fileToEdit; }; + auto &classLocs = inWhat->locs(); + auto classLoc = absl::c_find_if(classLocs, inCurrentFile); + + if (classLoc == classLocs.end()) { + if ((inWhatRef == Symbols::root() || inWhatRef == Symbols::Object()) && defaultInsertLoc.exists()) { + // We don't put any locs on for performance (would have one loc for each file in the codebase) + // If they're writing methods at the top level, it's probably a small script. + // Just put the `extend` immediately above the sig. + + auto [sigStart, _sigEnd] = defaultInsertLoc.position(gs); + auto thisLineStart = core::Loc::Detail{sigStart.line, 1}; + auto thisLineLoc = core::Loc::fromDetails(gs, defaultInsertLoc.file(), thisLineStart, thisLineStart); + ENFORCE(thisLineLoc.has_value()); + auto [_, thisLinePadding] = thisLineLoc.value().findStartOfLine(gs); + + string prefix(thisLinePadding, ' '); + return autocorrectEditForDSLMethod(gs, thisLineLoc.value(), prefix, dslOwner, dsl, needsDslOwner); + } else { + return nullopt; + } + } + + auto [classStart, classEnd] = classLoc->position(gs); + + core::Loc::Detail thisLineStart = {classStart.line, 1}; + auto thisLineLoc = core::Loc::fromDetails(gs, classLoc->file(), thisLineStart, thisLineStart); + ENFORCE(thisLineLoc.has_value()); + auto [_, thisLinePadding] = thisLineLoc.value().findStartOfLine(gs); + + core::Loc::Detail nextLineStart = {classStart.line + 1, 1}; + auto nextLineLoc = core::Loc::fromDetails(gs, classLoc->file(), nextLineStart, nextLineStart); + if (!nextLineLoc.has_value()) { + return nullopt; + } + auto [replacementLoc, nextLinePadding] = nextLineLoc.value().findStartOfLine(gs); + + // Preserve the indentation of the line below us. + string prefix(max(thisLinePadding + 2, nextLinePadding), ' '); + return autocorrectEditForDSLMethod(gs, nextLineLoc.value(), prefix, dslOwner, dsl, needsDslOwner); +} + +void TypeErrorDiagnostics::maybeInsertDSLMethod(const GlobalState &gs, ErrorBuilder &e, FileRef fileToEdit, + Loc defaultInsertLoc, ClassOrModuleRef inWhatRef, + ClassOrModuleRef dslOwner, string_view dsl) { + auto edit = editForDSLMethod(gs, fileToEdit, defaultInsertLoc, inWhatRef, dslOwner, dsl); + if (!edit.has_value()) { + return; + } + + auto label = dsl == "" ? fmt::format("Add `extend {}`", dslOwner.show(gs)) : fmt::format("Insert `{}`", dsl); + + e.addAutocorrect(core::AutocorrectSuggestion{move(label), {move(edit.value())}}); +} + } // namespace sorbet::core diff --git a/core/TypeErrorDiagnostics.h b/core/TypeErrorDiagnostics.h index 5f770183bc..6c663deb14 100644 --- a/core/TypeErrorDiagnostics.h +++ b/core/TypeErrorDiagnostics.h @@ -1,6 +1,7 @@ #ifndef SORBET_TYPE_ERROR_DIAGNOSTICS_H #define SORBET_TYPE_ERROR_DIAGNOSTICS_H +#include "core/Context.h" #include "core/Error.h" #include "core/GlobalState.h" #include "core/TypeConstraint.h" @@ -16,14 +17,27 @@ class TypeErrorDiagnostics final { // (It should usually be a cfg::Send::argLocs element or cfg::Return::whatLoc) // // Statefully accumulates the autocorrect directly onto the provided `ErrorBuilder`. - static void maybeAutocorrect(const GlobalState &gs, ErrorBuilder &e, Loc loc, TypeConstraint &constr, + static void maybeAutocorrect(const GlobalState &gs, ErrorBuilder &e, Loc loc, const TypeConstraint &constr, const TypePtr &expectedType, const TypePtr &actualType); static void explainTypeMismatch(const GlobalState &gs, ErrorBuilder &e, const TypePtr &expected, const TypePtr &got); - static void insertUntypedTypeArguments(const GlobalState &gs, ErrorBuilder &e, ClassOrModuleRef klass, - core::Loc replaceLoc); + static void insertTypeArguments(const GlobalState &gs, ErrorBuilder &e, ClassOrModuleRef klass, + core::Loc replaceLoc); + + static void explainUntyped(const GlobalState &gs, ErrorBuilder &e, ErrorClass what, const TypeAndOrigins &untyped, + Loc originForUninitialized); + + static void explainUntyped(const GlobalState &gs, ErrorBuilder &e, ErrorClass what, TypePtr untyped, Loc origin, + Loc originForUninitialized); + + static std::optional + editForDSLMethod(const GlobalState &gs, FileRef fileToEdit, Loc defaultInsertLoc, ClassOrModuleRef inWhat, + ClassOrModuleRef dslOwner, std::string_view dsl); + + static void maybeInsertDSLMethod(const GlobalState &gs, ErrorBuilder &e, FileRef fileToEdit, Loc defaultInsertLoc, + ClassOrModuleRef inWhat, ClassOrModuleRef dslOwner, std::string_view dsl); }; } // namespace sorbet::core diff --git a/core/Types.h b/core/Types.h index 16b532d782..80954ffb0d 100644 --- a/core/Types.h +++ b/core/Types.h @@ -3,7 +3,7 @@ #include "absl/base/casts.h" #include "absl/types/span.h" -#include "common/Counters.h" +#include "common/counters/Counters.h" #include "core/Context.h" #include "core/Error.h" #include "core/ParsedArg.h" @@ -113,7 +113,6 @@ class Types final { static TypePtr hashOfUntyped(); static TypePtr procClass(); static TypePtr nilableProcClass(); - static TypePtr classClass(); static TypePtr declBuilderForProcsSingletonClass(); static TypePtr falsyTypes(); static TypePtr todo(); @@ -145,7 +144,6 @@ class Types final { * tc.solve(). If the constraint has already been solved, use `instantiate` instead. */ static TypePtr approximate(const GlobalState &gs, const TypePtr &what, const TypeConstraint &tc); - static TypePtr dispatchCallWithoutBlock(const GlobalState &gs, const TypePtr &recv, const DispatchArgs &args); static TypePtr dropLiteral(const GlobalState &gs, const TypePtr &tp); /** Internal implementation. You should probably use all(). */ @@ -157,6 +155,8 @@ class Types final { static TypePtr arrayOf(const GlobalState &gs, const TypePtr &elem); static TypePtr rangeOf(const GlobalState &gs, const TypePtr &elem); static TypePtr hashOf(const GlobalState &gs, const TypePtr &elem); + static TypePtr setOf(const TypePtr &elem); + static TypePtr tClass(const TypePtr &attachedClass); static TypePtr dropNil(const GlobalState &gs, const TypePtr &from); /** Recursively replaces proxies with their underlying types */ @@ -170,6 +170,32 @@ class Types final { // This is an internal method for implementing intrinsics. In the future we should make all updateKnowledge methods // be intrinsics so that this can become an anonymous helper function in calls.cc. static core::ClassOrModuleRef getRepresentedClass(const GlobalState &gs, const core::TypePtr &ty); + + /** + * unwrapType is used to take an expression that's parsed at the value-level, + * and turn it into a type. For example, consider the following two expressions: + * + * > Integer.sqrt 10 + * > T::Array[Integer].new + * + * In both lines, `Integer` is initially resolved as the singleton class of + * `Integer`. This is because it's not immediately clear if we want to refer + * to the type `Integer` or if we want the singleton class of Integer for + * calling singleton methods. In the first line this was the correct choice, as + * we're just invoking the singleton method `sqrt`. In the second case we need + * to fix up the `Integer` sub-expression, and turn it back into the type of + * integer values. This is what `unwrapType` does, it turns the value-level + * expression back into a type-level one. + */ + static TypePtr unwrapType(const GlobalState &gs, Loc loc, const TypePtr &tp); + + // Converts type syntax like `GenericClass[Arg0, Arg1]` into a TypePtr. + // + // Called both from type_syntax.cc during sig parsing and from infer after encountering + // something that look like type syntax in a method body. + static TypePtr applyTypeArguments(const GlobalState &gs, const CallLocs &locs, uint16_t numPosArgs, + const InlinedVector &args, + ClassOrModuleRef genericClass); }; struct Intrinsic { @@ -422,6 +448,12 @@ template <> inline TypePtr make_type(cor return make_type(sym); } +template <> +inline TypePtr make_type(const core::TypeMemberRef &definition) { + auto sym = SymbolRef(definition); + return make_type(sym); +} + template <> inline SelfTypeParam cast_type_nonnull(const TypePtr &what) { ENFORCE_NO_TIMER(isa_type(what)); return SelfTypeParam(core::SymbolRef::fromRaw(what.inlinedValue())); diff --git a/core/TypesAndOrigins.cc b/core/TypesAndOrigins.cc index ac77401eaf..8830b06f82 100644 --- a/core/TypesAndOrigins.cc +++ b/core/TypesAndOrigins.cc @@ -1,5 +1,5 @@ #include "Types.h" -#include "common/sort.h" +#include "common/sort/sort.h" using namespace std; namespace sorbet::core { diff --git a/core/core.h b/core/core.h index e6edd5df7e..face39f3a2 100644 --- a/core/core.h +++ b/core/core.h @@ -1,7 +1,7 @@ #ifndef SORBET_CORE_H #define SORBET_CORE_H -#include "common/Counters.h" +#include "common/counters/Counters.h" #include "core/Context.h" #include "core/GlobalState.h" #include "core/Loc.h" diff --git a/core/errors/infer.cc b/core/errors/infer.cc new file mode 100644 index 0000000000..d7fe3e7016 --- /dev/null +++ b/core/errors/infer.cc @@ -0,0 +1,42 @@ +#include "core/errors/infer.h" +#include "core/GlobalState.h" + +namespace sorbet::core::errors::Infer { + +ErrorClass errorClassForUntyped(const GlobalState &gs, FileRef file, const TypePtr &untyped) { + prodCounterInc("types.input.untyped.usages"); + if (!gs.trackUntyped) { + return UntypedValue; + } + + auto isOpenInClient = file.data(gs).isOpenInClient(); + if (gs.printingFileTable) { + // Note: this metric, despite being a prod metric, will not get reported in the normal way + // to the metrics file, the web trace file, nor statsd. We call getAndClearHistogram BEFORE + // calling getAndClearThreadCounters on the main thread, which means that the metric will + // have been deleted before reporting to SignalFX. We don't even compute this if we are + // not running that code path (i.e. printing in realmain), because: + // + // - Tracking this metric causes a noticeable slowdown (it involves growing and merging + // large UnorderedMap's), and + // - If we did accidentally forget to clear the metric (e.g., in all LSP code paths), it + // would spam statsd services + prodHistogramInc("untyped.usages", file.id()); + } + + // we also run this code in test mode so we get some rudimentary coverage + // by running this path when running tests. + // + // Keep this in sync with core::Types::untyped(...) + if constexpr (sorbet::track_untyped_blame_mode || sorbet::debug_mode) { + prodHistogramInc("untyped.blames", untyped.untypedBlame().rawId()); + } + + if (isOpenInClient && file.data(gs).strictLevel < core::StrictLevel::Strong) { + return UntypedValueInformation; + } else { + return UntypedValue; + } +} + +} // namespace sorbet::core::errors::Infer diff --git a/core/errors/infer.h b/core/errors/infer.h index 00c16924a4..c03148c52c 100644 --- a/core/errors/infer.h +++ b/core/errors/infer.h @@ -1,6 +1,7 @@ #ifndef SORBET_CORE_ERRORS_INFER_H #define SORBET_CORE_ERRORS_INFER_H #include "core/Error.h" +#include "core/TypePtr.h" namespace sorbet::core::errors::Infer { // N.B infer does not run for untyped call at all. StrictLevel::False here would be meaningless @@ -47,6 +48,12 @@ constexpr ErrorClass CallOnUnboundedTypeMember{7039, StrictLevel::True}; constexpr ErrorClass AttachedClassOnInstance{7040, StrictLevel::True}; constexpr ErrorClass UntypedFieldSuggestion{7043, StrictLevel::Strict}; constexpr ErrorClass DigExtraArgs{7044, StrictLevel::True}; +constexpr ErrorClass IncorrectlyAssumedType{7045, StrictLevel::True}; +constexpr ErrorClass NonOverlappingEqual{7046, StrictLevel::True}; +constexpr ErrorClass UntypedValueInformation{7047, StrictLevel::True}; // N.B infer does not run for untyped call at all. StrictLevel::False here would be meaningless + +ErrorClass errorClassForUntyped(const GlobalState &gs, FileRef file, const TypePtr &ptr); + } // namespace sorbet::core::errors::Infer #endif diff --git a/core/errors/namer.h b/core/errors/namer.h index 5d0163fcee..e456262970 100644 --- a/core/errors/namer.h +++ b/core/errors/namer.h @@ -25,6 +25,7 @@ constexpr ErrorClass MultipleBehaviorDefs{4019, StrictLevel::False}; // constexpr ErrorClass YAMLSyntaxError{4020, StrictLevel::False}; constexpr ErrorClass OldTypeMemberSyntax{4021, StrictLevel::False}; constexpr ErrorClass ConstantKindRedefinition{4022, StrictLevel::False}; +constexpr ErrorClass HasAttachedClassInClass{4023, StrictLevel::False}; } // namespace sorbet::core::errors::Namer #endif diff --git a/core/errors/packager.h b/core/errors/packager.h index c7cf7033c1..bef6907c99 100644 --- a/core/errors/packager.h +++ b/core/errors/packager.h @@ -27,5 +27,6 @@ constexpr ErrorClass MissingImport{3718, StrictLevel::False}; constexpr ErrorClass UsedTestOnlyName{3720, StrictLevel::False}; constexpr ErrorClass InvalidExport{3721, StrictLevel::False}; // constexpr ErrorClass ExportingTypeAlias{3722, StrictLevel::False}; +constexpr ErrorClass ImportNotVisible{3723, StrictLevel::False}; } // namespace sorbet::core::errors::Packager #endif diff --git a/core/errors/resolver.h b/core/errors/resolver.h index 6cc8386a9a..a53793f7ab 100644 --- a/core/errors/resolver.h +++ b/core/errors/resolver.h @@ -18,7 +18,7 @@ constexpr ErrorClass RedefinitionOfParents{5012, StrictLevel::False}; constexpr ErrorClass ConstantAssertType{5013, StrictLevel::False}; constexpr ErrorClass ParentTypeNotDeclared{5014, StrictLevel::False}; constexpr ErrorClass ParentVarianceMismatch{5015, StrictLevel::False}; -constexpr ErrorClass VariantTypeMemberInClass{5016, StrictLevel::False}; +// constexpr ErrorClass VariantTypeMemberInClass{5016, StrictLevel::False}; constexpr ErrorClass TypeMembersInWrongOrder{5017, StrictLevel::False}; constexpr ErrorClass NotATypeVariable{5018, StrictLevel::False}; constexpr ErrorClass AbstractMethodWithBody{5019, StrictLevel::False}; @@ -29,6 +29,7 @@ constexpr ErrorClass BadAbstractMethod{5023, StrictLevel::False}; constexpr ErrorClass RecursiveTypeAlias{5024, StrictLevel::False}; constexpr ErrorClass TypeAliasInGenericClass{5025, StrictLevel::False}; constexpr ErrorClass BadStdlibGeneric{5026, StrictLevel::False}; +constexpr ErrorClass OutOfOrderConstantAccess{5027, StrictLevel::False}; // constexpr ErrorClass InvalidTypeDeclarationTyped{5027, StrictLevel::True}; // constexpr ErrorClass ConstantMissingTypeAnnotation{5028, StrictLevel::Strict}; @@ -77,6 +78,8 @@ constexpr ErrorClass MultipleStatementsInSig{5069, StrictLevel::False}; constexpr ErrorClass NilableUntyped{5070, StrictLevel::False}; constexpr ErrorClass BindNonBlockParameter{5071, StrictLevel::False}; constexpr ErrorClass TypeMemberScopeMismatch{5072, StrictLevel::False}; +constexpr ErrorClass AbstractClassInstantiated{5073, StrictLevel::True}; +constexpr ErrorClass HasAttachedClassIncluded{5074, StrictLevel::False}; } // namespace sorbet::core::errors::Resolver #endif diff --git a/core/errors/rewriter.h b/core/errors/rewriter.h index 3864d3aab1..1e095b72d4 100644 --- a/core/errors/rewriter.h +++ b/core/errors/rewriter.h @@ -15,5 +15,8 @@ constexpr ErrorClass ComputedBySymbol{3509, StrictLevel::False}; constexpr ErrorClass InitializeReturnType{3510, StrictLevel::False}; constexpr ErrorClass InvalidStructMember{3511, StrictLevel::False}; constexpr ErrorClass NilableUntyped{3512, StrictLevel::False}; +// moved to namer: +// constexpr ErrorClass HasAttachedClassInClass{3513, StrictLevel::False}; +constexpr ErrorClass ContravariantHasAttachedClass{3514, StrictLevel::False}; } // namespace sorbet::core::errors::Rewriter #endif diff --git a/core/lsp/Query.cc b/core/lsp/Query.cc index 35d34a96e8..0ba6d3b0c4 100644 --- a/core/lsp/Query.cc +++ b/core/lsp/Query.cc @@ -21,10 +21,10 @@ Query Query::createSymbolQuery(core::SymbolRef symbol) { return Query(Query::Kind::SYMBOL, core::Loc::none(), symbol, core::LocalVariable()); } -Query Query::createVarQuery(core::SymbolRef owner, core::LocalVariable variable) { +Query Query::createVarQuery(core::SymbolRef owner, core::Loc enclosingLoc, core::LocalVariable variable) { ENFORCE(owner.exists()); ENFORCE(variable.exists()); - return Query(Query::Kind::VAR, core::Loc::none(), owner, variable); + return Query(Query::Kind::VAR, enclosingLoc, owner, variable); } Query Query::createSuggestSigQuery(core::MethodRef method) { diff --git a/core/lsp/Query.h b/core/lsp/Query.h index 892c15341c..a4297c5474 100644 --- a/core/lsp/Query.h +++ b/core/lsp/Query.h @@ -25,6 +25,9 @@ class Query final { }; Kind kind; + // If Kind == VAR, this is the loc of the MethodDef that encloses the variable + // If Kind == LOC, this is the loc to look for. + // Otherwise, this is meaningless. core::Loc loc; // If Kind == SYMBOL, this is the symbol that the query is looking for. // If Kind == SUGGEST_SIG, this is the method to suggest a sig for. @@ -35,7 +38,7 @@ class Query final { static Query noQuery(); static Query createLocQuery(core::Loc loc); static Query createSymbolQuery(core::SymbolRef symbol); - static Query createVarQuery(core::SymbolRef owner, core::LocalVariable variable); + static Query createVarQuery(core::SymbolRef owner, core::Loc enclosingLoc, core::LocalVariable variable); static Query createSuggestSigQuery(core::MethodRef method); bool matchesSymbol(const core::SymbolRef &symbol) const; diff --git a/core/lsp/QueryResponse.cc b/core/lsp/QueryResponse.cc index 0862ca07bf..843738fa0f 100644 --- a/core/lsp/QueryResponse.cc +++ b/core/lsp/QueryResponse.cc @@ -18,7 +18,8 @@ const SendResponse *QueryResponse::isSend() const { } const optional SendResponse::getMethodNameLoc(const core::GlobalState &gs) const { - return (this->funLoc.exists() && !this->funLoc.empty()) ? make_optional(this->funLoc) : nullopt; + auto existsNonEmpty = (this->funLocOffsets.exists() && !this->funLocOffsets.empty()); + return existsNonEmpty ? make_optional(this->funLoc()) : nullopt; } const IdentResponse *QueryResponse::isIdent() const { @@ -49,7 +50,7 @@ core::Loc QueryResponse::getLoc() const { if (auto ident = isIdent()) { return ident->termLoc; } else if (auto send = isSend()) { - return send->termLoc; + return send->termLoc(); } else if (auto literal = isLiteral()) { return literal->termLoc; } else if (auto constant = isConstant()) { diff --git a/core/lsp/QueryResponse.h b/core/lsp/QueryResponse.h index a6d0184725..668a5e7032 100644 --- a/core/lsp/QueryResponse.h +++ b/core/lsp/QueryResponse.h @@ -10,36 +10,51 @@ class TypeConstraint; class SendResponse final { public: - SendResponse(core::Loc termLoc, std::shared_ptr dispatchResult, core::NameRef callerSideName, - bool isPrivateOk, core::MethodRef enclosingMethod, core::Loc receiverLoc, core::Loc funLoc, - size_t totalArgs) - : dispatchResult(std::move(dispatchResult)), callerSideName(callerSideName), termLoc(termLoc), - isPrivateOk(isPrivateOk), enclosingMethod(enclosingMethod), receiverLoc(receiverLoc), funLoc(funLoc), - totalArgs(totalArgs){}; + SendResponse(std::shared_ptr dispatchResult, InlinedVector argLocOffsets, + core::NameRef callerSideName, core::MethodRef enclosingMethod, bool isPrivateOk, core::FileRef file, + core::LocOffsets termLocOffsets, core::LocOffsets receiverLocOffsets, core::LocOffsets funLocOffsets) + : dispatchResult(std::move(dispatchResult)), argLocOffsets(std::move(argLocOffsets)), + callerSideName(callerSideName), enclosingMethod(enclosingMethod), isPrivateOk(isPrivateOk), file(file), + termLocOffsets(termLocOffsets), receiverLocOffsets(receiverLocOffsets), funLocOffsets(funLocOffsets){}; const std::shared_ptr dispatchResult; + const InlinedVector argLocOffsets; const core::NameRef callerSideName; - const core::Loc termLoc; - const bool isPrivateOk; const core::MethodRef enclosingMethod; - const core::Loc receiverLoc; - const core::Loc funLoc; - const size_t totalArgs; + const bool isPrivateOk; + const core::FileRef file; + const core::LocOffsets termLocOffsets; + const core::LocOffsets receiverLocOffsets; + const core::LocOffsets funLocOffsets; + + core::Loc termLoc() const { + return core::Loc(file, termLocOffsets); + } + core::Loc receiverLoc() const { + return core::Loc(file, receiverLocOffsets); + } + core::Loc funLoc() const { + return core::Loc(file, funLocOffsets); + } const std::optional getMethodNameLoc(const core::GlobalState &gs) const; }; -CheckSize(SendResponse, 72, 8); +CheckSize(SendResponse, 80, 8); class IdentResponse final { public: IdentResponse(core::Loc termLoc, core::LocalVariable variable, core::TypeAndOrigins retType, - core::MethodRef enclosingMethod) - : termLoc(termLoc), variable(variable), enclosingMethod(enclosingMethod), retType(std::move(retType)) {} + core::MethodRef enclosingMethod, core::Loc enclosingMethodLoc) + : termLoc(termLoc), variable(variable), enclosingMethod(enclosingMethod), + enclosingMethodLoc(enclosingMethodLoc), retType(std::move(retType)) {} const core::Loc termLoc; const core::LocalVariable variable; const core::MethodRef enclosingMethod; + // The loc of the MethodDef this ident was in. + // (not the declLoc, which can be found by way of the enclosingMethod's entry in the symbol table) + const core::Loc enclosingMethodLoc; const core::TypeAndOrigins retType; }; -CheckSize(IdentResponse, 56, 8); +CheckSize(IdentResponse, 72, 8); class LiteralResponse final { public: diff --git a/core/packages/MangledName.cc b/core/packages/MangledName.cc new file mode 100644 index 0000000000..6bebd4a75a --- /dev/null +++ b/core/packages/MangledName.cc @@ -0,0 +1,26 @@ +#include "core/packages/MangledName.h" +#include "absl/strings/str_join.h" +#include "core/GlobalState.h" +#include "core/Names.h" + +using namespace std; + +namespace sorbet::core::packages { +core::NameRef MangledName::mangledNameFromParts(core::GlobalState &gs, std::vector &parts) { + // Foo::Bar => Foo_Bar_Package + auto mangledName = absl::StrCat(absl::StrJoin(parts, "_"), core::PACKAGE_SUFFIX); + + auto utf8Name = gs.enterNameUTF8(mangledName); + auto packagerName = gs.freshNameUnique(core::UniqueNameKind::Packager, utf8Name, 1); + return gs.enterNameConstant(packagerName); +} + +core::NameRef MangledName::mangledNameFromParts(core::GlobalState &gs, std::vector &parts) { + // Foo::Bar => Foo_Bar_Package + auto mangledName = absl::StrCat(absl::StrJoin(parts, "_", NameFormatter(gs)), core::PACKAGE_SUFFIX); + + auto utf8Name = gs.enterNameUTF8(mangledName); + auto packagerName = gs.freshNameUnique(core::UniqueNameKind::Packager, utf8Name, 1); + return gs.enterNameConstant(packagerName); +} +} // namespace sorbet::core::packages diff --git a/core/packages/MangledName.h b/core/packages/MangledName.h new file mode 100644 index 0000000000..92d6b6a473 --- /dev/null +++ b/core/packages/MangledName.h @@ -0,0 +1,30 @@ +#ifndef SORBET_CORE_PACKAGES_MANGLEDNAME_H +#define SORBET_CORE_PACKAGES_MANGLEDNAME_H + +#include "core/GlobalState.h" +#include "core/LocOffsets.h" +#include "core/NameRef.h" +#include + +namespace sorbet::core::packages { +class MangledName final { +public: + static core::NameRef mangledNameFromParts(core::GlobalState &gs, std::vector &parts); + static core::NameRef mangledNameFromParts(core::GlobalState &gs, std::vector &parts); +}; + +class NameFormatter final { + const core::GlobalState &gs; + +public: + NameFormatter(const core::GlobalState &gs) : gs(gs) {} + + void operator()(std::string *out, core::NameRef name) const { + out->append(name.shortName(gs)); + } + void operator()(std::string *out, std::pair p) const { + out->append(p.first.shortName(gs)); + } +}; +} // namespace sorbet::core::packages +#endif diff --git a/core/packages/PackageDB.cc b/core/packages/PackageDB.cc index d8acb1ba0b..d3b5d4c797 100644 --- a/core/packages/PackageDB.cc +++ b/core/packages/PackageDB.cc @@ -1,7 +1,7 @@ #include "core/packages/PackageDB.h" #include "absl/strings/match.h" #include "absl/strings/str_replace.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "core/AutocorrectSuggestion.h" #include "core/GlobalState.h" #include "core/Loc.h" @@ -58,7 +58,12 @@ class NonePackage final : public PackageInfo { return false; } - bool strictAutoloaderCompatibility() const { + bool legacyAutoloaderCompatibility() const { + notImplemented(); + return true; + } + + bool exportAll() const { notImplemented(); return false; } @@ -72,6 +77,9 @@ class NonePackage final : public PackageInfo { std::vector> testImports() const { return vector>(); } + std::vector> visibleTo() const { + return vector>(); + } std::optional importsPackage(core::NameRef mangledName) const { notImplemented(); @@ -237,6 +245,10 @@ const std::string_view PackageDB::errorHint() const { return errorHint_; } +bool PackageDB::skipImportVisibilityCheckFor(core::NameRef mangledName) const { + return absl::c_find(skipImportVisibilityCheckFor_, mangledName) != skipImportVisibilityCheckFor_.end(); +} + PackageDB PackageDB::deepCopy() const { ENFORCE(frozen); PackageDB result; diff --git a/core/packages/PackageDB.h b/core/packages/PackageDB.h index 758cafcaea..404c99e292 100644 --- a/core/packages/PackageDB.h +++ b/core/packages/PackageDB.h @@ -59,6 +59,7 @@ class PackageDB final { const std::vector &skipRBIExportEnforcementDirs() const; const std::string_view errorHint() const; + bool skipImportVisibilityCheckFor(const core::NameRef mangledName) const; private: std::vector secondaryTestPackageNamespaceRefs_; @@ -66,6 +67,7 @@ class PackageDB final { std::vector extraPackageFilesDirectorySlashPrefixes_; std::string errorHint_; std::vector skipRBIExportEnforcementDirs_; + std::vector skipImportVisibilityCheckFor_; // This vector is kept in sync with the size of the file table in the global state by // `Packager::setPackageNameOnFiles`. A `FileRef` being out of bounds in this vector is treated as the file having diff --git a/core/packages/PackageInfo.cc b/core/packages/PackageInfo.cc index c21b7d2a8c..430a958b10 100644 --- a/core/packages/PackageInfo.cc +++ b/core/packages/PackageInfo.cc @@ -57,4 +57,62 @@ string PackageInfo::show(const core::GlobalState &gs) const { "::", [&](string *out, core::NameRef name) { absl::StrAppend(out, name.show(gs)); }); } +core::ClassOrModuleRef getTestSym(const core::GlobalState &gs) { + return core::Symbols::root().data(gs)->findMember(gs, core::Names::Constants::Test()).asClassOrModuleRef(); +} + +core::ClassOrModuleRef getParentNamespaceSym(const core::GlobalState &gs, const core::SymbolRef sym) { + auto testSym = getTestSym(gs); + if (!testSym.exists()) { + return core::Symbols::root(); + } + + if (sym.isUnderNamespace(gs, testSym)) { + return testSym; + } + + return core::Symbols::root(); +} + +core::ClassOrModuleRef lookupNameOn(const core::GlobalState &gs, const core::ClassOrModuleRef root, + const std::vector &name) { + auto curSym = root; + if (!curSym.exists()) { + return {}; + } + + for (const auto part : name) { + auto member = curSym.data(gs)->findMember(gs, part); + if (!member.exists() || !member.isClassOrModule()) { + return {}; + } + curSym = member.asClassOrModuleRef(); + } + + return curSym; +} + +core::ClassOrModuleRef PackageInfo::getPackageScope(const core::GlobalState &gs) const { + return lookupNameOn(gs, core::Symbols::root(), fullName()); +} + +core::ClassOrModuleRef PackageInfo::getPackageTestScope(const core::GlobalState &gs) const { + auto testSym = core::Symbols::root().data(gs)->findMember(gs, core::Names::Constants::Test()); + if (!testSym.isClassOrModule()) { + return {}; + } + + return lookupNameOn(gs, testSym.asClassOrModuleRef(), fullName()); +} + +// Given a package named Project::MyPackage, returns the class/module ref corresponding to +// the symbol Project::MyPackage or Test::Project::MyPackage, depending on whether the suggestion scope +// is a primary namespace constant or a test namespace constant. See packager/packager.cc for further explanation of +// test namespaces. +core::ClassOrModuleRef PackageInfo::getRootSymbolForAutocorrectSearch(const core::GlobalState &gs, + const core::SymbolRef suggestionScope) const { + auto parentSym = getParentNamespaceSym(gs, suggestionScope); + return lookupNameOn(gs, parentSym, fullName()); +} + } // namespace sorbet::core::packages diff --git a/core/packages/PackageInfo.h b/core/packages/PackageInfo.h index 4f60b46289..59e75f6cd8 100644 --- a/core/packages/PackageInfo.h +++ b/core/packages/PackageInfo.h @@ -15,6 +15,7 @@ class Context; } // namespace sorbet::core namespace sorbet::core::packages { + enum class ImportType { Normal, Test, @@ -28,11 +29,17 @@ class PackageInfo { virtual std::vector> exports() const = 0; virtual std::vector> imports() const = 0; virtual std::vector> testImports() const = 0; + virtual std::vector> visibleTo() const = 0; virtual std::unique_ptr deepCopy() const = 0; virtual core::Loc fullLoc() const = 0; virtual core::Loc declLoc() const = 0; virtual bool exists() const final; std::string show(const core::GlobalState &gs) const; + core::ClassOrModuleRef getRootSymbolForAutocorrectSearch(const core::GlobalState &gs, + core::SymbolRef suggestionScope) const; + + core::ClassOrModuleRef getPackageScope(const core::GlobalState &gs) const; + core::ClassOrModuleRef getPackageTestScope(const core::GlobalState &gs) const; virtual std::optional importsPackage(core::NameRef mangledName) const = 0; @@ -57,7 +64,8 @@ class PackageInfo { }; virtual bool ownsSymbol(const core::GlobalState &gs, core::SymbolRef symbol) const = 0; - virtual bool strictAutoloaderCompatibility() const = 0; + virtual bool legacyAutoloaderCompatibility() const = 0; + virtual bool exportAll() const = 0; // Utilities: diff --git a/core/proto/proto.cc b/core/proto/proto.cc index 9c5804f48c..ec007bb528 100644 --- a/core/proto/proto.cc +++ b/core/proto/proto.cc @@ -5,8 +5,8 @@ #include "absl/strings/match.h" #include "absl/strings/str_cat.h" -#include "common/Counters_impl.h" #include "common/Random.h" +#include "common/counters/Counters_impl.h" #include "common/typecase.h" #include "core/Names.h" @@ -387,8 +387,11 @@ com::stripe::rubytyper::File::CompiledLevel compiledToProto(core::CompiledLevel } } -com::stripe::rubytyper::FileTable Proto::filesToProto(const GlobalState &gs, bool showFull) { +com::stripe::rubytyper::FileTable Proto::filesToProto(const GlobalState &gs, + const UnorderedMap &untypedUsages, bool showFull) { com::stripe::rubytyper::FileTable files; + const auto &packageDB = gs.packageDB(); + auto stripePackages = !packageDB.empty(); for (int i = 1; i < gs.filesUsed(); ++i) { core::FileRef file(i); if (file.data(gs).isPayload()) { @@ -408,6 +411,18 @@ com::stripe::rubytyper::FileTable Proto::filesToProto(const GlobalState &gs, boo entry->set_strict(strictToProto(file.data(gs).strictLevel)); entry->set_min_error_level(strictToProto(file.data(gs).minErrorLevel())); entry->set_compiled(compiledToProto(file.data(gs).compiledLevel)); + + auto frefIdIt = untypedUsages.find(i); + if (frefIdIt != untypedUsages.end()) { + entry->set_untyped_usages(frefIdIt->second); + } + + if (stripePackages) { + const auto &packageInfo = packageDB.getPackageForFile(gs, file); + if (packageInfo.exists()) { + entry->set_pkg(packageInfo.show(gs)); + } + } } return files; } diff --git a/core/proto/proto.h b/core/proto/proto.h index 5e7ee55170..b88f5bda5d 100644 --- a/core/proto/proto.h +++ b/core/proto/proto.h @@ -28,7 +28,8 @@ class Proto { static com::stripe::rubytyper::Type toProto(const GlobalState &gs, const TypePtr &typ); static com::stripe::rubytyper::Loc toProto(const GlobalState &gs, Loc loc); - static com::stripe::rubytyper::FileTable filesToProto(const GlobalState &gs, bool showFull); + static com::stripe::rubytyper::FileTable filesToProto(const GlobalState &gs, + const UnorderedMap &untypedUsages, bool showFull); static com::stripe::payserver::events::cibot::SourceMetrics toProto(const CounterState &counters, std::string_view prefix); diff --git a/core/serialize/BUILD b/core/serialize/BUILD index 3f80adee1e..6aabaf9740 100644 --- a/core/serialize/BUILD +++ b/core/serialize/BUILD @@ -29,7 +29,7 @@ cc_test( visibility = ["//tools:__pkg__"], deps = [ ":serialize", - "@doctest", - "@doctest//:doctest_main", + "@doctest//doctest", + "@doctest//doctest:main", ], ) diff --git a/core/serialize/serialize.cc b/core/serialize/serialize.cc index 06039d5eff..ca409ba305 100644 --- a/core/serialize/serialize.cc +++ b/core/serialize/serialize.cc @@ -2,8 +2,8 @@ #include "absl/base/casts.h" #include "absl/types/span.h" #include "ast/Helpers.h" -#include "common/Timer.h" -#include "common/sort.h" +#include "common/sort/sort.h" +#include "common/timers/Timer.h" #include "core/Error.h" #include "core/FileHash.h" #include "core/GlobalState.h" @@ -241,7 +241,6 @@ void SerializerImpl::pickle(Pickler &p, shared_ptr fh) { p.putU1(1); p.putU4(fh->localSymbolTableHashes.hierarchyHash); p.putU4(fh->localSymbolTableHashes.classModuleHash); - p.putU4(fh->localSymbolTableHashes.typeArgumentHash); p.putU4(fh->localSymbolTableHashes.typeMemberHash); p.putU4(fh->localSymbolTableHashes.fieldHash); p.putU4(fh->localSymbolTableHashes.staticFieldHash); @@ -297,7 +296,6 @@ unique_ptr SerializerImpl::unpickleFileHash(UnPickler &p) { ret.localSymbolTableHashes.hierarchyHash = p.getU4(); ret.localSymbolTableHashes.classModuleHash = p.getU4(); - ret.localSymbolTableHashes.typeArgumentHash = p.getU4(); ret.localSymbolTableHashes.typeMemberHash = p.getU4(); ret.localSymbolTableHashes.fieldHash = p.getU4(); ret.localSymbolTableHashes.staticFieldHash = p.getU4(); @@ -633,6 +631,7 @@ void SerializerImpl::pickle(Pickler &p, const Method &what) { pickle(p, a); } pickle(p, what.resultType); + p.putU4(what.intrinsicOffset); p.putU4(what.locs().size()); for (auto &loc : what.locs()) { pickle(p, loc); @@ -665,6 +664,7 @@ Method SerializerImpl::unpickleMethod(UnPickler &p, const GlobalState *gs) { } result.resultType = unpickleType(p, gs); + result.intrinsicOffset = p.getU4(); auto locCount = p.getU4(); for (int i = 0; i < locCount; i++) { result.locs_.emplace_back(unpickleLoc(p)); @@ -1054,13 +1054,12 @@ LocOffsets SerializerImpl::unpickleLocOffsets(UnPickler &p) { return LocOffsets{p.getU4(), p.getU4()}; } -vector Serializer::store(GlobalState &gs) { +vector Serializer::store(const GlobalState &gs) { Pickler p = SerializerImpl::pickle(gs); return p.result(); } -std::vector Serializer::storePayloadAndNameTable(GlobalState &gs) { - Timer timeit(gs.tracer(), "Serializer::storePayloadAndNameTable"); +std::vector Serializer::storePayloadAndNameTable(const GlobalState &gs) { Pickler p = SerializerImpl::pickle(gs, true); return p.result(); } @@ -1070,7 +1069,6 @@ void Serializer::loadGlobalState(GlobalState &gs, const uint8_t *const data) { "Can't load into a non-empty state"); UnPickler p(data, gs.tracer()); SerializerImpl::unpickleGS(p, gs); - gs.installIntrinsics(); } uint32_t Serializer::loadGlobalStateUUID(const GlobalState &gs, const uint8_t *const data) { diff --git a/core/serialize/serialize.h b/core/serialize/serialize.h index afa8fdd036..1364995da4 100644 --- a/core/serialize/serialize.h +++ b/core/serialize/serialize.h @@ -9,13 +9,13 @@ class Serializer { static const uint32_t VERSION = 6; // Serialize a global state. - static std::vector store(GlobalState &gs); + static std::vector store(const GlobalState &gs); // Stores a GlobalState, but only includes `File`s with Type == Payload. // This can be used in conjunction with `storeFile` to store // a global state containing a name table along side a large number of // individual cached files, which can be loaded independently. - static std::vector storePayloadAndNameTable(GlobalState &gs); + static std::vector storePayloadAndNameTable(const GlobalState &gs); // Serializes an AST and file hash. static std::vector storeTree(const core::File &file, const ast::ParsedFile &tree); diff --git a/core/serialize/test/serialize_test.cc b/core/serialize/test/serialize_test.cc index 8e1b3c4f4e..53e8dfa403 100644 --- a/core/serialize/test/serialize_test.cc +++ b/core/serialize/test/serialize_test.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" // has to go first as it violates our requirements #include "core/serialize/pickler.h" #include "core/serialize/serialize.h" diff --git a/core/test/core_test.cc b/core/test/core_test.cc index 603f4902ba..5ec5da042f 100644 --- a/core/test/core_test.cc +++ b/core/test/core_test.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" // has to go first as it violates our requirements #include "core/Error.h" #include "core/ErrorCollector.h" diff --git a/core/tools/generate_names.cc b/core/tools/generate_names.cc index 5df0334155..e9f2bf2471 100644 --- a/core/tools/generate_names.cc +++ b/core/tools/generate_names.cc @@ -135,6 +135,7 @@ NameDef names[] = { {"proc"}, {"untyped"}, {"noreturn"}, + {"anything"}, {"singletonClass", "singleton_class"}, {"class_", "class"}, {"classOf", "class_of"}, @@ -148,6 +149,7 @@ NameDef names[] = { {"let"}, {"uncheckedLet", ""}, {"syntheticBind", ""}, + {"assumeType", ""}, {"unsafe"}, {"must"}, {"mustBecause", "must_because"}, @@ -195,6 +197,7 @@ NameDef names[] = { {"fixed"}, {"lower"}, {"upper"}, + {"declareHasAttachedClass", "has_attached_class!"}, {"prop"}, {"tokenProp", "token_prop"}, @@ -231,15 +234,10 @@ NameDef names[] = { {"factory"}, {"InexactStruct", "InexactStruct", true}, {"ImmutableStruct", "ImmutableStruct", true}, - {"Chalk", "Chalk", true}, - {"ODM", "ODM", true}, - {"Document", "Document", true}, - {"DeprecatedNumeric", "DeprecatedNumeric", true}, {"Private", "Private", true}, {"Types", "Types", true}, {"Methods", "Methods", true}, {"DeclBuilder", "DeclBuilder", true}, - {"Chalk_ODM_Document", "::Chalk::ODM::Document"}, {"prefix"}, {"to"}, @@ -250,6 +248,8 @@ NameDef names[] = { {"cattrAccessor", "cattr_accessor"}, {"cattrReader", "cattr_reader"}, {"cattrWriter", "cattr_writer"}, + {"threadMattrAccessor", "thread_mattr_accessor"}, + {"threadCattrAccessor", "thread_cattr_accessor"}, {"instanceReader", "instance_reader"}, {"instanceWriter", "instance_writer"}, {"instanceAccessor", "instance_accessor"}, @@ -299,6 +299,7 @@ NameDef names[] = { {"genericPropGetter"}, {"raise"}, + {"fail"}, {"rewriterRaiseUnimplemented", "Sorbet rewriter pass partially unimplemented"}, {"test"}, @@ -346,6 +347,9 @@ NameDef names[] = { {"blockGiven_p", "block_given?"}, {"anonymousBlock", ""}, + // Method names known to Data + {"define"}, + // Used to generate temporary names for destructuring arguments ala proc do // |(x,y)|; end {"destructureArg", ""}, @@ -437,7 +441,6 @@ NameDef names[] = { {"callWithSplatAndBlock", ""}, {"enumerableToH", "enumerable_to_h"}, {"blockBreak", ""}, - {"selfNew", ""}, {"stringInterpolate", ""}, // Packager @@ -448,6 +451,8 @@ NameDef names[] = { {"autoloader_compatibility"}, {"legacy"}, {"strict"}, + {"visible_to"}, + {"exportAll", "export_all!"}, {"PackageSpec", "PackageSpec", true}, {"PackageSpecRegistry", "", true}, @@ -456,7 +461,7 @@ NameDef names[] = { {"compilerVersion", "compiler_version"}, // GlobalState initEmpty() - {"Top", "", true}, + {"Top", "T.anything", true}, {"Bottom", "T.noreturn", true}, {"Untyped", "T.untyped", true}, {"Root", "", true}, @@ -464,6 +469,7 @@ NameDef names[] = { {"String", "String", true}, {"Integer", "Integer", true}, {"Float", "Float", true}, + {"Numeric", "Numeric", true}, {"Symbol", "Symbol", true}, {"Array", "Array", true}, {"Hash", "Hash", true}, @@ -489,6 +495,7 @@ NameDef names[] = { {"Kernel", "Kernel", true}, {"Range", "Range", true}, {"Regexp", "Regexp", true}, + {"Exception", "Exception", true}, {"StandardError", "StandardError", true}, {"Complex", "Complex", true}, {"Rational", "Rational", true}, @@ -504,8 +511,10 @@ NameDef names[] = { {"Enumerable", "Enumerable", true}, {"Enumerator", "Enumerator", true}, {"Lazy", "Lazy", true}, + {"Chain", "Chain", true}, {"Set", "Set", true}, {"Struct", "Struct", true}, + {"Data", "Data", true}, {"File", "File", true}, {"Encoding", "Encoding", true}, {"getEncoding", ""}, diff --git a/core/types/calls.cc b/core/types/calls.cc index 19cac23391..b194d0fb0f 100644 --- a/core/types/calls.cc +++ b/core/types/calls.cc @@ -1,7 +1,7 @@ #include "absl/strings/match.h" #include "absl/strings/str_split.h" #include "common/common.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "common/typecase.h" #include "core/GlobalState.h" #include "core/Names.h" @@ -21,6 +21,9 @@ using namespace std; namespace sorbet::core { namespace { + +const bool IMPLICIT_CONVERSION_ALLOWS_PRIVATE = true; + DispatchResult dispatchCallProxyType(const GlobalState &gs, TypePtr und, const DispatchArgs &args) { categoryCounterInc("dispatch_call", "proxytype"); return und.dispatchCall(gs, args.withThisRef(und)); @@ -278,6 +281,30 @@ unique_ptr matchArgType(const GlobalState &gs, TypeConstraint &constr, Lo expectedType = Types::replaceSelfType(gs, expectedType, selfType); if (Types::isSubTypeUnderConstraint(gs, constr, argTpe.type, expectedType, UntypedMode::AlwaysCompatible)) { + if (expectedType.isUntyped()) { + // TODO(jez) We should have code like this, but currently there are too many places that + // are fine "accepting anything" that are typed as `T.untyped`. + // + // We should take a pass at fixing a lot of those in our RBI files, and then circle back + // to enabling this error. + // + // auto what = core::errors::Infer::errorClassForUntyped(gs, argLoc.file()); + // if (auto e = gs.beginError(argLoc, what)) { + // e.setHeader("Method parameter `{}` is declared with `{}`", argSym.argumentName(gs), "T.untyped"); + // TypeErrorDiagnostics::explainUntyped(gs, e, what, expectedType, argSym.loc, originForUninitialized); + // return e.build(); + // } + } else if (argTpe.type.isUntyped()) { + auto what = core::errors::Infer::errorClassForUntyped(gs, argLoc.file(), argTpe.type); + if (auto e = gs.beginError(argLoc, what)) { + e.setHeader("Argument passed to parameter `{}` is `{}`", argSym.argumentName(gs), "T.untyped"); + auto for_ = + ErrorColors::format("argument `{}` of method `{}`", argSym.argumentName(gs), method.show(gs)); + e.addErrorSection(TypeAndOrigins::explainExpected(gs, expectedType, argSym.loc, for_)); + TypeErrorDiagnostics::explainUntyped(gs, e, what, argTpe, originForUninitialized); + return e.build(); + } + } return nullptr; } @@ -438,94 +465,6 @@ MethodRef guessOverload(const GlobalState &gs, ClassOrModuleRef inClass, MethodR return fallback; } -/** - * unwrapType is used to take an expression that's parsed at the value-level, - * and turn it into a type. For example, consider the following two expressions: - * - * > Integer.sqrt 10 - * > T::Array[Integer].new - * - * In both lines, `Integer` is initially resolved as the singleton class of - * `Integer`. This is because it's not immediately clear if we want to refer - * to the type `Integer` or if we want the singleton class of Integer for - * calling singleton methods. In the first line this was the correct choice, as - * we're just invoking the singleton method `sqrt`. In the second case we need - * to fix up the `Integer` sub-expression, and turn it back into the type of - * integer values. This is what `unwrapType` does, it turns the value-level - * expression back into a type-level one. - */ -TypePtr unwrapType(const GlobalState &gs, Loc loc, const TypePtr &tp) { - if (auto *metaType = cast_type(tp)) { - return metaType->wrapped; - } - - if (isa_type(tp)) { - auto classType = cast_type_nonnull(tp); - if (classType.symbol.data(gs)->derivesFrom(gs, core::Symbols::T_Enum())) { - // T::Enum instances are allowed to stand for themselves in type syntax positions. - // See the note in type_syntax.cc regarding T::Enum. - return tp; - } - - auto attachedClass = classType.symbol.data(gs)->attachedClass(gs); - if (!attachedClass.exists()) { - if (auto e = gs.beginError(loc, errors::Infer::BareTypeUsage)) { - e.setHeader("Unexpected bare `{}` value found in type position", tp.show(gs)); - if (classType.symbol == core::Symbols::T_Types_Base() || - classType.symbol.data(gs)->derivesFrom(gs, core::Symbols::T_Types_Base())) { - // T::Types::Base is the parent class for runtime type objects. - // Give a more helpful error message - e.addErrorNote("Sorbet only allows statically-analyzable types in type positions.\n" - " To compute new runtime types, you must explicitly wrap with `{}`", - "T.unsafe"); - auto locSource = loc.source(gs); - if (locSource.has_value()) { - e.replaceWith("Wrap in `T.unsafe`", loc, fmt::format("T.unsafe({})", locSource.value())); - } - } - } - - return Types::untypedUntracked(); - } - - return attachedClass.data(gs)->externalType(); - } - - if (auto *appType = cast_type(tp)) { - ClassOrModuleRef attachedClass = appType->klass.data(gs)->attachedClass(gs); - if (!attachedClass.exists()) { - if (auto e = gs.beginError(loc, errors::Infer::BareTypeUsage)) { - e.setHeader("Unexpected bare `{}` value found in type position", tp.show(gs)); - } - return Types::untypedUntracked(); - } - - return attachedClass.data(gs)->externalType(); - } - - if (auto *shapeType = cast_type(tp)) { - vector unwrappedValues; - unwrappedValues.reserve(shapeType->values.size()); - for (auto &value : shapeType->values) { - unwrappedValues.emplace_back(unwrapType(gs, loc, value)); - } - return make_type(shapeType->keys, move(unwrappedValues)); - } else if (auto *tupleType = cast_type(tp)) { - vector unwrappedElems; - unwrappedElems.reserve(tupleType->elems.size()); - for (auto &elem : tupleType->elems) { - unwrappedElems.emplace_back(unwrapType(gs, loc, elem)); - } - return make_type(move(unwrappedElems)); - } else if (isa_type(tp) || isa_type(tp) || isa_type(tp)) { - if (auto e = gs.beginError(loc, errors::Infer::BareTypeUsage)) { - e.setHeader("Unexpected bare `{}` value found in type position", tp.show(gs)); - } - return Types::untypedUntracked(); - } - return tp; -} - struct ArityComponents { int required; int optional; @@ -560,73 +499,6 @@ string prettyArity(const GlobalState &gs, MethodRef method) { } } -bool extendsModule(const GlobalState &gs, core::ClassOrModuleRef enclosingClass, core::ClassOrModuleRef mod) { - ENFORCE(enclosingClass.exists()); - auto enclosingSingletonClass = enclosingClass.data(gs)->lookupSingletonClass(gs); - ENFORCE(enclosingSingletonClass.exists()); - return enclosingSingletonClass.data(gs)->derivesFrom(gs, mod); -} - -/** - * Make an autocorrection for adding `extend T::Sig` or `extend T::Helpers`, when needed. - */ -optional maybeSuggestExtendModule(const GlobalState &gs, - core::ClassOrModuleRef enclosingClass, const Loc &call, - core::ClassOrModuleRef mod) { - if (extendsModule(gs, enclosingClass, mod)) { - // No need to suggest here, because it already has 'extend T::Sig' - return nullopt; - } - - if (enclosingClass == core::Symbols::root()) { - // We don't put any locs on for performance (would have one loc for each file in the codebase) - // If they're writing methods at the top level, it's probably a small script. - // Just put the `extend` immediately above the sig. - - auto [sigStart, _sigEnd] = call.position(gs); - auto thisLineStart = core::Loc::Detail{sigStart.line, 1}; - auto thisLineLoc = core::Loc::fromDetails(gs, call.file(), thisLineStart, thisLineStart); - ENFORCE(thisLineLoc.has_value()); - auto [_, thisLinePadding] = thisLineLoc.value().findStartOfLine(gs); - - string prefix(thisLinePadding, ' '); - auto modStr = mod.show(gs); - return core::AutocorrectSuggestion{ - fmt::format("Add `extend {}`", modStr), - {core::AutocorrectSuggestion::Edit{thisLineLoc.value(), fmt::format("{}extend {}\n\n", prefix, modStr)}}}; - } - - auto inFileOfMethod = [&](const auto &loc) { return loc.file() == call.file(); }; - auto &classLocs = enclosingClass.data(gs)->locs(); - auto classLoc = absl::c_find_if(classLocs, inFileOfMethod); - - if (classLoc == classLocs.end()) { - // Couldn't a loc for the enclosing class in this file, give up. - return nullopt; - } - - auto [classStart, classEnd] = classLoc->position(gs); - - auto thisLineStart = core::Loc::Detail{classStart.line, 1}; - auto thisLineLoc = core::Loc::fromDetails(gs, classLoc->file(), thisLineStart, thisLineStart); - ENFORCE(thisLineLoc.has_value()); - auto [_, thisLinePadding] = thisLineLoc.value().findStartOfLine(gs); - - auto nextLineStart = core::Loc::Detail{classStart.line + 1, 1}; - auto nextLineLoc = core::Loc::fromDetails(gs, classLoc->file(), nextLineStart, nextLineStart); - if (!nextLineLoc.has_value()) { - return nullopt; - } - auto [replacementLoc, nextLinePadding] = nextLineLoc.value().findStartOfLine(gs); - - // Preserve the indentation of the line below us. - string prefix(max(thisLinePadding + 2, nextLinePadding), ' '); - auto modStr = mod.show(gs); - return core::AutocorrectSuggestion{ - fmt::format("Add `extend {}`", modStr), - {core::AutocorrectSuggestion::Edit{nextLineLoc.value(), fmt::format("{}extend {}\n", prefix, modStr)}}}; -} - void maybeSuggestUnsafeKwsplat(const core::GlobalState &gs, core::ErrorBuilder &e, core::Loc kwSplatArgLoc) { if (!kwSplatArgLoc.exists()) { return; @@ -690,6 +562,12 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg auto funLoc = args.funLoc(); auto errLoc = (funLoc.exists() && !funLoc.empty()) ? funLoc : args.callLoc(); if (symbol == core::Symbols::untyped()) { + auto what = core::errors::Infer::errorClassForUntyped(gs, args.locs.file, args.thisType); + if (auto e = gs.beginError(errLoc, what)) { + e.setHeader("Call to method `{}` on `{}`", args.name.show(gs), "T.untyped"); + TypeErrorDiagnostics::explainUntyped(gs, e, what, args.fullType, args.originForUninitialized); + } + return DispatchResult(Types::untyped(gs, args.thisType.untypedBlame()), std::move(args.selfType), Symbols::noMethod()); } else if (symbol == Symbols::void_()) { @@ -712,7 +590,12 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg return DispatchResult(Types::untypedUntracked(), std::move(args.selfType), Symbols::noMethod()); } - MethodRef mayBeOverloaded = symbol.data(gs)->findMethodTransitive(gs, args.name); + // TODO(jez) It would be nice to make `core::Symbols::top()` not have `Object` as its ancestor, + // in which case we could simply let the findMethodTransitive run and fail to find any methods + MethodRef mayBeOverloaded; + if (symbol != core::Symbols::top()) { + mayBeOverloaded = symbol.data(gs)->findMethodTransitive(gs, args.name); + } if (!mayBeOverloaded.exists() && gs.requiresAncestorEnabled) { // Before raising any error, we look if the method exists in all required ancestors by this symbol @@ -772,16 +655,12 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg args.name == core::Names::mixesInClassMethods() || (args.name == core::Names::requiresAncestor() && gs.requiresAncestorEnabled)) { auto attachedClass = symbol.data(gs)->attachedClass(gs); - if (auto suggestion = - maybeSuggestExtendModule(gs, attachedClass, args.callLoc(), core::Symbols::T_Helpers())) { - e.addAutocorrect(std::move(*suggestion)); - } + TypeErrorDiagnostics::maybeInsertDSLMethod(gs, e, args.locs.file, args.callLoc(), attachedClass, + Symbols::T_Helpers(), ""); } else if (args.name == core::Names::sig()) { auto attachedClass = symbol.data(gs)->attachedClass(gs); - if (auto suggestion = - maybeSuggestExtendModule(gs, attachedClass, args.callLoc(), core::Symbols::T_Sig())) { - e.addAutocorrect(std::move(*suggestion)); - } + TypeErrorDiagnostics::maybeInsertDSLMethod(gs, e, args.locs.file, args.callLoc(), attachedClass, + Symbols::T_Sig(), ""); } else if (args.receiverLoc().exists() && (gs.suggestUnsafe.has_value() || (args.fullType.type != args.thisType && symbol == Symbols::NilClass()))) { @@ -832,6 +711,11 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg } if (possibleSymbol.isClassOrModule()) { + if (possibleSymbol.asClassOrModuleRef().data(gs)->typeArity(gs) > 0) { + // If this call was in type sytnax, we might have already have built an + // autocorrect to turn this from `MyClass(...)` to `MyClass[...]`. + continue; + } e.addErrorNote("Ruby uses `.new` to invoke a class's constructor"); e.replaceWith("Insert `.new`", args.funLoc().copyEndWithZeroLength(), ".new"); continue; @@ -879,6 +763,19 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg ? guessOverload(gs, symbol, mayBeOverloaded, args.numPosArgs, args.args, targs, args.block != nullptr) : mayBeOverloaded; + if (method.data(gs)->flags.isPrivate && !args.isPrivateOk) { + if (auto e = gs.beginError(errLoc, core::errors::Infer::PrivateMethod)) { + if (args.fullType.type != args.thisType) { + e.setHeader("Non-private call to private method `{}` on `{}` component of `{}`", + method.data(gs)->name.show(gs), args.thisType.show(gs), args.fullType.type.show(gs)); + } else { + e.setHeader("Non-private call to private method `{}` on `{}`", method.data(gs)->name.show(gs), + args.thisType.show(gs)); + } + e.addErrorLine(method.data(gs)->loc(), "Defined in `{}` here", method.data(gs)->owner.show(gs)); + } + } + DispatchResult result; auto &component = result.main; component.receiver = args.selfType; @@ -1133,6 +1030,7 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg } } else if (!Types::isSubTypeUnderConstraint(gs, *constr, kwSplatKeyType, Types::Symbol(), UntypedMode::AlwaysCompatible)) { + // TODO(jez) Highlight untyped code for this error if (auto e = gs.beginError(kwSplatArgLoc, errors::Infer::MethodArgumentMismatch)) { e.setHeader("Expected `{}` but found `{}` for keyword splat keys type", "Symbol", kwSplatKeyType.show(gs)); @@ -1149,6 +1047,7 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg if (kwParamType == nullptr) { kwParamType = Types::untyped(gs, method); } + // TODO(jez) Highlight untyped code for this error if (Types::isSubTypeUnderConstraint(gs, *constr, kwSplatValueType, kwParamType, UntypedMode::AlwaysCompatible)) { continue; @@ -1245,7 +1144,6 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg TypeAndOrigins tpe{hash->values[offset], kwargsLoc}; if (auto e = matchArgType(gs, *constr, args.receiverLoc(), symbol, method, tpe, spec, args.selfType, targs, kwargsLoc, args.originForUninitialized)) { - stopInDebugger(); result.main.errors.emplace_back(std::move(e)); } } @@ -1486,10 +1384,13 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg e.addErrorSection(constr->explain(gs)); result.main.errors.emplace_back(e.build()); } + // This mimics the behavior of the SolveConstraint case in processBinding + resultType = Types::untypedUntracked(); } ENFORCE(!data->arguments.empty(), "Every method should at least have a block arg."); ENFORCE(data->arguments.back().flags.isBlock, "The last arg should be the block arg."); auto blockType = data->arguments.back().type; + // TODO(jez) Highlight untyped code for this error if (blockType && !core::Types::isSubType(gs, core::Types::nilClass(), blockType)) { if (auto e = gs.beginError(args.callLoc().copyEndWithZeroLength(), errors::Infer::BlockNotPassed)) { e.setHeader("`{}` requires a block parameter, but no block was passed", args.name.show(gs)); @@ -1582,10 +1483,17 @@ bool canCallNew(const GlobalState &gs, const TypePtr &wrapped) { auto sym = cast_type_nonnull(wrapped).symbol; if (sym == Symbols::untyped() || sym == Symbols::bottom()) { return false; + } else if (sym.data(gs)->isSingletonClass(gs)) { + return false; } } if (auto *appliedType = cast_type(wrapped)) { + if (appliedType->klass == core::Symbols::Class()) { + // T::Class[...].new is not implemented--users should just use Class.new(super_class) + return false; + } + if (appliedType->klass.data(gs)->isSingletonClass(gs)) { return false; } @@ -1603,12 +1511,33 @@ DispatchResult badMetaTypeCall(const GlobalState &gs, const DispatchArgs &args, const TypePtr &wrapped) { if (auto e = gs.beginError(errLoc, errors::Infer::MetaTypeDispatchCall)) { e.setHeader("Call to method `{}` on `{}` mistakes a type for a value", args.name.show(gs), wrapped.show(gs)); + + if (isa_type(wrapped)) { + auto selfTypeParam = cast_type_nonnull(wrapped); + if (selfTypeParam.definition.isTypeMember()) { + e.addErrorNote("Sorbet erases all generics, so `{}` is never a meaningful, concrete type at runtime.\n" + " If you want to call a method on some class, that class object must be an argument " + "to this method.", + selfTypeParam.show(gs)); + } + } + if (args.name == core::Names::tripleEq()) { if (auto appliedType = cast_type(wrapped)) { e.addErrorNote("It looks like you're trying to pattern match on a generic, " "which doesn't work at runtime"); e.replaceWith("Replace with class name", args.callLoc(), "{}", appliedType->klass.show(gs)); } + } else if (auto *appliedType = cast_type(wrapped)) { + // For T.class_of(Foo), we'll suggest replacing it with the attached class (Foo). + if (appliedType->klass.data(gs)->isSingletonClass(gs)) { + auto receiverLoc = core::Loc(args.locs.file, args.locs.receiver); + e.replaceWith("Replace with class name", receiverLoc, "{}", + appliedType->klass.data(gs)->attachedClass(gs).show(gs)); + } else if (appliedType->klass == core::Symbols::Class()) { + e.addErrorNote("Sorbet erases generics, so `{}` does not work. Use `{}` instead.", "T::Class[...].new", + "Class.new(...)"); + } } } return DispatchResult(Types::untypedUntracked(), std::move(args.selfType), Symbols::noMethod()); @@ -1622,22 +1551,11 @@ DispatchResult MetaType::dispatchCall(const GlobalState &gs, const DispatchArgs switch (args.name.rawId()) { case Names::new_().rawId(): { if (!canCallNew(gs, wrapped)) { - if (auto e = gs.beginError(errLoc, errors::Infer::MetaTypeDispatchCall)) { - e.setHeader("Call to method `{}` on `{}` mistakes a type for a value", Names::new_().show(gs), - wrapped.show(gs)); - - // For T.class_of(Foo), we'll suggest replacing it with the attached class (Foo). - if (auto *appliedType = cast_type(wrapped)) { - if (appliedType->klass.data(gs)->isSingletonClass(gs)) { - auto receiverLoc = core::Loc(args.locs.file, args.locs.receiver); - e.replaceWith("Replace with class name", receiverLoc, "{}", - appliedType->klass.data(gs)->attachedClass(gs).show(gs)); - } - } - } + badMetaTypeCall(gs, args, errLoc, wrapped); return DispatchResult(Types::untypedUntracked(), std::move(args.selfType), Symbols::noMethod()); } + // The Ruby VM treats `initialize` as private by default, but allows calling it directly within `new`. auto innerArgs = DispatchArgs{Names::initialize(), args.locs, args.numPosArgs, @@ -1647,7 +1565,7 @@ DispatchResult MetaType::dispatchCall(const GlobalState &gs, const DispatchArgs wrapped, args.block, args.originForUninitialized, - args.isPrivateOk, + /* isPrivateOk */ true, args.suppressErrors}; auto original = wrapped.dispatchCall(gs, innerArgs); original.returnType = wrapped; @@ -1685,7 +1603,7 @@ DispatchResult MetaType::dispatchCall(const GlobalState &gs, const DispatchArgs auto returns = core::Types::void_(); if (args.name == core::Names::returns()) { - returns = unwrapType(gs, args.argLoc(0), args.args[0]->type); + returns = Types::unwrapType(gs, args.argLoc(0), args.args[0]->type); } // Have to create a new type for the result because dispatchCall is a const member method @@ -1769,6 +1687,13 @@ class T_noreturn : public IntrinsicMethod { } } T_noreturn; +class T_anything : public IntrinsicMethod { +public: + void apply(const GlobalState &gs, const DispatchArgs &args, DispatchResult &res) const override { + res.returnType = make_type(Types::top()); + } +} T_anything; + class T_class_of : public IntrinsicMethod { public: void apply(const GlobalState &gs, const DispatchArgs &args, DispatchResult &res) const override { @@ -1779,7 +1704,7 @@ class T_class_of : public IntrinsicMethod { // The argument to `T.class_of(...)` is a value, but has a type meaning. That means we need // to `unwrapType` to handle things like type aliases and constant literal types. - auto unwrappedType = unwrapType(gs, args.argLoc(0), args.args[0]->type); + auto unwrappedType = Types::unwrapType(gs, args.argLoc(0), args.args[0]->type); auto mustExist = false; auto classSymbol = unwrapSymbol(gs, unwrappedType, mustExist); if (!classSymbol.exists()) { @@ -1871,7 +1796,7 @@ class T_any : public IntrinsicMethod { auto i = -1; for (auto &arg : args.args) { i++; - auto ty = unwrapType(gs, args.argLoc(i), arg->type); + auto ty = Types::unwrapType(gs, args.argLoc(i), arg->type); ret = Types::any(gs, ret, ty); } @@ -1890,7 +1815,7 @@ class T_all : public IntrinsicMethod { auto i = -1; for (auto &arg : args.args) { i++; - auto ty = unwrapType(gs, args.argLoc(i), arg->type); + auto ty = Types::unwrapType(gs, args.argLoc(i), arg->type); ret = Types::all(gs, ret, ty); } @@ -1920,8 +1845,8 @@ class T_nilable : public IntrinsicMethod { return; } - res.returnType = - make_type(Types::any(gs, unwrapType(gs, args.argLoc(0), args.args[0]->type), Types::nilClass())); + res.returnType = make_type( + Types::any(gs, Types::unwrapType(gs, args.argLoc(0), args.args[0]->type), Types::nilClass())); } } T_nilable; @@ -1952,7 +1877,7 @@ class T_proc_params : public IntrinsicMethod { targs.emplace_back(core::Types::todo()); for (size_t i = 1; i < args.args.size(); i += 2) { - auto unwrappedType = unwrapType(gs, args.argLoc(i), args.args[i]->type); + auto unwrappedType = Types::unwrapType(gs, args.argLoc(i), args.args[i]->type); targs.emplace_back(move(unwrappedType)); } @@ -1980,7 +1905,7 @@ class T_proc_returns : public IntrinsicMethod { auto sym = core::Symbols::Proc(0); vector targs; - auto unwrappedType = unwrapType(gs, args.argLoc(0), args.args[0]->type); + auto unwrappedType = Types::unwrapType(gs, args.argLoc(0), args.args[0]->type); targs.emplace_back(move(unwrappedType)); res.returnType = make_type(core::make_type(sym, move(targs))); } @@ -1999,12 +1924,38 @@ class Object_class : public IntrinsicMethod { void apply(const GlobalState &gs, const DispatchArgs &args, DispatchResult &res) const override { auto mustExist = true; ClassOrModuleRef self = unwrapSymbol(gs, args.thisType, mustExist); + auto tClassSelfType = Types::tClass(args.selfType); + if (self.data(gs)->isModule()) { + ENFORCE(gs.requiresAncestorEnabled, "Congrats, you've found a test case. Please add it, then delete this."); + // This normally can't happen, because `Object` is not an ancestor of any module + // instance by default. But Sorbet supports requires ancestor in a really weird way (by + // simply dispatching to a completely unrelated method) which means that sometimes we + // can actually get a call to this on a module. + // + // In the case where the receiver is a module, `singleton` will be `T.class_of(MyModule)` + // which will not actually reflect how `.class` in a module instance method works at runtime. + // (see https://sorbet.org/docs/class-of#tclass_of-and-modules) + res.returnType = tClassSelfType; + return; + } + auto singleton = self.data(gs)->lookupSingletonClass(gs); - if (singleton.exists()) { - res.returnType = singleton.data(gs)->externalType(); - } else { - res.returnType = Types::classClass(); + if (!singleton.exists()) { + res.returnType = tClassSelfType; + return; } + + // `singleton` might have more type members than just the `` one. + // Calling `externalType` is the easiest way to get proper defaults for all of those. + // For the `` type member, we'll default it to its upper bound, like + // T.class_of(MyClass)[MyClass, ...] + // Then the `T.all` is the easiest way to narrow *only* the type argument that + // corresponds to the `` type member, because `T.all` has logic to align + // type members in parent/child classes. The `T.all` gets pushed through and collapsed + // like normal, and ends up something like + // T.class_of(MyClass)[T.all(TypeOfReceiver, MyClass)] + // (This matters, btw, in case the receiver is something like a generic.) + res.returnType = Types::all(gs, tClassSelfType, singleton.data(gs)->externalType()); } } Object_class; @@ -2020,26 +1971,35 @@ class Class_new : public IntrinsicMethod { auto attachedClass = self.data(gs)->attachedClass(gs); if (!attachedClass.exists()) { - if (self == Symbols::Class()) { - // `Class.new(...)`, but it isn't a specific Class. We know - // calling .new on a Class will yield some sort of Object - attachedClass = Symbols::Object(); - } else { - return; - } + // If someone takes `klass: T::Class[T.anything]` and calls `klass.new`, the call is + // actually going to be on an "instance" not a singleton (Class.new, the one on the + // singleton, is the one that defines a new class at runtime). + // + // In that case, there's no attachedClass to look for an `initialize` method on. + // We could _maybe_ imagine trying to dispatch to `initialize` on the `` + // type argument? But I haven't thought about what the consequences of that would be. + ENFORCE(self == Symbols::Class()); + return; } auto instanceTy = attachedClass.data(gs)->externalType(); - DispatchArgs innerArgs{Names::initialize(), args.locs, args.numPosArgs, - args.args, instanceTy, {instanceTy, args.fullType.origins}, - instanceTy, args.block, args.originForUninitialized, - args.isPrivateOk, args.suppressErrors}; + // The Ruby VM treats `initialize` as private by default, but allows calling it directly within `new`. + DispatchArgs innerArgs{Names::initialize(), + args.locs, + args.numPosArgs, + args.args, + instanceTy, + {instanceTy, args.fullType.origins}, + instanceTy, + args.block, + args.originForUninitialized, + /* isPrivateOk */ true, + args.suppressErrors}; auto dispatched = instanceTy.dispatchCall(gs, innerArgs); for (auto &err : res.main.errors) { dispatched.main.errors.emplace_back(std::move(err)); } res.main.errors.clear(); - res.returnType = instanceTy; res.main = move(dispatched.main); if (!res.main.method.exists()) { // If we actually dispatched to some `initialize` method, use that method as the result, @@ -2063,12 +2023,6 @@ class Class_subclasses : public IntrinsicMethod { class T_Generic_squareBrackets : public IntrinsicMethod { public: - // This method is actually special: not only is it called from processBinding in infer, it's - // also called directly by type_syntax parsing in resolver (because this method checks some - // invariants of generics that we want to hold even in `typed: false` files). - // - // Unfortunately, this means that some errors are double reported (once by resolver, and then - // again by infer). void apply(const GlobalState &gs, const DispatchArgs &args, DispatchResult &res) const override { auto mustExist = true; ClassOrModuleRef self = unwrapSymbol(gs, args.thisType, mustExist); @@ -2078,107 +2032,10 @@ class T_Generic_squareBrackets : public IntrinsicMethod { return; } - attachedClass = attachedClass.maybeUnwrapBuiltinGenericForwarder(); - - if (attachedClass.data(gs)->typeMembers().empty()) { - return; - } - - int arity; - if (attachedClass == Symbols::Hash()) { - arity = 2; - } else { - arity = attachedClass.data(gs)->typeArity(gs); - } - - // This is something like Generic[T1,...,foo: bar...] - auto numKwArgs = args.args.size() - args.numPosArgs; - if (numKwArgs > 0) { - auto begin = args.locs.args[args.numPosArgs].beginPos(); - auto end = args.locs.args.back().endPos(); - core::Loc kwargsLoc{args.locs.file, begin, end}; - - if (auto e = gs.beginError(kwargsLoc, errors::Infer::GenericArgumentKeywordArgs)) { - e.setHeader("Keyword arguments given to `{}`", attachedClass.show(gs)); - // offer an autocorrect to turn the keyword args into a hash if there is no double-splat - if (numKwArgs % 2 == 0 && kwargsLoc.exists()) { - e.replaceWith(fmt::format("Wrap with braces"), kwargsLoc, "{{{}}}", kwargsLoc.source(gs).value()); - } - } - } - - if (args.numPosArgs != arity) { - if (auto e = gs.beginError(args.argsLoc(), errors::Infer::GenericArgumentCountMismatch)) { - e.setHeader("Wrong number of type parameters for `{}`. Expected: `{}`, got: `{}`", - attachedClass.show(gs), arity, args.numPosArgs); - } - } - - vector targs; - auto it = args.args.begin(); - int i = -1; - targs.reserve(attachedClass.data(gs)->typeMembers().size()); - for (auto mem : attachedClass.data(gs)->typeMembers()) { - ++i; - - auto memData = mem.data(gs); - - auto *memType = cast_type(memData->resultType); - ENFORCE(memType != nullptr); - - if (memData->flags.isFixed) { - // Fixed args are implicitly applied, and won't consume type - // arguments from the list that's supplied. - targs.emplace_back(memType->upperBound); - } else if (it != args.args.end()) { - auto loc = args.argLoc(it - args.args.begin()); - auto argType = unwrapType(gs, loc, (*it)->type); - bool validBounds = true; - - // Validate type parameter bounds. - if (!Types::isSubType(gs, argType, memType->upperBound)) { - validBounds = false; - if (auto e = gs.beginError(loc, errors::Resolver::GenericTypeParamBoundMismatch)) { - auto argStr = argType.show(gs); - e.setHeader("`{}` is not a subtype of upper bound of type member `{}`", argStr, - mem.showFullName(gs)); - e.addErrorLine(memData->loc(), "`{}` is `{}` bounded by `{}` here", mem.showFullName(gs), - "upper", memType->upperBound.show(gs)); - } - } - - if (!Types::isSubType(gs, memType->lowerBound, argType)) { - validBounds = false; - - if (auto e = gs.beginError(loc, errors::Resolver::GenericTypeParamBoundMismatch)) { - auto argStr = argType.show(gs); - e.setHeader("`{}` is not a supertype of lower bound of type member `{}`", argStr, - mem.showFullName(gs)); - e.addErrorLine(memData->loc(), "`{}` is `{}` bounded by `{}` here", mem.showFullName(gs), - "lower", memType->lowerBound.show(gs)); - } - } - - if (validBounds) { - targs.emplace_back(argType); - } else { - targs.emplace_back(Types::untypedUntracked()); - } - - ++it; - } else if (attachedClass == Symbols::Hash() && i == 2) { - auto tupleArgs = targs; - targs.emplace_back(make_type(tupleArgs)); - } else { - targs.emplace_back(Types::untypedUntracked()); - } - } - - res.returnType = make_type(make_type(attachedClass, move(targs))); + res.returnType = Types::applyTypeArguments(gs, args.locs, args.numPosArgs, args.args, attachedClass); } } T_Generic_squareBrackets; -namespace { void applySig(const GlobalState &gs, const DispatchArgs &args, DispatchResult &res, size_t argsToDropOffEnd) { // We should always have the actual receiver plus whatever args we're going // to ignore for dispatching purposes. @@ -2206,7 +2063,6 @@ void applySig(const GlobalState &gs, const DispatchArgs &args, DispatchResult &r recv.type, args.block, args.originForUninitialized, args.isPrivateOk, args.suppressErrors}); } -} // namespace class SorbetPrivateStatic_sig : public IntrinsicMethod { public: @@ -2378,15 +2234,6 @@ class Magic_callWithSplat : public IntrinsicMethod { if (args.args.size() != 4) { return; } - auto &receiver = args.args[0]; - if (receiver->type.isUntyped()) { - res.returnType = receiver->type; - return; - } - - if (!receiver->type.isFullyDefined()) { - return; - } if (!isa_type(args.args[1]->type)) { return; @@ -2397,7 +2244,30 @@ class Magic_callWithSplat : public IntrinsicMethod { } NameRef fn = lit.asName(); + + auto &receiver = args.args[0]; + if (receiver->type.isUntyped()) { + auto what = core::errors::Infer::errorClassForUntyped(gs, args.locs.file, receiver->type); + if (auto e = gs.beginError(args.argLoc(0), what)) { + e.setHeader("Call to method `{}` on `{}`", fn.show(gs), "T.untyped"); + TypeErrorDiagnostics::explainUntyped(gs, e, what, *args.args[0], args.originForUninitialized); + } + + res.returnType = receiver->type; + return; + } + + if (!receiver->type.isFullyDefined()) { + return; + } + if (args.args[2]->type.isUntyped()) { + auto what = core::errors::Infer::errorClassForUntyped(gs, args.locs.file, args.args[2]->type); + if (auto e = gs.beginError(args.argLoc(2), what)) { + e.setHeader("Call to method `{}` with `{}` splat arguments", fn.show(gs), "T.untyped"); + TypeErrorDiagnostics::explainUntyped(gs, e, what, *args.args[2], args.originForUninitialized); + } + res.returnType = args.args[2]->type; return; } @@ -2459,9 +2329,17 @@ class Magic_callWithBlock : public IntrinsicMethod { private: static TypePtr typeToProc(const GlobalState &gs, const TypeAndOrigins &blockType, core::FileRef file, LocOffsets callLoc, LocOffsets receiverLoc, LocOffsets funLoc, Loc originForUninitialized, - bool isPrivateOk, bool suppressErrors) { + bool suppressErrors) { auto nonNilBlockType = blockType; auto typeIsNilable = false; + if (blockType.type.isUntyped()) { + // Don't simulate a call to `to_proc` on `T.untyped` + // This avoids reporting a typed: strong error for `&x` where `x` is untyped--we may + // still want to report an error later when matching this `T.untyped` we're about to + // return with the method's block parameter. + return blockType.type; + } + if (Types::isSubType(gs, Types::nilClass(), blockType.type)) { nonNilBlockType = TypeAndOrigins{Types::dropNil(gs, blockType.type), blockType.origins}; typeIsNilable = true; @@ -2483,7 +2361,7 @@ class Magic_callWithBlock : public IntrinsicMethod { nonNilBlockType.type, nullptr, originForUninitialized, - isPrivateOk, + IMPLICIT_CONVERSION_ALLOWS_PRIVATE, suppressErrors}; auto dispatched = nonNilBlockType.type.dispatchCall(gs, innerArgs); for (auto &err : dispatched.main.errors) { @@ -2540,6 +2418,7 @@ class Magic_callWithBlock : public IntrinsicMethod { // as we do the subtyping check. auto &constr = dispatched.main.constr; auto &blockPreType = dispatched.main.blockPreType; + // TODO(jez) How should this interact with highlight untyped? if (blockPreType && !Types::isSubTypeUnderConstraint(gs, *constr, passedInBlockType, blockPreType, UntypedMode::AlwaysCompatible)) { auto nonNilableBlockType = Types::dropNil(gs, blockPreType); @@ -2584,6 +2463,7 @@ class Magic_callWithBlock : public IntrinsicMethod { auto bspecType = bspec.type; if (bspecType) { + // TODO(jez) How should this interact with highlight untyped? // This subtype check is here to discover the correct generic bounds. Types::isSubTypeUnderConstraint(gs, *constr, passedInBlockType, bspecType, UntypedMode::AlwaysCompatible); @@ -2626,8 +2506,24 @@ class Magic_callWithBlock : public IntrinsicMethod { if (args.args.size() < 3) { return; } + + if (!isa_type(args.args[1]->type)) { + return; + } + auto lit = cast_type_nonnull(args.args[1]->type); + if (!lit.derivesFrom(gs, Symbols::Symbol())) { + return; + } + + NameRef fn = lit.asName(); auto &receiver = args.args[0]; if (receiver->type.isUntyped()) { + auto what = core::errors::Infer::errorClassForUntyped(gs, args.locs.file, receiver->type); + if (auto e = gs.beginError(args.argLoc(0), what)) { + e.setHeader("Call to method `{}` on `{}`", fn.show(gs), "T.untyped"); + TypeErrorDiagnostics::explainUntyped(gs, e, what, args.fullType, args.originForUninitialized); + } + res.returnType = receiver->type; return; } @@ -2643,16 +2539,6 @@ class Magic_callWithBlock : public IntrinsicMethod { return; } - if (!isa_type(args.args[1]->type)) { - return; - } - auto lit = cast_type_nonnull(args.args[1]->type); - if (!lit.derivesFrom(gs, Symbols::Symbol())) { - return; - } - - NameRef fn = lit.asName(); - uint16_t numPosArgs = args.numPosArgs - 3; InlinedVector sendArgStore; InlinedVector sendArgLocs; @@ -2667,9 +2553,9 @@ class Magic_callWithBlock : public IntrinsicMethod { } CallLocs sendLocs{args.locs.file, args.locs.call, args.locs.args[0], args.locs.fun, sendArgLocs}; - TypePtr finalBlockType = Magic_callWithBlock::typeToProc( - gs, *args.args[2], args.locs.file, args.locs.call, args.locs.args[2], args.locs.fun, - args.originForUninitialized, args.isPrivateOk, args.suppressErrors); + TypePtr finalBlockType = + Magic_callWithBlock::typeToProc(gs, *args.args[2], args.locs.file, args.locs.call, args.locs.args[2], + args.locs.fun, args.originForUninitialized, args.suppressErrors); std::optional blockArity = Magic_callWithBlock::getArityForBlock(finalBlockType); auto link = make_shared(fn, Magic_callWithBlock::argInfoByArity(blockArity), -1); res.main.constr = make_unique(); @@ -2711,15 +2597,6 @@ class Magic_callWithSplatAndBlock : public IntrinsicMethod { if (args.args.size() != 5) { return; } - auto &receiver = args.args[0]; - if (receiver->type.isUntyped()) { - res.returnType = receiver->type; - return; - } - - if (!receiver->type.isFullyDefined()) { - return; - } if (!isa_type(args.args[1]->type)) { return; @@ -2731,7 +2608,29 @@ class Magic_callWithSplatAndBlock : public IntrinsicMethod { NameRef fn = lit.asName(); + auto &receiver = args.args[0]; + if (receiver->type.isUntyped()) { + auto what = core::errors::Infer::errorClassForUntyped(gs, args.locs.file, receiver->type); + if (auto e = gs.beginError(args.argLoc(0), what)) { + e.setHeader("Call to method `{}` on `{}`", fn.show(gs), "T.untyped"); + TypeErrorDiagnostics::explainUntyped(gs, e, what, *args.args[0], args.originForUninitialized); + } + + res.returnType = receiver->type; + return; + } + + if (!receiver->type.isFullyDefined()) { + return; + } + if (args.args[2]->type.isUntyped()) { + auto what = core::errors::Infer::errorClassForUntyped(gs, args.locs.file, args.args[2]->type); + if (auto e = gs.beginError(args.argLoc(2), what)) { + e.setHeader("Call to method `{}` with `{}` splat arguments", fn.show(gs), "T.untyped"); + TypeErrorDiagnostics::explainUntyped(gs, e, what, *args.args[2], args.originForUninitialized); + } + res.returnType = args.args[2]->type; return; } @@ -2768,9 +2667,9 @@ class Magic_callWithSplatAndBlock : public IntrinsicMethod { InlinedVector sendArgLocs(sendArgs.size(), args.locs.args[2]); CallLocs sendLocs{args.locs.file, args.locs.call, args.locs.args[0], args.locs.fun, sendArgLocs}; - TypePtr finalBlockType = Magic_callWithBlock::typeToProc( - gs, *args.args[4], args.locs.file, args.locs.call, args.locs.args[4], args.locs.fun, - args.originForUninitialized, args.isPrivateOk, args.suppressErrors); + TypePtr finalBlockType = + Magic_callWithBlock::typeToProc(gs, *args.args[4], args.locs.file, args.locs.call, args.locs.args[4], + args.locs.fun, args.originForUninitialized, args.suppressErrors); std::optional blockArity = Magic_callWithBlock::getArityForBlock(finalBlockType); auto link = make_shared(fn, Magic_callWithBlock::argInfoByArity(blockArity), -1); res.main.constr = make_unique(); @@ -2808,7 +2707,7 @@ class Magic_suggestUntypedConstantType : public IntrinsicMethod { if (auto e = gs.beginError(loc, core::errors::Infer::UntypedConstantSuggestion)) { e.setHeader("Constants must have type annotations with `{}` when specifying `{}`", "T.let", "# typed: strict"); - if (!ty.isUntyped() && loc.exists() && argLocExists) { + if ((gs.suggestUnsafe || !ty.isUntyped()) && loc.exists() && argLocExists) { // (skip the autocorrect if we had to fall back to using callLoc, because using that // will suggest something syntactically invalid like `T.let(U = begin; end, NilClass))` e.replaceWith(fmt::format("Initialize as `{}`", ty.show(gs)), loc, "T.let({}, {})", @@ -2844,7 +2743,7 @@ class Magic_suggestUntypedFieldType : public IntrinsicMethod { e.setHeader("The {} variable `{}` must be declared using `{}` when specifying `{}`", fieldKind, fieldName, "T.let", "# typed: strict"); auto replaceLoc = args.argLoc(0); - if (replaceLoc.exists()) { + if ((gs.suggestUnsafe.has_value() || !suggestType.isUntyped()) && replaceLoc.exists()) { // Loc might not exist be because our argument was an EmptyTree (`begin; end`). // In that case we don't have an RHS we can easily wrap in something, so skip the autocorrect. auto title = fmt::format("Initialize as `{}`", suggestType.show(gs)); @@ -2860,69 +2759,6 @@ class Magic_suggestUntypedFieldType : public IntrinsicMethod { } } Magic_suggestUntypedFieldType; -/** - * This is a special version of `new` that will return `T.attached_class` - * instead. - */ -class Magic_selfNew : public IntrinsicMethod { -public: - vector dispatchesTo() const override { - // Technically only dispatches to `new` but we manually flatten the chain to avoid having to - // compute the transitive closure of dispatchesTo. - return {core::Names::new_(), core::Names::initialize()}; - } - - void apply(const GlobalState &gs, const DispatchArgs &args, DispatchResult &res) const override { - // args[0] is the Class to create an instance of - // args[1..] are the arguments to the constructor - - if (args.args.empty()) { - res.returnType = core::Types::untypedUntracked(); - return; - } - - auto selfTy = *args.args[0]; - auto mustExist = true; - ClassOrModuleRef self = unwrapSymbol(gs, selfTy.type, mustExist); - - uint16_t numPosArgs = args.numPosArgs - 1; - - InlinedVector sendArgStore; - InlinedVector sendArgLocs; - for (int i = 1; i < args.args.size(); ++i) { - sendArgStore.emplace_back(args.args[i]); - sendArgLocs.emplace_back(args.locs.args[i]); - } - CallLocs sendLocs{args.locs.file, args.locs.call, args.locs.args[0], args.locs.fun, sendArgLocs}; - - DispatchArgs innerArgs{Names::new_(), sendLocs, numPosArgs, - sendArgStore, selfTy.type, selfTy, - selfTy.type, args.block, args.originForUninitialized, - args.isPrivateOk, args.suppressErrors}; - auto dispatched = selfTy.type.dispatchCall(gs, innerArgs); - auto returnTy = dispatched.returnType; - - // If we actually dispatch to something that looks like a construtor, replace return with `T.attached_class` - if (self.data(gs)->isSingletonClass(gs) && dispatched.main.method.exists() && - (dispatched.main.method == core::Symbols::Class_new() || - dispatched.main.method.data(gs)->name == core::Names::initialize())) { - // AttachedClass will only be missing on `T.untyped`, which will have a dispatch component of noSymbol - auto attachedClass = self.data(gs)->findMember(gs, core::Names::Constants::AttachedClass()); - ENFORCE(attachedClass.exists()); - - returnTy = make_type(attachedClass); - } - - for (auto &err : res.main.errors) { - dispatched.main.errors.emplace_back(std::move(err)); - } - res.main.errors.clear(); - res.main = move(dispatched.main); - res.returnType = returnTy; - res.main.sendTp = returnTy; - } -} Magic_selfNew; - class Magic_attachedClass : public IntrinsicMethod { public: void apply(const GlobalState &gs, const DispatchArgs &args, DispatchResult &res) const override { @@ -2935,22 +2771,34 @@ class Magic_attachedClass : public IntrinsicMethod { auto selfTy = *args.args[0]; auto mustExist = true; auto self = unwrapSymbol(gs, selfTy.type, mustExist); + auto selfData = self.data(gs); - if (self.data(gs)->isSingletonClass(gs)) { - auto attachedClass = self.data(gs)->findMember(gs, core::Names::Constants::AttachedClass()); - ENFORCE(attachedClass.exists()); + auto attachedClass = selfData->findMember(gs, core::Names::Constants::AttachedClass()); + if (attachedClass.exists()) { res.returnType = make_type(make_type(attachedClass)); } else if (self != core::Symbols::T_Private_Methods_DeclBuilder() && !args.suppressErrors) { if (auto e = gs.beginError(args.callLoc(), core::errors::Infer::AttachedClassOnInstance)) { - e.setHeader("`{}` may only be used in a singleton class method context", "T.attached_class"); - e.addErrorSection(selfTy.explainGot(gs, args.originForUninitialized)); - auto singletonClass = self.data(gs)->lookupSingletonClass(gs); - if (singletonClass.exists()) { - e.addErrorNote( - "`{}` represents instances of a class; `{}` represents the corresponding singleton class", - self.show(gs), singletonClass.show(gs)); + auto hasAttachedClass = core::Names::declareHasAttachedClass().show(gs); + if (selfData->isModule()) { + e.setHeader("`{}` must declare `{}` before module instance methods can use `{}`", self.show(gs), + hasAttachedClass, "T.attached_class"); + // TODO(jez) Autocorrect to insert `has_attached_class!` + } else if (selfData->isSingletonClass(gs)) { + // Combination of `isSingletonClass` and `` missing means + // this is the singleton class of a module. + ENFORCE(selfData->attachedClass(gs).data(gs)->isModule()); + e.setHeader("`{}` cannot be used in singleton methods on modules, because modules cannot be " + "instantiated", + "T.attached_class"); } else { - e.addErrorNote("`{}` represents instances of a class", self.show(gs)); + // Technically, this error message should also have something like "..., or + // instance methods on `::Class`", but that makes the error message wordy, and + // anyone who cares about that technicality likely knows what they're doing. + e.setHeader( + "`{}` may only be used in singleton methods on classes or instance methods on `{}` modules", + "T.attached_class", hasAttachedClass); + e.addErrorNote("Current context is `{}`, which is an instance class not a singleton class", + self.show(gs)); } } res.returnType = core::Types::untypedUntracked(); @@ -3038,7 +2886,8 @@ class Magic_checkAndAnd : public IntrinsicMethod { selfTyAndAnd.type, args.block, args.originForUninitialized, - args.isPrivateOk, + // We already reported one visibility error, if relevant + /* isPrivateOk */ true, args.suppressErrors, }; auto retried = selfTyAndAnd.type.dispatchCall(gs, newInnerArgs); @@ -3122,7 +2971,7 @@ class Magic_splat : public IntrinsicMethod { arg->type, nullptr, args.originForUninitialized, - args.isPrivateOk, + IMPLICIT_CONVERSION_ALLOWS_PRIVATE, args.suppressErrors}; auto dispatched = arg->type.dispatchCall(gs, dispatch); @@ -3328,8 +3177,6 @@ class Tuple_concat : public IntrinsicMethod { } } Tuple_concat; -namespace { - optional locOfValueForKey(const GlobalState &gs, const Loc origin, const NameRef key, const TypePtr expectedType) { if (!isa_type(expectedType)) { return nullopt; @@ -3375,8 +3222,6 @@ optional locOfValueForKey(const GlobalState &gs, const Loc origin, const Na return nullopt; } -} // namespace - class Shape_squareBracketsEq : public IntrinsicMethod { public: void apply(const GlobalState &gs, const DispatchArgs &args, DispatchResult &res) const override { @@ -3398,6 +3243,7 @@ class Shape_squareBracketsEq : public IntrinsicMethod { auto expectedType = valueType; auto actualType = *args.args[1]; // This check (with the dropLiteral's) mimicks what we do for pinning errors in environment.cc + // TODO(jez) How should this interact with highlight untyped? if (!Types::isSubType(gs, Types::dropLiteral(gs, actualType.type), Types::dropLiteral(gs, expectedType))) { auto argLoc = args.argLoc(1); @@ -3536,7 +3382,7 @@ class Magic_toHash : public IntrinsicMethod { arg->type, nullptr, args.originForUninitialized, - args.isPrivateOk, + IMPLICIT_CONVERSION_ALLOWS_PRIVATE, args.suppressErrors}; res = arg->type.dispatchCall(gs, dispatch); } @@ -3652,8 +3498,6 @@ class Magic_mergeHashValues : public IntrinsicMethod { } } Magic_mergeHashValues; -namespace { - void digImplementation(const GlobalState &gs, const DispatchArgs &args, DispatchResult &res, NameRef methodToDigWith) { if (args.args.size() == 0 || args.numPosArgs != args.args.size()) { // A type error was already reported for arg mismatch @@ -3760,8 +3604,6 @@ void digImplementation(const GlobalState &gs, const DispatchArgs &args, Dispatch res.returnType = move(recursiveDispatch.returnType); } -} // namespace - class Hash_dig : public IntrinsicMethod { public: void apply(const GlobalState &gs, const DispatchArgs &args, DispatchResult &res) const override { @@ -3800,7 +3642,7 @@ class Array_flatten : public IntrinsicMethod { type, nullptr, args.originForUninitialized, - args.isPrivateOk, + IMPLICIT_CONVERSION_ALLOWS_PRIVATE, args.suppressErrors}; auto dispatched = type.dispatchCall(gs, innerArgs); @@ -3950,86 +3792,6 @@ class Array_compact : public IntrinsicMethod { } } Array_compact; -class Array_plus : public IntrinsicMethod { -public: - vector dispatchesTo() const override { - return {core::Names::concat()}; - } - - void apply(const GlobalState &gs, const DispatchArgs &args, DispatchResult &res) const override { - if (args.suppressErrors || res.main.errors.empty() || args.numPosArgs != 1) { - return; - } - - const auto finder = [](const auto &e) { return e->what == core::errors::Infer::MethodArgumentMismatch; }; - if (absl::c_count_if(res.main.errors, finder) != 1) { - // Want exactly one, not at least one - return; - } - - auto dispatchArgs = DispatchArgs{ - core::Names::concat(), - args.locs, - args.numPosArgs, - // x.+(y) is the same arity as x.concat(y) - args.args, - args.selfType, - args.fullType, - // Reset thisType to selfType. dispatchCallProxyType will widen to underlying if needed - args.selfType, - args.block, - args.originForUninitialized, - args.isPrivateOk, - args.suppressErrors, - }; - auto dispatched = args.selfType.dispatchCall(gs, dispatchArgs); - - if (!dispatched.main.errors.empty()) { - return; - } - - const auto iter = absl::c_find_if(res.main.errors, finder); - ENFORCE(iter != res.main.errors.end(), "c_count above should have guaranteed a result"); - - const auto argMismatchErrorIdx = std::distance(res.main.errors.begin(), iter); - const auto &argMismatchError = *iter; - - if (auto e = gs.beginError(argMismatchError->loc, core::errors::Infer::MethodArgumentMismatch)) { - e.setHeader("{}", argMismatchError->header); - // This copies the section intentionally (no auto&) to hack around const-ness - for (auto section : argMismatchError->sections) { - e.addErrorSection(std::move(section)); - } - // This copies the section intentionally (no auto&) to hack around const-ness - for (auto autocorrect : argMismatchError->autocorrects) { - e.addAutocorrect(std::move(autocorrect)); - } - - e.addErrorNote("If the desired behavior is to widen the type to `{}`, use `{}` instead", - dispatched.returnType.show(gs), "Array#concat"); - - auto replaceBegin = args.locs.receiver.endPos(); - auto replaceEnd = args.locs.call.endPos(); - auto replaceLoc = core::Loc(args.locs.file, replaceBegin, replaceEnd); - if (replaceLoc.exists() && args.locs.args[0].exists()) { - auto arg0Loc = core::Loc(args.locs.file, args.locs.args[0]); - auto replaceLocSource = replaceLoc.source(gs).value(); - if (absl::StartsWith(absl::StripLeadingAsciiWhitespace(replaceLocSource), "+=")) { - auto recvSource = core::Loc(args.locs.file, args.locs.receiver).source(gs); - if (recvSource.has_value()) { - e.replaceWith("Replace with `concat`", replaceLoc, " = {}.concat({})", recvSource.value(), - arg0Loc.source(gs).value()); - } - } else { - e.replaceWith("Replace with `concat`", replaceLoc, ".concat({})", arg0Loc.source(gs).value()); - } - } - - res.main.errors[argMismatchErrorIdx] = e.build(); - } - } -} Array_plus; - class Array_zip : public IntrinsicMethod { public: void apply(const GlobalState &gs, const DispatchArgs &args, DispatchResult &res) const override { @@ -4061,6 +3823,70 @@ class Array_zip : public IntrinsicMethod { } } Array_zip; +class Symbol_eqeq : public IntrinsicMethod { +public: + void apply(const GlobalState &gs, const DispatchArgs &args, DispatchResult &res) const override { + if (args.args.size() != 1) { + return; + } + + // NOTE: + // If you update this, please update error-reference to mention which types this check applies to + // TODO(jez) How should this interact with highlight untyped? + auto isOnlySymbol = + Types::isSubType(gs, args.fullType.type, Types::any(gs, Types::nilClass(), Types::Symbol())); + if (isOnlySymbol && Types::all(gs, args.fullType.type, args.args[0]->type).isBottom()) { + auto funLoc = args.funLoc(); + auto errLoc = (funLoc.exists() && !funLoc.empty()) ? funLoc : args.callLoc(); + if (auto e = gs.beginError(errLoc, errors::Infer::NonOverlappingEqual)) { + e.setHeader("Comparison between `{}` and `{}` is always false", args.fullType.type.show(gs), + args.args[0]->type.show(gs)); + e.addErrorSection(args.fullType.explainGot(gs, args.originForUninitialized)); + e.addErrorSection(args.args[0]->explainGot(gs, args.originForUninitialized)); + if (args.args[0]->type == Types::String() && args.argLoc(0).exists()) { + e.replaceWith("Convert arg to Symbol", args.argLoc(0).copyEndWithZeroLength(), ".to_sym"); + } + } + } + } +} Symbol_eqeq; + +class String_eqeq : public IntrinsicMethod { +public: + void apply(const GlobalState &gs, const DispatchArgs &args, DispatchResult &res) const override { + if (args.args.size() != 1) { + return; + } + + // TODO(jez) How should this interact with highlight untyped? + auto isOnlyString = + Types::isSubType(gs, args.fullType.type, Types::any(gs, Types::nilClass(), Types::String())); + + // NOTE: + // If you update this, please update error-reference to mention which types this check applies to + if (isOnlyString && + // This extra `isSubType` is here (not in Symbol_eqeq) because of how implicit String#== + // allows implicit conversions with `to_str`. In essence, this check implements the + // assumption that `Symbol` is final and thus no subclass can implement `to_str` (we + // could also implement the actual final logic for arbitrary classes, but have opted not + // to because it would likely be a cause for surprise). + Types::isSubType(gs, args.args[0]->type, Types::any(gs, Types::nilClass(), Types::Symbol())) && + Types::all(gs, args.fullType.type, args.args[0]->type).isBottom()) { + auto funLoc = args.funLoc(); + auto errLoc = (funLoc.exists() && !funLoc.empty()) ? funLoc : args.callLoc(); + if (auto e = gs.beginError(errLoc, errors::Infer::NonOverlappingEqual)) { + e.setHeader("Comparison between `{}` and `{}` is always false", args.fullType.type.show(gs), + args.args[0]->type.show(gs)); + e.addErrorSection(args.fullType.explainGot(gs, args.originForUninitialized)); + e.addErrorSection(args.args[0]->explainGot(gs, args.originForUninitialized)); + if (args.args[0]->type == Types::Symbol() && args.receiverLoc().exists()) { + e.replaceWith("Convert arg to Symbol", args.receiverLoc().copyEndWithZeroLength(), ".to_sym"); + } + } + } + } +} String_eqeq; + class Kernel_proc : public IntrinsicMethod { public: void apply(const GlobalState &gs, const DispatchArgs &args, DispatchResult &res) const override { @@ -4079,6 +3905,76 @@ class Kernel_proc : public IntrinsicMethod { } } Kernel_proc; +class Kernel_raise : public IntrinsicMethod { +public: + vector dispatchesTo() const override { + // Technically only dispatches to `new` but we manually flatten the chain to avoid having to + // compute the transitive closure of dispatchesTo. + return {core::Names::new_(), core::Names::initialize()}; + } + + void apply(const GlobalState &gs, const DispatchArgs &args, DispatchResult &res) const override { + if (args.args.size() < 1) { + return; + } + auto classArg = args.args[0]; + + // The isUntyped check is part performance optimization and part "correctness": + // - no need to run another dispatch if it's just going to be untyped, but also... + // - arguably none of this intrinsic should run if we don't know that arg0 isn't a Class + // + // Technically speaking, the Ruby VM actually implements this by way of calling + // arg0.exception. It just so happens that Exception.exception is defined to forward to + // `.new` (https://ruby-doc.org/core-2.7.2/Exception.html#method-c-exception) but anything + // that defines a method called `exception` could be called. We don't implement that here, + // because implicit conversions are hard in the presence of subtyping. Instead, we restrict + // the check to only subclasses of `Exception`, not arbitrary classes. + auto classOfException = make_type(Symbols::Exception().data(gs)->lookupSingletonClass(gs)); + if (!classArg->type.isUntyped() && !Types::isSubType(gs, classArg->type, classOfException)) { + return; + } + + uint16_t newNumPosArgs = args.args.size() >= 2 ? 1 : 0; + auto newSendArgLocs = InlinedVector(); + if (newNumPosArgs > 0) { + newSendArgLocs.emplace_back(args.locs.args[1]); + } + auto newCallLocs = CallLocs{ + args.locs.file, + /* callLoc */ args.locs.args[0].join(args.locs.args[newNumPosArgs]), + /* receiverLoc */ args.locs.args[0], + /* funLoc */ args.locs.args[0].copyEndWithZeroLength(), + newSendArgLocs, + }; + + auto newSendArgs = InlinedVector(); + if (newNumPosArgs) { + newSendArgs.emplace_back(args.args[1]); + } + + DispatchArgs newArgs{ + Names::new_(), + newCallLocs, + newNumPosArgs, + newSendArgs, + classArg->type, + *classArg, + classArg->type, + /* block */ nullptr, + args.originForUninitialized, + IMPLICIT_CONVERSION_ALLOWS_PRIVATE, + args.suppressErrors, + }; + auto dispatched = classArg->type.dispatchCall(gs, newArgs); + + for (auto it = &dispatched; it != nullptr; it = it->secondary.get()) { + for (auto &err : it->main.errors) { + res.main.errors.emplace_back(std::move(err)); + } + } + } +} Kernel_raise; + class Enumerable_toH : public IntrinsicMethod { public: vector dispatchesTo() const override { @@ -4245,6 +4141,7 @@ const vector intrinsics{ {Symbols::T(), Intrinsic::Kind::Singleton, Names::nilable(), &T_nilable}, {Symbols::T(), Intrinsic::Kind::Singleton, Names::revealType(), &T_revealType}, {Symbols::T(), Intrinsic::Kind::Singleton, Names::noreturn(), &T_noreturn}, + {Symbols::T(), Intrinsic::Kind::Singleton, Names::anything(), &T_anything}, {Symbols::T(), Intrinsic::Kind::Singleton, Names::classOf(), &T_class_of}, {Symbols::T(), Intrinsic::Kind::Singleton, Names::selfType(), &T_self_type}, {Symbols::T(), Intrinsic::Kind::Singleton, Names::attachedClass(), &T_attached_class}, @@ -4263,8 +4160,10 @@ const vector intrinsics{ {Symbols::T_Enumerable(), Intrinsic::Kind::Singleton, Names::squareBrackets(), &T_Generic_squareBrackets}, {Symbols::T_Enumerator(), Intrinsic::Kind::Singleton, Names::squareBrackets(), &T_Generic_squareBrackets}, {Symbols::T_Enumerator_Lazy(), Intrinsic::Kind::Singleton, Names::squareBrackets(), &T_Generic_squareBrackets}, + {Symbols::T_Enumerator_Chain(), Intrinsic::Kind::Singleton, Names::squareBrackets(), &T_Generic_squareBrackets}, {Symbols::T_Range(), Intrinsic::Kind::Singleton, Names::squareBrackets(), &T_Generic_squareBrackets}, {Symbols::T_Set(), Intrinsic::Kind::Singleton, Names::squareBrackets(), &T_Generic_squareBrackets}, + {Symbols::T_Class(), Intrinsic::Kind::Singleton, Names::squareBrackets(), &T_Generic_squareBrackets}, {Symbols::Object(), Intrinsic::Kind::Instance, Names::class_(), &Object_class}, {Symbols::Object(), Intrinsic::Kind::Instance, Names::singletonClass(), &Object_class}, @@ -4285,7 +4184,6 @@ const vector intrinsics{ {Symbols::Magic(), Intrinsic::Kind::Singleton, Names::callWithSplatAndBlock(), &Magic_callWithSplatAndBlock}, {Symbols::Magic(), Intrinsic::Kind::Singleton, Names::suggestConstantType(), &Magic_suggestUntypedConstantType}, {Symbols::Magic(), Intrinsic::Kind::Singleton, Names::suggestFieldType(), &Magic_suggestUntypedFieldType}, - {Symbols::Magic(), Intrinsic::Kind::Singleton, Names::selfNew(), &Magic_selfNew}, {Symbols::Magic(), Intrinsic::Kind::Singleton, Names::attachedClass(), &Magic_attachedClass}, {Symbols::Magic(), Intrinsic::Kind::Singleton, Names::checkAndAnd(), &Magic_checkAndAnd}, {Symbols::Magic(), Intrinsic::Kind::Singleton, Names::splat(), &Magic_splat}, @@ -4315,11 +4213,15 @@ const vector intrinsics{ {Symbols::Array(), Intrinsic::Kind::Instance, Names::flatten(), &Array_flatten}, {Symbols::Array(), Intrinsic::Kind::Instance, Names::product(), &Array_product}, {Symbols::Array(), Intrinsic::Kind::Instance, Names::compact(), &Array_compact}, - {Symbols::Array(), Intrinsic::Kind::Instance, Names::plus(), &Array_plus}, {Symbols::Array(), Intrinsic::Kind::Instance, Names::zip(), &Array_zip}, + {Symbols::Symbol(), Intrinsic::Kind::Instance, Names::eqeq(), &Symbol_eqeq}, + {Symbols::String(), Intrinsic::Kind::Instance, Names::eqeq(), &String_eqeq}, + {Symbols::Kernel(), Intrinsic::Kind::Instance, Names::proc(), &Kernel_proc}, {Symbols::Kernel(), Intrinsic::Kind::Instance, Names::lambda(), &Kernel_proc}, + {Symbols::Kernel(), Intrinsic::Kind::Instance, Names::raise(), &Kernel_raise}, + {Symbols::Kernel(), Intrinsic::Kind::Instance, Names::fail(), &Kernel_raise}, {Symbols::Enumerable(), Intrinsic::Kind::Instance, Names::toH(), &Enumerable_toH}, diff --git a/core/types/printing.cc b/core/types/printing.cc index 60dbfcfe4c..0283476b5f 100644 --- a/core/types/printing.cc +++ b/core/types/printing.cc @@ -2,7 +2,7 @@ #include "absl/strings/escaping.h" #include "absl/strings/match.h" #include "common/common.h" -#include "common/formatting.h" +#include "common/strings/formatting.h" #include "core/Context.h" #include "core/Names.h" #include "core/Symbols.h" @@ -54,16 +54,7 @@ string argTypeForUnresolvedAppliedType(const GlobalState &gs, const TypePtr &t, string UnresolvedAppliedType::show(const GlobalState &gs, ShowOptions options) const { string resolvedString = options.showForRBI ? "" : " (unresolved)"; - ClassOrModuleRef symForPrinting; - - if (options.showForRBI) { - auto attachedClass = this->klass.data(gs)->attachedClass(gs); - symForPrinting = attachedClass; - } else { - symForPrinting = this->klass; - } - - return fmt::format("{}[{}]{}", symForPrinting.show(gs, options), + return fmt::format("{}[{}]{}", this->klass.show(gs, options), fmt::map_join(targs, ", ", [&](auto targ) { return options.showForRBI ? argTypeForUnresolvedAppliedType(gs, targ, options) @@ -444,10 +435,14 @@ string AppliedType::show(const GlobalState &gs, ShowOptions options) const { fmt::format_to(std::back_inserter(buf), "T::Enumerator"); } else if (this->klass == Symbols::Enumerator_Lazy()) { fmt::format_to(std::back_inserter(buf), "T::Enumerator::Lazy"); + } else if (this->klass == Symbols::Enumerator_Chain()) { + fmt::format_to(std::back_inserter(buf), "T::Enumerator::Chain"); } else if (this->klass == Symbols::Range()) { fmt::format_to(std::back_inserter(buf), "T::Range"); } else if (this->klass == Symbols::Set()) { fmt::format_to(std::back_inserter(buf), "T::Set"); + } else if (this->klass == Symbols::Class()) { + fmt::format_to(std::back_inserter(buf), "T::Class"); } else { if (std::optional procArity = Types::getProcArity(*this)) { fmt::format_to(std::back_inserter(buf), "T.proc"); @@ -498,7 +493,17 @@ string AppliedType::show(const GlobalState &gs, ShowOptions options) const { auto tm = typeMember; if (tm.data(gs)->flags.isFixed) { it = targs.erase(it); - } else if (typeMember.data(gs)->name == core::Names::Constants::AttachedClass()) { + } else if (this->klass.data(gs)->isSingletonClass(gs) && + typeMember.data(gs)->name == core::Names::Constants::AttachedClass() && + // We only want to hide the arg if it's the same as the default. + // (Things like `T.all` can make this upper bound more narrow than the default.) + // Relies on the fact that the common case is for the upperBound to be a + // ClassType (most classes are not generic), and ClassTypes can be compared with + // `==` because they are inlined (instead of being behind pointers). + (cast_type(typeMember.data(gs)->resultType)->upperBound == *it || + // This side handles the selfType case, which is how we compute the initial type + // of in builder_entry. + (isa_type(*it) && cast_type_nonnull(*it).definition == typeMember))) { it = targs.erase(it); } else if (this->klass == Symbols::Hash() && typeMember == typeMembers.back()) { it = targs.erase(it); diff --git a/core/types/subtyping.cc b/core/types/subtyping.cc index 4af418a149..2e4600cfb9 100644 --- a/core/types/subtyping.cc +++ b/core/types/subtyping.cc @@ -286,8 +286,8 @@ TypePtr Types::lub(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2) return OrType::make_shared(t1, t2); } - bool ltr = a1->klass == a2->klass || a2->klass.data(gs)->derivesFrom(gs, a1->klass); - bool rtl = !ltr && a1->klass.data(gs)->derivesFrom(gs, a2->klass); + bool rtl = a1->klass == a2->klass || a1->klass.data(gs)->derivesFrom(gs, a2->klass); + bool ltr = !rtl && a2->klass.data(gs)->derivesFrom(gs, a1->klass); if (!rtl && !ltr) { return OrType::make_shared(t1, t2); } diff --git a/core/types/types.cc b/core/types/types.cc index 27be7baf83..e494c8d378 100644 --- a/core/types/types.cc +++ b/core/types/types.cc @@ -8,6 +8,8 @@ #include "core/Names.h" #include "core/Symbols.h" #include "core/TypeConstraint.h" +#include "core/errors/infer.h" +#include "core/errors/resolver.h" #include #include "core/Types.h" @@ -21,18 +23,6 @@ namespace sorbet::core { using namespace std; -TypePtr Types::dispatchCallWithoutBlock(const GlobalState &gs, const TypePtr &recv, const DispatchArgs &args) { - auto dispatched = recv.dispatchCall(gs, args); - auto link = &dispatched; - while (link != nullptr) { - for (auto &err : link->main.errors) { - gs._error(move(err)); - } - link = link->secondary.get(); - } - return move(dispatched.returnType); -} - TypePtr Types::top() { return make_type(Symbols::top()); } @@ -50,7 +40,11 @@ TypePtr Types::untypedUntracked() { } TypePtr Types::untyped(const sorbet::core::GlobalState &gs, sorbet::core::SymbolRef blame) { - if (sorbet::debug_mode && blame.exists()) { + if constexpr (!sorbet::track_untyped_blame_mode && !sorbet::debug_mode) { + return untypedUntracked(); + } + + if (blame.exists()) { return make_type(blame); } else { return untypedUntracked(); @@ -109,10 +103,6 @@ TypePtr Types::nilableProcClass() { return res; } -TypePtr Types::classClass() { - return make_type(Symbols::Class()); -} - TypePtr Types::declBuilderForProcsSingletonClass() { return make_type(Symbols::DeclBuilderForProcsSingleton()); } @@ -308,6 +298,17 @@ TypePtr Types::hashOf(const GlobalState &gs, const TypePtr &elem) { return make_type(Symbols::Hash(), move(targs)); } +TypePtr Types::setOf(const TypePtr &elem) { + vector targs{elem}; + return make_type(Symbols::Set(), move(targs)); +} + +TypePtr Types::tClass(const TypePtr &attachedClass) { + vector targs; + targs.emplace_back(attachedClass); + return make_type(Symbols::Class(), move(targs)); +} + TypePtr Types::dropNil(const GlobalState &gs, const TypePtr &from) { return Types::dropSubtypesOf(gs, from, Symbols::NilClass()); } @@ -886,6 +887,209 @@ core::ClassOrModuleRef Types::getRepresentedClass(const GlobalState &gs, const T return singleton.data(gs)->attachedClass(gs); } +TypePtr Types::unwrapType(const GlobalState &gs, Loc loc, const TypePtr &tp) { + if (auto *metaType = cast_type(tp)) { + return metaType->wrapped; + } + + if (isa_type(tp)) { + auto classType = cast_type_nonnull(tp); + if (classType.symbol.data(gs)->derivesFrom(gs, core::Symbols::T_Enum())) { + // T::Enum instances are allowed to stand for themselves in type syntax positions. + // See the note in type_syntax.cc regarding T::Enum. + return tp; + } + + auto attachedClass = classType.symbol.data(gs)->attachedClass(gs); + if (!attachedClass.exists()) { + if (auto e = gs.beginError(loc, errors::Infer::BareTypeUsage)) { + e.setHeader("Unexpected bare `{}` value found in type position", tp.show(gs)); + if (classType.symbol == core::Symbols::T_Types_Base() || + classType.symbol.data(gs)->derivesFrom(gs, core::Symbols::T_Types_Base())) { + // T::Types::Base is the parent class for runtime type objects. + // Give a more helpful error message + e.addErrorNote("Sorbet only allows statically-analyzable types in type positions.\n" + " To compute new runtime types, you must explicitly wrap with `{}`", + "T.unsafe"); + auto locSource = loc.source(gs); + if (locSource.has_value()) { + e.replaceWith("Wrap in `T.unsafe`", loc, fmt::format("T.unsafe({})", locSource.value())); + } + } + } + + return Types::untypedUntracked(); + } + + return attachedClass.data(gs)->externalType(); + } + + if (auto *appType = cast_type(tp)) { + ClassOrModuleRef attachedClass = appType->klass.data(gs)->attachedClass(gs); + if (!attachedClass.exists()) { + if (auto e = gs.beginError(loc, errors::Infer::BareTypeUsage)) { + e.setHeader("Unexpected bare `{}` value found in type position", tp.show(gs)); + } + return Types::untypedUntracked(); + } + + return attachedClass.data(gs)->externalType(); + } + + if (auto *shapeType = cast_type(tp)) { + vector unwrappedValues; + unwrappedValues.reserve(shapeType->values.size()); + for (auto &value : shapeType->values) { + unwrappedValues.emplace_back(unwrapType(gs, loc, value)); + } + return make_type(shapeType->keys, move(unwrappedValues)); + } else if (auto *tupleType = cast_type(tp)) { + vector unwrappedElems; + unwrappedElems.reserve(tupleType->elems.size()); + for (auto &elem : tupleType->elems) { + unwrappedElems.emplace_back(unwrapType(gs, loc, elem)); + } + return make_type(move(unwrappedElems)); + } else if (isa_type(tp) || isa_type(tp) || isa_type(tp)) { + if (auto e = gs.beginError(loc, errors::Infer::BareTypeUsage)) { + e.setHeader("Unexpected bare `{}` value found in type position", tp.show(gs)); + } + return Types::untypedUntracked(); + } + return tp; +} + +// This method is actually special: not only is it called from dispatchCall in calls.cc, it's +// also called directly by type_syntax parsing in resolver (because this method checks some +// invariants of generics that we want to hold even in `typed: false` files). +// +// Unfortunately, this means that some errors are double reported (once by resolver, and then +// again by infer). + +TypePtr Types::applyTypeArguments(const GlobalState &gs, const CallLocs &locs, uint16_t numPosArgs, + const InlinedVector &args, ClassOrModuleRef genericClass) { + genericClass = genericClass.maybeUnwrapBuiltinGenericForwarder(); + + int arity; + if (genericClass == Symbols::Hash()) { + arity = 2; + } else { + arity = genericClass.data(gs)->typeArity(gs); + } + + // This is something like Generic[T1,...,foo: bar...] + auto numKwArgs = args.size() - numPosArgs; + if (numKwArgs > 0) { + auto begin = locs.args[numPosArgs].beginPos(); + auto end = locs.args.back().endPos(); + core::Loc kwargsLoc{locs.file, begin, end}; + + if (auto e = gs.beginError(kwargsLoc, errors::Infer::GenericArgumentKeywordArgs)) { + e.setHeader("Keyword arguments given to `{}`", genericClass.show(gs)); + // offer an autocorrect to turn the keyword args into a hash if there is no double-splat + if (numKwArgs % 2 == 0 && kwargsLoc.exists()) { + e.replaceWith("Wrap with braces", kwargsLoc, "{{{}}}", kwargsLoc.source(gs).value()); + } + } + } + + // This is a hack. In single package RBI generation mode we exclude all source files that are + // not in the package we're generating RBIs for, and just recover from the fact that certain + // things are missing. So it might look like the `A` in `A[...]` does not resolve, and single + // package RBI generation mode simply says "ok I'll make a fake stub constant." It then uses how + // UnresolvedAppliedType works (which was invented for a slightly related reason: correct fast path + // hashing) to take care of the applied type. + // + // Because of all of this, we don't actually want to report this error in single package RBI + // generation mode. + bool singlePackageRbiGeneration = gs.singlePackageImports.has_value(); + + if (!singlePackageRbiGeneration && (numPosArgs != arity || arity == 0)) { + auto squareBracketsLoc = core::Loc(locs.file, locs.fun.endPos(), locs.call.endPos()); + auto errLoc = + !locs.args.empty() ? core::Loc(locs.file, locs.args.front().join(locs.args.back())) : squareBracketsLoc; + if (auto e = gs.beginError(errLoc, errors::Infer::GenericArgumentCountMismatch)) { + if (arity == 0) { + if (genericClass.data(gs)->typeMembers().empty()) { + e.setHeader("`{}` is not a generic class, but was given type parameters", genericClass.show(gs)); + } else { + e.setHeader("All type parameters for `{}` have already been fixed", genericClass.show(gs)); + } + e.replaceWith("Remove square brackets", squareBracketsLoc, ""); + } else { + e.setHeader("Wrong number of type parameters for `{}`. Expected: `{}`, got: `{}`", + genericClass.show(gs), arity, numPosArgs); + } + } + } + + if (genericClass.data(gs)->typeMembers().empty()) { + return Types::untypedUntracked(); + } + + vector targs; + auto it = args.begin(); + int i = -1; + targs.reserve(genericClass.data(gs)->typeMembers().size()); + for (auto mem : genericClass.data(gs)->typeMembers()) { + ++i; + + auto memData = mem.data(gs); + + auto *memType = cast_type(memData->resultType); + ENFORCE(memType != nullptr); + + if (memData->flags.isFixed) { + // Fixed args are implicitly applied, and won't consume type + // arguments from the list that's supplied. + targs.emplace_back(memType->upperBound); + } else if (it != args.end()) { + auto loc = core::Loc(locs.file, locs.args[it - args.begin()]); + auto argType = unwrapType(gs, loc, (*it)->type); + bool validBounds = true; + + // Validate type parameter bounds. + if (!Types::isSubType(gs, argType, memType->upperBound)) { + validBounds = false; + if (auto e = gs.beginError(loc, errors::Resolver::GenericTypeParamBoundMismatch)) { + auto argStr = argType.show(gs); + e.setHeader("`{}` is not a subtype of upper bound of type member `{}`", argStr, + mem.showFullName(gs)); + e.addErrorLine(memData->loc(), "`{}` is `{}` bounded by `{}` here", mem.showFullName(gs), "upper", + memType->upperBound.show(gs)); + } + } + + if (!Types::isSubType(gs, memType->lowerBound, argType)) { + validBounds = false; + + if (auto e = gs.beginError(loc, errors::Resolver::GenericTypeParamBoundMismatch)) { + auto argStr = argType.show(gs); + e.setHeader("`{}` is not a supertype of lower bound of type member `{}`", argStr, + mem.showFullName(gs)); + e.addErrorLine(memData->loc(), "`{}` is `{}` bounded by `{}` here", mem.showFullName(gs), "lower", + memType->lowerBound.show(gs)); + } + } + + if (validBounds) { + targs.emplace_back(argType); + } else { + targs.emplace_back(Types::untypedUntracked()); + } + + ++it; + } else if (genericClass == Symbols::Hash() && i == 2) { + auto tupleArgs = targs; + targs.emplace_back(make_type(tupleArgs)); + } else { + targs.emplace_back(Types::untypedUntracked()); + } + } + + return make_type(make_type(genericClass, move(targs))); +} + Loc DispatchArgs::blockLoc(const GlobalState &gs) const { ENFORCE(this->block != nullptr); auto blockLoc = core::Loc(locs.file, argsLoc().endPos(), callLoc().endPos()); diff --git a/definition_validator/validator.cc b/definition_validator/validator.cc index e7430ceb5d..ab217850ec 100644 --- a/definition_validator/validator.cc +++ b/definition_validator/validator.cc @@ -2,7 +2,7 @@ #include "absl/strings/match.h" #include "ast/ast.h" #include "ast/treemap/treemap.h" -#include "common/Timer.h" +#include "common/timers/Timer.h" #include "core/core.h" #include "core/errors/resolver.h" @@ -265,13 +265,17 @@ void validateCompatibleOverride(const core::Context ctx, core::MethodRef superMe for (auto req : left.kw.required) { auto corresponding = absl::c_find_if(right.kw.required, [&](const auto &r) { return r.get().name == req.get().name; }); - if (corresponding == right.kw.required.end()) { + + auto hasCorrespondingRequired = corresponding != right.kw.required.end(); + if (!hasCorrespondingRequired) { corresponding = absl::c_find_if(right.kw.optional, [&](const auto &r) { return r.get().name == req.get().name; }); } + auto hasCorrespondingOptional = corresponding != right.kw.optional.end(); + // if there is a corresponding parameter, make sure it has the right type - if (corresponding != right.kw.required.end() && corresponding != right.kw.optional.end()) { + if (hasCorrespondingRequired || hasCorrespondingOptional) { if (!checkSubtype(ctx, *constr, corresponding->get().type, method, req.get().type, superMethod, core::Polarity::Negative)) { if (auto e = @@ -316,6 +320,25 @@ void validateCompatibleOverride(const core::Context ctx, core::MethodRef superMe "A parameter's type must be a supertype of the same parameter's type on the super method."); } } + } else if (absl::c_any_of(right.kw.required, + [&](const auto &r) { return r.get().name == opt.get().name; })) { + if (auto e = ctx.state.beginError(method.data(ctx)->loc(), core::errors::Resolver::BadMethodOverride)) { + e.setHeader("{} method `{}` must redeclare keyword parameter `{}` as optional", + implementationOf(ctx, superMethod), superMethod.show(ctx), opt.get().name.show(ctx)); + // Show the superMethod loc (declLoc) so the error message includes the default value + e.addErrorLine(superMethod.data(ctx)->loc(), + "The optional super method parameter `{}` was declared here", + opt.get().name.show(ctx)); + } + } else { + if (auto e = ctx.state.beginError(method.data(ctx)->loc(), core::errors::Resolver::BadMethodOverride)) { + e.setHeader("{} method `{}` must accept optional keyword parameter `{}`", + implementationOf(ctx, superMethod), superMethod.show(ctx), opt.get().name.show(ctx)); + // Show the superMethod loc (declLoc) so the error message includes the default value + e.addErrorLine(superMethod.data(ctx)->loc(), + "The optional super method parameter `{}` was declared here", + opt.get().name.show(ctx)); + } } } } @@ -346,6 +369,10 @@ void validateCompatibleOverride(const core::Context ctx, core::MethodRef superMe if (absl::c_any_of(left.kw.required, [&](const auto &l) { return l.get().name == extra.get().name; })) { continue; } + if (absl::c_any_of(left.kw.optional, [&](const auto &l) { return l.get().name == extra.get().name; })) { + // We would have already reported a more informative error above. + continue; + } if (auto e = ctx.state.beginError(method.data(ctx)->loc(), core::errors::Resolver::BadMethodOverride)) { e.setHeader("{} method `{}` contains extra required keyword argument `{}`", implementationOf(ctx, superMethod), superMethod.show(ctx), extra.get().name.toString(ctx)); @@ -401,14 +428,9 @@ void validateOverriding(const core::Context ctx, core::MethodRef method) { auto klassData = klass.data(ctx); InlinedVector overridenMethods; - // both of these match the behavior of the runtime checks, which will only allow public methods to be defined in - // interfaces - if (klassData->flags.isInterface && method.data(ctx)->flags.isPrivate) { - if (auto e = ctx.state.beginError(method.data(ctx)->loc(), core::errors::Resolver::NonPublicAbstract)) { - e.setHeader("Interface method `{}` cannot be private", method.show(ctx)); - } - } - + // Matches the behavior of the runtime checks + // NOTE(jez): I don't think this check makes all that much sense, but I haven't thought about it. + // We already deleted the corresponding check for `private`, and may want to revisit this, too. if (klassData->flags.isInterface && method.data(ctx)->flags.isProtected) { if (auto e = ctx.state.beginError(method.data(ctx)->loc(), core::errors::Resolver::NonPublicAbstract)) { e.setHeader("Interface method `{}` cannot be protected", method.show(ctx)); @@ -467,7 +489,7 @@ void validateOverriding(const core::Context ctx, core::MethodRef method) { e.addErrorLine(overridenMethod.data(ctx)->loc(), "defined here"); } } - if (!method.data(ctx)->flags.isOverride && method.data(ctx)->hasSig() && + if (!method.data(ctx)->flags.isOverride && !method.data(ctx)->flags.isAbstract && method.data(ctx)->hasSig() && overridenMethod.data(ctx)->flags.isAbstract && overridenMethod.data(ctx)->hasSig() && !method.data(ctx)->flags.isRewriterSynthesized && !isRBI) { if (auto e = ctx.state.beginError(method.data(ctx)->loc(), core::errors::Resolver::UndeclaredOverride)) { @@ -957,8 +979,44 @@ class ValidateWalk { variance::validateMethodVariance(ctx, methodDef.symbol); } + // See the comment in `VarianceValidator::validateMethod` for an explanation of why we don't + // need to check types on instance variables. + validateOverriding(ctx, methodDef.symbol); } + + void postTransformSend(core::Context ctx, ast::ExpressionPtr &tree) { + auto &send = ast::cast_tree_nonnull(tree); + if (send.fun != core::Names::new_()) { + return; + } + + auto *id = ast::cast_tree(send.recv); + if (id == nullptr || !id->symbol.exists() || !id->symbol.isClassOrModule()) { + return; + } + + auto symbol = id->symbol.asClassOrModuleRef().data(ctx); + if (!symbol->flags.isAbstract) { + return; + } + + auto singletonClass = symbol->lookupSingletonClass(ctx.state); + if (!singletonClass.exists()) { + return; + } + + auto method_new = singletonClass.data(ctx)->findMethodTransitive(ctx.state, core::Names::new_()); + // If the .new method we find is owned by Class, that means + // there was no user defined .new method, which warrants an error. + if (method_new.data(ctx)->owner == core::Symbols::Class()) { + if (auto e = ctx.beginError(send.loc, core::errors::Resolver::AbstractClassInstantiated)) { + auto symbolName = id->symbol.show(ctx); + e.setHeader("Attempt to instantiate abstract class `{}`", symbolName); + e.addErrorLine(id->symbol.loc(ctx), "`{}` defined here", symbolName); + } + } + } }; } // namespace @@ -966,7 +1024,7 @@ ast::ParsedFile runOne(core::Context ctx, ast::ParsedFile tree) { Timer timeit(ctx.state.tracer(), "validateSymbols"); ValidateWalk validate; - ast::ShallowWalk::apply(ctx, validate, tree.tree); + ast::TreeWalk::apply(ctx, validate, tree.tree); return tree; } diff --git a/definition_validator/variance.cc b/definition_validator/variance.cc index ee28c6efb6..b9c46ec935 100644 --- a/definition_validator/variance.cc +++ b/definition_validator/variance.cc @@ -94,6 +94,7 @@ class VarianceValidator { if (auto e = ctx.state.beginError(this->loc, core::errors::Resolver::AttachedClassAsParam)) { e.setHeader("`{}` may only be used in an `{}` context, like `{}`", "T.attached_class", ":out", "returns"); + e.addErrorNote("Methods marked `{}` are not subject to this constraint", "private"); } } else { if (auto e = ctx.state.beginError(this->loc, core::errors::Resolver::InvalidVariance)) { @@ -110,6 +111,7 @@ class VarianceValidator { e.addErrorLine(paramData->loc(), "`{}` `{}` defined here as `{}`", flavor, paramName, core::Polarities::showVariance(paramVariance)); + e.addErrorNote("Methods marked `{}` are not subject to this constraint", "private"); } } } @@ -140,13 +142,27 @@ class VarianceValidator { return validator.validate(ctx, polarity, type); } - // Variance checking, parameterized on the external polarity of the method - // context. + // Variance checking, parameterized on the external polarity of the method context. static void validateMethod(const core::Context ctx, const core::Polarity polarity, const core::MethodRef method) { auto methodData = method.data(ctx); + if (methodData->flags.isPrivate) { + // `private` methods in Ruby behave like `protected[this]` methods in Scala ("object-protected" methods). + // Variance annotations are ignored for all `protected[this]` methods in Scala, so it's fine in Sorbet too: + // + // > References to the type parameters in object-private or object-protected values, + // > types, variables, or methods of the class are not checked for their variance + // > position. In these members the type parameter may appear anywhere without restricting + // > its legal variance annotations. + // + // https://scala-lang.org/files/archive/spec/2.13/04-basic-declarations-and-definitions.html#variance-annotations + // + // Similarly, instance variables behave the same as private methods in Ruby. + // (This would stop being the case if Ruby ever invented syntax like `x.@foo` to access an instance + // variable on something other than the implicit `self` that the `@foo` syntax currently implies) + return; + } - // Negate the polarity for checking arguments in a ContraVariant - // context. + // Negate the polarity for checking arguments in a ContraVariant context. const core::Polarity negated = core::Polarities::negatePolarity(polarity); for (auto &arg : methodData->arguments) { diff --git a/docs/lmdb.md b/docs/lmdb.md index b054651bf7..eddf8872e8 100644 --- a/docs/lmdb.md +++ b/docs/lmdb.md @@ -62,6 +62,9 @@ Be sure to cross reference with the [command line tools] docs for further usage info. ```bash +# Install the command line tools +pip3 install lmdb + # Reads unnamed database from /tmp/sorbet-cache/data.mdb # Writes into ./main.cdbmake # Will list all the "flavors" in the Sorbet cache @@ -93,6 +96,21 @@ python -mlmdb --env example-cache shell # ... # {'psize': 4096, 'depth': 2, 'branch_pages': 1, 'leaf_pages': 20, 'overflow_pages': 699, 'entries': 90} +# Print a list of keylen(val) pairs to out.txt: +cd /tmp/sorbet-cache +python -mlmdb --env . shell +# Python 3.9.12 (main, Mar 23 2022, 21:36:19) +# [GCC 5.4.0 20160609] on linux +# Type "help", "copyright", "credits" or "license" for more information. +# (InteractiveConsole) +# >>> with open('/tmp/out.txt', 'wb') as f: +# ... with ENV.begin(db=ENV.open_db(b'experimentalfastpath')) as txn: +# ... cursor = txn.cursor() +# ... for key, val in cursor: +# ... f.write(key) +# ... f.write(b'\t') +# ... f.write(str(len(val)).encode()) +# ... f.write(b'\n') ``` diff --git a/docs/tracing.md b/docs/tracing.md index 03615fef28..61bccbdae2 100644 --- a/docs/tracing.md +++ b/docs/tracing.md @@ -37,6 +37,22 @@ To collect a trace, run Sorbet with the `--web-trace-file=` flag: Sorbet will typecheck the file or codebase like normal, and then write out `trace.json` (or whatever `` name you chose). +### Tracing and LSP + +The traces Sorbet submits are the same as the [metrics] that Sorbet submits to +StatsD (if enabled with the `--statsd-host` option). + +[metrics]: https://sorbet.org/docs/metrics + +For better performance in LSP mode, Sorbet will only report stats to the +specified host after it finishes processing a task **and** it's been 5 minutes +since the last stats dump. + +Passing `--web-trace-file` overrides this behavior, forcibly flushing the trace +file and the StatsD stats after **every** task (no matter how long ago the last +flush was). This is often desired when debugging but can potentially cause +increased traffic on StatsD and/or slower IDE performance in normal operation. + ## Loading a trace into the viewer Once you've [Collected a trace](#collecting-a-trace), you can load it into the diff --git a/gems/sorbet-runtime/.rubocop.yml b/gems/sorbet-runtime/.rubocop.yml index 1620ebdb17..85b1848b0d 100644 --- a/gems/sorbet-runtime/.rubocop.yml +++ b/gems/sorbet-runtime/.rubocop.yml @@ -4,6 +4,7 @@ require: rubocop-performance AllCops: NewCops: disable + # Stripe in-house styles that are prevalent in the codebase already. # # Generally, prefer not to add things here; unless there's a specific reason @@ -30,6 +31,8 @@ Style/EmptyElse: EnforcedStyle: empty Layout/FirstArrayElementIndentation: EnforcedStyle: consistent +Style/SingleLineMethods: + Enabled: false # This doesn't play very well with implementations of Sorbet abstract methods Lint/UnusedMethodArgument: @@ -65,6 +68,11 @@ Naming/FileName: Exclude: # Filename deliberately matches gem name - 'lib/sorbet-runtime.rb' +# You can convey semantic meaning by using is_foo? +Naming/PredicateName: + Enabled: false +Naming/MethodParameterName: + Enabled: false Lint/MissingSuper: Exclude: @@ -85,6 +93,9 @@ Style/DoubleNegation: Style/SlicingWithRange: Enabled: false +Style/CaseEquality: + Enabled: false + Layout/EmptyLines: Exclude: # We deliberately add extra lines here to aid Pry debugging diff --git a/gems/sorbet-runtime/.rubocop_todo.yml b/gems/sorbet-runtime/.rubocop_todo.yml index 7d61db8683..d2c8b1d537 100644 --- a/gems/sorbet-runtime/.rubocop_todo.yml +++ b/gems/sorbet-runtime/.rubocop_todo.yml @@ -220,6 +220,7 @@ Naming/MemoizedInstanceVariableName: Naming/MethodParameterName: Exclude: - 'bench/typecheck.rb' + - 'bench/typecheck_kwargs_splat.rb' - 'lib/types/types/base.rb' - 'test/types/abstract_validation.rb' - 'test/types/builder_syntax.rb' diff --git a/gems/sorbet-runtime/Rakefile b/gems/sorbet-runtime/Rakefile index 352196f27e..debc45199d 100644 --- a/gems/sorbet-runtime/Rakefile +++ b/gems/sorbet-runtime/Rakefile @@ -9,7 +9,11 @@ T::Configuration.default_checked_level = :always task default: %i[test rubocop] def require_tests - Dir.glob('./test/**/*.rb').reject {|path| path.match(%r{^./test/types/fixtures/})}.each(&method(:require)) + Dir + .glob('./test/**/*.rb') + .reject {|path| path.match(%r{^./test/types/fixtures/})} + .reject {|path| path.match(%r{^./test/wholesome/})} + .each(&method(:require)) end task :test do diff --git a/gems/sorbet-runtime/bench/constructor.rb b/gems/sorbet-runtime/bench/constructor.rb index 3903f09b84..609610b545 100644 --- a/gems/sorbet-runtime/bench/constructor.rb +++ b/gems/sorbet-runtime/bench/constructor.rb @@ -8,6 +8,21 @@ module SorbetBenchmarks module Constructor + class ExamplePoro + def initialize(hash) + @prop1 = hash.fetch(:prop1, nil) + @prop2 = hash.fetch(:prop2, 0) + @prop3 = hash.fetch(:prop3) + @prop4 = hash.fetch(:prop4) + @prop5 = hash.fetch(:prop5, []) + @prop6 = hash.fetch(:prop6) + @prop7 = hash.fetch(:prop7, {}) + @prop8 = hash.fetch(:prop8, nil) + @prop9 = hash.fetch(:prop9, []) + @prop10 = hash.fetch(:prop10, {}) + end + end + class Example < T::Struct class Subdoc < T::Struct prop :prop, String @@ -32,6 +47,19 @@ def self.run prop6: {}, }.freeze + 100_000.times do + ExamplePoro.new(input) + end + + result = Benchmark.measure do + 1_000_000.times do + ExamplePoro.new(input) + end + end + + puts "Plain Ruby (μs/iter):" + puts result + 100_000.times do Example.new(input) end diff --git a/gems/sorbet-runtime/bench/prop_validation.rb b/gems/sorbet-runtime/bench/prop_validation.rb new file mode 100644 index 0000000000..05b0e5bce3 --- /dev/null +++ b/gems/sorbet-runtime/bench/prop_validation.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true +# typed: true + +require 'benchmark' + +require_relative '../lib/sorbet-runtime' + +module SorbetBenchmarks + module PropValidation + class Subdoc < T::Struct + include T::Props::TypeValidation + prop :prop, String + end + + def self.run + GC.disable + before = GC.stat(:total_allocated_objects) + result = Benchmark.measure do + 5_000.times do + cls = Class.new(T::Struct) do + include T::Props::TypeValidation + prop :prop1, T.nilable(Integer) + prop :prop2, Integer, default: 0 + prop :prop3, Integer + prop :prop4, T::Array[Integer] + prop :prop5, T::Array[Integer], default: [] + prop :prop6, T::Hash[String, Integer] + prop :prop7, T::Hash[String, Integer], default: {} + prop :prop8, T.nilable(Subdoc) + prop :prop9, T::Array[Subdoc], default: [] + prop :prop10, T::Hash[String, Subdoc], default: {} + end + cls.decorator.eagerly_define_lazy_methods! + end + end + after = GC.stat(:total_allocated_objects) + + puts "Subclassing T::Struct, with ten props (ms/iter):" + puts result + puts "Allocations: #{after - before}" + end + end +end diff --git a/gems/sorbet-runtime/bench/serialize_custom_type.rb b/gems/sorbet-runtime/bench/serialize_custom_type.rb new file mode 100644 index 0000000000..89b15a5286 --- /dev/null +++ b/gems/sorbet-runtime/bench/serialize_custom_type.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true +# typed: true + +require 'benchmark' + +require_relative '../lib/sorbet-runtime' + +module SorbetBenchmarks + module SerializeCustomType + class MyCustomType + extend T::Props::CustomType + + attr_accessor :value + + def initialize(value) + @value = value + end + + # Not used in this benchmark. + def self.deserialize(value) + result = new + result.value = value.clone.freeze + result + end + + def self.serialize(instance) + instance.value + end + end + + def self.run + input = MyCustomType.new(123) + + 100_000.times do + T::Props::CustomType.checked_serialize(input) + end + + result = Benchmark.measure do + 1_000_000.times do + T::Props::CustomType.checked_serialize(input) + end + end + + puts "T::Props::CustomType.checked_serialize (μs/iter):" + puts result + end + end +end diff --git a/gems/sorbet-runtime/bench/sigs.rb b/gems/sorbet-runtime/bench/sigs.rb new file mode 100644 index 0000000000..a96c8d31a4 --- /dev/null +++ b/gems/sorbet-runtime/bench/sigs.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true +# typed: true + +require 'benchmark' + +require_relative '../lib/sorbet-runtime' + +module SorbetBenchmarks + module Sigs + extend T::Sig + + def self.run + GC.start + GC.disable + + t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond) + begin_allocs = GC.stat(:total_allocated_objects) + T::Utils.run_all_sig_blocks + duration_s = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond) - t0 + end_allocs = GC.stat(:total_allocated_objects) - 1 + + str = duration_s >= 1000 ? "#{(duration_s / 1000).round(3)} μs" : "#{duration_s.round(3)} ns" + puts "run_all_sigs: #{str} #{end_allocs - begin_allocs}" + end + end +end diff --git a/gems/sorbet-runtime/bench/tasks.rb b/gems/sorbet-runtime/bench/tasks.rb index ff55136fa4..0d26fc44ba 100644 --- a/gems/sorbet-runtime/bench/tasks.rb +++ b/gems/sorbet-runtime/bench/tasks.rb @@ -6,7 +6,12 @@ require_relative 'constructor' require_relative 'deserialize' require_relative 'prop_definition' +require_relative 'prop_validation' +require_relative 'serialize_custom_type' +require_relative 'sigs' +require_relative 'tutils' require_relative 'typecheck' +require_relative 'typecheck_kwargs_splat' namespace :bench do task :getters do @@ -29,9 +34,29 @@ SorbetBenchmarks::PropDefinition.run end + task :prop_validation do + SorbetBenchmarks::PropValidation.run + end + + task :serialize_custom_type do + SorbetBenchmarks::SerializeCustomType.run + end + + task :sigs do + SorbetBenchmarks::Sigs.run + end + task :typecheck do SorbetBenchmarks::Typecheck.run end - task all: %i[getters setters constructor deserialize prop_definition typecheck] + task :typecheck_kwargs_splat do + SorbetBenchmarks::TypecheckKwargsSplat.run + end + + task :tutils do + SorbetBenchmarks::TUtils.run + end + + task all: %i[getters setters constructor deserialize prop_definition serialize_custom_type sigs tutils typecheck] end diff --git a/gems/sorbet-runtime/bench/tutils.rb b/gems/sorbet-runtime/bench/tutils.rb new file mode 100644 index 0000000000..fc77168c91 --- /dev/null +++ b/gems/sorbet-runtime/bench/tutils.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true +# typed: true + +require 'benchmark' + +require_relative '../lib/sorbet-runtime' + +module SorbetBenchmarks + module TUtils + extend T::Sig + + def self.time_block(name, iterations_of_block: 1_000_000, &blk) + 1_000.times(&blk) # warmup + + GC.start + GC.disable + + t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC) + iterations_of_block.times(&blk) + duration_s = Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0 + + GC.enable + + ns_per_iter = duration_s * 1_000_000_000 / iterations_of_block + duration_str = ns_per_iter >= 1000 ? "#{(ns_per_iter / 1000).round(3)} μs" : "#{ns_per_iter.round(3)} ns" + puts "#{name}: #{duration_str}" + end + + def self.run + type = T::Utils.coerce(Integer) + time_block("T.unwrap_nilable(#{type})") do + T::Utils.unwrap_nilable(type) + end + + time_block("get_underlying_type(#{type})") do + T::Utils::Nilable.get_underlying_type(type) + end + + type = T::Utils.coerce(T.nilable(Integer)) + time_block("T.unwrap_nilable(#{type})") do + T::Utils.unwrap_nilable(type) + end + + time_block("get_underlying_type(#{type})") do + T::Utils::Nilable.get_underlying_type(type) + end + + type = T::Utils.coerce(T.any(Integer, Float)) + time_block("T.unwrap_nilable(#{type})") do + T::Utils.unwrap_nilable(type) + end + + time_block("get_underlying_type(#{type})") do + T::Utils::Nilable.get_underlying_type(type) + end + end + end +end diff --git a/gems/sorbet-runtime/bench/typecheck_kwargs_splat.rb b/gems/sorbet-runtime/bench/typecheck_kwargs_splat.rb new file mode 100644 index 0000000000..c23ed99eb3 --- /dev/null +++ b/gems/sorbet-runtime/bench/typecheck_kwargs_splat.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true +# typed: true + +require 'benchmark' + +require_relative '../lib/sorbet-runtime' + +module SorbetBenchmarks + module TypecheckKwargsSplat + extend T::Sig + + class Example; end + + def self.time_block(name, iterations_of_block: 1_000_000, iterations_in_block: 2, &blk) + 10_000.times(&blk) # warmup + + GC.start + GC.disable + before_alloc = GC.stat(:total_allocated_objects) + + t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC) + iterations_of_block.times(&blk) + duration_s = Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0 + + after_alloc = GC.stat(:total_allocated_objects) + GC.enable + + ns_per_iter = duration_s * 1_000_000_000 / (iterations_of_block * iterations_in_block) + duration_str = ns_per_iter >= 1000 ? "#{(ns_per_iter / 1000).round(3)} μs" : "#{ns_per_iter.round(3)} ns" + puts "#{name}: #{duration_str}" + puts "Allocations for #{name}: #{after_alloc - before_alloc}" + end + + def self.run + time_block("sig {params(s: Symbol, rest: String, x: Integer, y: Integer).void} (with kwargs plus splat)") do + arg_plus_kwargs(:foo, "foo", "bar", "baz", x: 1, y: 2) + arg_plus_kwargs(:bar, "foo", "bar", "baz", x: 1) + end + end + + sig {params(s: Symbol, rest: String, x: Integer, y: Integer).void} + def self.arg_plus_kwargs(s, *rest, x:, y: 0); end + end +end diff --git a/gems/sorbet-runtime/lib/sorbet-runtime.rb b/gems/sorbet-runtime/lib/sorbet-runtime.rb index 8d8199f1d9..e997da431f 100644 --- a/gems/sorbet-runtime/lib/sorbet-runtime.rb +++ b/gems/sorbet-runtime/lib/sorbet-runtime.rb @@ -37,14 +37,15 @@ module T::Private::Types; end require_relative 'types/types/fixed_hash' require_relative 'types/types/intersection' require_relative 'types/types/noreturn' +require_relative 'types/types/anything' require_relative 'types/types/proc' require_relative 'types/types/attached_class' require_relative 'types/types/self_type' require_relative 'types/types/simple' require_relative 'types/types/t_enum' require_relative 'types/types/type_parameter' -require_relative 'types/types/typed_array' require_relative 'types/types/typed_enumerator' +require_relative 'types/types/typed_enumerator_chain' require_relative 'types/types/typed_enumerator_lazy' require_relative 'types/types/typed_hash' require_relative 'types/types/typed_range' @@ -82,6 +83,10 @@ module T::Private::Types; end require_relative 'types/utils' require_relative 'types/boolean' +# Depends on types/utils +require_relative 'types/types/typed_array' +require_relative 'types/types/typed_class' + # Props dependencies require_relative 'types/private/abstract/data' require_relative 'types/private/mixins/mixins' diff --git a/gems/sorbet-runtime/lib/types/_types.rb b/gems/sorbet-runtime/lib/types/_types.rb index 581c12dc1a..55401ca76d 100644 --- a/gems/sorbet-runtime/lib/types/_types.rb +++ b/gems/sorbet-runtime/lib/types/_types.rb @@ -47,6 +47,10 @@ def self.noreturn T::Types::NoReturn::Private::INSTANCE end + def self.anything + T::Types::Anything::Private::INSTANCE + end + # T.all(, , ...) -- matches an object that has all of the types listed def self.all(type_a, type_b, *types) T::Types::Intersection.new([type_a, type_b] + types) @@ -119,7 +123,7 @@ def self.type_alias(type=nil, &blk) # .returns(T::Array[T.type_parameter(:U)]) # def map(&blk); end def self.type_parameter(name) - T::Types::TypeParameter.new(name) + T::Types::TypeParameter.make(name) end # Tells the typechecker that `value` is of type `type`. Use this to get additional checking after @@ -279,9 +283,9 @@ def self.absurd(value) module Array def self.[](type) if type.is_a?(T::Types::Untyped) - T::Types::TypedArray::Untyped.new + T::Types::TypedArray::Untyped::Private::INSTANCE else - T::Types::TypedArray.new(type) + T::Types::TypedArray::Private::Pool.type_for_module(type) end end end @@ -324,6 +328,16 @@ def self.[](type) end end end + + module Chain + def self.[](type) + if type.is_a?(T::Types::Untyped) + T::Types::TypedEnumeratorChain::Untyped.new + else + T::Types::TypedEnumeratorChain.new(type) + end + end + end end module Range @@ -341,4 +355,16 @@ def self.[](type) end end end + + module Class + def self.[](type) + if type.is_a?(T::Types::Untyped) + T::Types::TypedClass::Untyped::Private::INSTANCE + elsif type.is_a?(T::Types::Anything) + T::Types::TypedClass::Anything::Private::INSTANCE + else + T::Types::TypedClass::Private::Pool.type_for_module(type) + end + end + end end diff --git a/gems/sorbet-runtime/lib/types/compatibility_patches.rb b/gems/sorbet-runtime/lib/types/compatibility_patches.rb index 070b182aae..028b292995 100644 --- a/gems/sorbet-runtime/lib/types/compatibility_patches.rb +++ b/gems/sorbet-runtime/lib/types/compatibility_patches.rb @@ -27,8 +27,10 @@ module CompatibilityPatches module RSpecCompatibility module RecorderExtensions def observe!(method_name) - method = @klass.instance_method(method_name.to_sym) - T::Private::Methods.maybe_run_sig_block_for_method(method) + if @klass.method_defined?(method_name.to_sym) + method = @klass.instance_method(method_name.to_sym) + T::Private::Methods.maybe_run_sig_block_for_method(method) + end super(method_name) end end diff --git a/gems/sorbet-runtime/lib/types/enum.rb b/gems/sorbet-runtime/lib/types/enum.rb index c2de09abaf..381152636a 100644 --- a/gems/sorbet-runtime/lib/types/enum.rb +++ b/gems/sorbet-runtime/lib/types/enum.rb @@ -362,6 +362,11 @@ def self.inherited(child_class) super raise "Inheriting from children of T::Enum is prohibited" if self != T::Enum + + # "oj" gem JSON support + if Object.const_defined?(:Oj) + Object.const_get(:Oj).register_odd(child_class, child_class, :try_deserialize, :serialize) + end end # Marshal support diff --git a/gems/sorbet-runtime/lib/types/generic.rb b/gems/sorbet-runtime/lib/types/generic.rb index 58def7aa1a..11c073122c 100644 --- a/gems/sorbet-runtime/lib/types/generic.rb +++ b/gems/sorbet-runtime/lib/types/generic.rb @@ -19,4 +19,6 @@ def type_member(variance=:invariant, &blk) def type_template(variance=:invariant, &blk) T::Types::TypeTemplate.new(variance) end + + def has_attached_class!(variance=:invariant, &blk); end end diff --git a/gems/sorbet-runtime/lib/types/private/abstract/declare.rb b/gems/sorbet-runtime/lib/types/private/abstract/declare.rb index ce8ddf9531..43f216254d 100644 --- a/gems/sorbet-runtime/lib/types/private/abstract/declare.rb +++ b/gems/sorbet-runtime/lib/types/private/abstract/declare.rb @@ -27,15 +27,15 @@ def self.declare_abstract(mod, type:) raise "Classes can't be interfaces. Use `abstract!` instead of `interface!`." end - if mod.instance_method(:initialize).owner == mod - raise "You must call `abstract!` *before* defining an initialize method" + if Object.instance_method(:method).bind_call(mod, :new).owner == mod + raise "You must call `abstract!` *before* defining a `new` method" end # Don't need to silence warnings via without_ruby_warnings when calling # define_method because of the guard above - mod.send(:define_method, :initialize) do |*args, &blk| - if self.class == mod + mod.send(:define_singleton_method, :new) do |*args, &blk| + if T.unsafe(self) == mod raise "#{mod} is declared as abstract; it cannot be instantiated" end super(*args, &blk) @@ -43,10 +43,10 @@ def self.declare_abstract(mod, type:) # Ruby doesn not emit "method redefined" warnings for aliased methods # (more robust than undef_method that would create a small window in which the method doesn't exist) - mod.send(:alias_method, :initialize, :initialize) + mod.singleton_class.send(:alias_method, :new, :new) - if mod.respond_to?(:ruby2_keywords, true) - mod.send(:ruby2_keywords, :initialize) + if mod.singleton_class.respond_to?(:ruby2_keywords, true) + mod.singleton_class.send(:ruby2_keywords, :new) end end end diff --git a/gems/sorbet-runtime/lib/types/private/methods/_methods.rb b/gems/sorbet-runtime/lib/types/private/methods/_methods.rb index 1720892a95..fcfaaa79a8 100644 --- a/gems/sorbet-runtime/lib/types/private/methods/_methods.rb +++ b/gems/sorbet-runtime/lib/types/private/methods/_methods.rb @@ -83,7 +83,7 @@ def self.finalize_proc(decl) raise "Procs cannot have override/abstract modifiers" end if decl.mod != PROC_TYPE - raise "You are passing a DeclBuilder as a type. Did you accidentally use `self` inside a `sig` block?" + raise "You are passing a DeclBuilder as a type. Did you accidentally use `self` inside a `sig` block? Perhaps you wanted the `T.self_type` instead: https://sorbet.org/docs/self-type" end if decl.returns == ARG_NOT_PROVIDED raise "Procs must specify a return type" @@ -117,6 +117,11 @@ def self.signature_for_method(method) @signatures_by_method[key] end + # Fetch the directory name of the file that defines the `T::Private` constant and + # add a trailing slash to allow us to match it as a directory prefix. + SORBET_RUNTIME_LIB_PATH = File.dirname(T.const_source_location(:Private).first) + File::SEPARATOR + private_constant :SORBET_RUNTIME_LIB_PATH + # when target includes a module with instance methods source_method_names, ensure there is zero intersection between # the final instance methods of target and source_method_names. so, for every m in source_method_names, check if there # is already a method defined on one of target_ancestors with the same name that is final. @@ -158,7 +163,7 @@ def self._check_final_ancestors(target, target_ancestors, source_method_names, s definition_file, definition_line = T::Private::Methods.signature_for_method(ancestor.instance_method(method_name)).method.source_location is_redefined = target == ancestor - caller_loc = caller_locations&.find {|l| !l.to_s.match?(%r{sorbet-runtime[^/]*/lib/})} + caller_loc = caller_locations&.find {|l| !l.to_s.start_with?(SORBET_RUNTIME_LIB_PATH)} extra_info = "\n" if caller_loc extra_info = (is_redefined ? "Redefined" : "Overridden") + " here: #{caller_loc.path}:#{caller_loc.lineno}\n" @@ -334,9 +339,11 @@ def self.run_sig(hook_mod, method_name, original_method, declaration_block) nil end + declaration_block.loc = nil + signature = if current_declaration - build_sig(hook_mod, method_name, original_method, current_declaration, declaration_block.loc) + build_sig(hook_mod, method_name, original_method, current_declaration) else Signature.new_untyped(method: original_method) end @@ -353,7 +360,7 @@ def self.run_builder(declaration_block) .decl end - def self.build_sig(hook_mod, method_name, original_method, current_declaration, loc) + def self.build_sig(hook_mod, method_name, original_method, current_declaration) begin # We allow `sig` in the current module's context (normal case) and if hook_mod != current_declaration.mod && diff --git a/gems/sorbet-runtime/lib/types/private/methods/call_validation_2_6.rb b/gems/sorbet-runtime/lib/types/private/methods/call_validation_2_6.rb index ff7fcb46b3..e5c39b4cec 100644 --- a/gems/sorbet-runtime/lib/types/private/methods/call_validation_2_6.rb +++ b/gems/sorbet-runtime/lib/types/private/methods/call_validation_2_6.rb @@ -11,26 +11,28 @@ def self.create_validator_method_fast(mod, original_method, method_sig, original raise 'Should have used create_validator_procedure_fast' end # trampoline to reduce stack frame size - if method_sig.arg_types.empty? + arg_types = method_sig.arg_types + case arg_types.length + when 0 create_validator_method_fast0(mod, original_method, method_sig, original_visibility, method_sig.return_type.raw_type) - elsif method_sig.arg_types.length == 1 + when 1 create_validator_method_fast1(mod, original_method, method_sig, original_visibility, method_sig.return_type.raw_type, - method_sig.arg_types[0][1].raw_type) - elsif method_sig.arg_types.length == 2 + arg_types[0][1].raw_type) + when 2 create_validator_method_fast2(mod, original_method, method_sig, original_visibility, method_sig.return_type.raw_type, - method_sig.arg_types[0][1].raw_type, - method_sig.arg_types[1][1].raw_type) - elsif method_sig.arg_types.length == 3 + arg_types[0][1].raw_type, + arg_types[1][1].raw_type) + when 3 create_validator_method_fast3(mod, original_method, method_sig, original_visibility, method_sig.return_type.raw_type, - method_sig.arg_types[0][1].raw_type, - method_sig.arg_types[1][1].raw_type, - method_sig.arg_types[2][1].raw_type) - elsif method_sig.arg_types.length == 4 + arg_types[0][1].raw_type, + arg_types[1][1].raw_type, + arg_types[2][1].raw_type) + when 4 create_validator_method_fast4(mod, original_method, method_sig, original_visibility, method_sig.return_type.raw_type, - method_sig.arg_types[0][1].raw_type, - method_sig.arg_types[1][1].raw_type, - method_sig.arg_types[2][1].raw_type, - method_sig.arg_types[3][1].raw_type) + arg_types[0][1].raw_type, + arg_types[1][1].raw_type, + arg_types[2][1].raw_type, + arg_types[3][1].raw_type) else raise 'should not happen' end @@ -343,26 +345,28 @@ def self.create_validator_method_fast4(mod, original_method, method_sig, origina def self.create_validator_procedure_fast(mod, original_method, method_sig, original_visibility) # trampoline to reduce stack frame size - if method_sig.arg_types.empty? + arg_types = method_sig.arg_types + case arg_types.length + when 0 create_validator_procedure_fast0(mod, original_method, method_sig, original_visibility) - elsif method_sig.arg_types.length == 1 + when 1 create_validator_procedure_fast1(mod, original_method, method_sig, original_visibility, - method_sig.arg_types[0][1].raw_type) - elsif method_sig.arg_types.length == 2 + arg_types[0][1].raw_type) + when 2 create_validator_procedure_fast2(mod, original_method, method_sig, original_visibility, - method_sig.arg_types[0][1].raw_type, - method_sig.arg_types[1][1].raw_type) - elsif method_sig.arg_types.length == 3 + arg_types[0][1].raw_type, + arg_types[1][1].raw_type) + when 3 create_validator_procedure_fast3(mod, original_method, method_sig, original_visibility, - method_sig.arg_types[0][1].raw_type, - method_sig.arg_types[1][1].raw_type, - method_sig.arg_types[2][1].raw_type) - elsif method_sig.arg_types.length == 4 + arg_types[0][1].raw_type, + arg_types[1][1].raw_type, + arg_types[2][1].raw_type) + when 4 create_validator_procedure_fast4(mod, original_method, method_sig, original_visibility, - method_sig.arg_types[0][1].raw_type, - method_sig.arg_types[1][1].raw_type, - method_sig.arg_types[2][1].raw_type, - method_sig.arg_types[3][1].raw_type) + arg_types[0][1].raw_type, + arg_types[1][1].raw_type, + arg_types[2][1].raw_type, + arg_types[3][1].raw_type) else raise 'should not happen' end @@ -608,26 +612,28 @@ def self.create_validator_method_medium(mod, original_method, method_sig, origin raise 'Should have used create_validator_procedure_medium' end # trampoline to reduce stack frame size - if method_sig.arg_types.empty? + arg_types = method_sig.arg_types + case arg_types.length + when 0 create_validator_method_medium0(mod, original_method, method_sig, original_visibility, method_sig.return_type) - elsif method_sig.arg_types.length == 1 + when 1 create_validator_method_medium1(mod, original_method, method_sig, original_visibility, method_sig.return_type, - method_sig.arg_types[0][1]) - elsif method_sig.arg_types.length == 2 + arg_types[0][1]) + when 2 create_validator_method_medium2(mod, original_method, method_sig, original_visibility, method_sig.return_type, - method_sig.arg_types[0][1], - method_sig.arg_types[1][1]) - elsif method_sig.arg_types.length == 3 + arg_types[0][1], + arg_types[1][1]) + when 3 create_validator_method_medium3(mod, original_method, method_sig, original_visibility, method_sig.return_type, - method_sig.arg_types[0][1], - method_sig.arg_types[1][1], - method_sig.arg_types[2][1]) - elsif method_sig.arg_types.length == 4 + arg_types[0][1], + arg_types[1][1], + arg_types[2][1]) + when 4 create_validator_method_medium4(mod, original_method, method_sig, original_visibility, method_sig.return_type, - method_sig.arg_types[0][1], - method_sig.arg_types[1][1], - method_sig.arg_types[2][1], - method_sig.arg_types[3][1]) + arg_types[0][1], + arg_types[1][1], + arg_types[2][1], + arg_types[3][1]) else raise 'should not happen' end @@ -940,26 +946,28 @@ def self.create_validator_method_medium4(mod, original_method, method_sig, origi def self.create_validator_procedure_medium(mod, original_method, method_sig, original_visibility) # trampoline to reduce stack frame size - if method_sig.arg_types.empty? + arg_types = method_sig.arg_types + case arg_types.length + when 0 create_validator_procedure_medium0(mod, original_method, method_sig, original_visibility) - elsif method_sig.arg_types.length == 1 + when 1 create_validator_procedure_medium1(mod, original_method, method_sig, original_visibility, - method_sig.arg_types[0][1]) - elsif method_sig.arg_types.length == 2 + arg_types[0][1]) + when 2 create_validator_procedure_medium2(mod, original_method, method_sig, original_visibility, - method_sig.arg_types[0][1], - method_sig.arg_types[1][1]) - elsif method_sig.arg_types.length == 3 + arg_types[0][1], + arg_types[1][1]) + when 3 create_validator_procedure_medium3(mod, original_method, method_sig, original_visibility, - method_sig.arg_types[0][1], - method_sig.arg_types[1][1], - method_sig.arg_types[2][1]) - elsif method_sig.arg_types.length == 4 + arg_types[0][1], + arg_types[1][1], + arg_types[2][1]) + when 4 create_validator_procedure_medium4(mod, original_method, method_sig, original_visibility, - method_sig.arg_types[0][1], - method_sig.arg_types[1][1], - method_sig.arg_types[2][1], - method_sig.arg_types[3][1]) + arg_types[0][1], + arg_types[1][1], + arg_types[2][1], + arg_types[3][1]) else raise 'should not happen' end diff --git a/gems/sorbet-runtime/lib/types/private/methods/call_validation_2_7.rb b/gems/sorbet-runtime/lib/types/private/methods/call_validation_2_7.rb index 26c261746a..e90abac7fe 100644 --- a/gems/sorbet-runtime/lib/types/private/methods/call_validation_2_7.rb +++ b/gems/sorbet-runtime/lib/types/private/methods/call_validation_2_7.rb @@ -11,26 +11,28 @@ def self.create_validator_method_fast(mod, original_method, method_sig, original raise 'Should have used create_validator_procedure_fast' end # trampoline to reduce stack frame size - if method_sig.arg_types.empty? + arg_types = method_sig.arg_types + case arg_types.length + when 0 create_validator_method_fast0(mod, original_method, method_sig, original_visibility, method_sig.return_type.raw_type) - elsif method_sig.arg_types.length == 1 + when 1 create_validator_method_fast1(mod, original_method, method_sig, original_visibility, method_sig.return_type.raw_type, - method_sig.arg_types[0][1].raw_type) - elsif method_sig.arg_types.length == 2 + arg_types[0][1].raw_type) + when 2 create_validator_method_fast2(mod, original_method, method_sig, original_visibility, method_sig.return_type.raw_type, - method_sig.arg_types[0][1].raw_type, - method_sig.arg_types[1][1].raw_type) - elsif method_sig.arg_types.length == 3 + arg_types[0][1].raw_type, + arg_types[1][1].raw_type) + when 3 create_validator_method_fast3(mod, original_method, method_sig, original_visibility, method_sig.return_type.raw_type, - method_sig.arg_types[0][1].raw_type, - method_sig.arg_types[1][1].raw_type, - method_sig.arg_types[2][1].raw_type) - elsif method_sig.arg_types.length == 4 + arg_types[0][1].raw_type, + arg_types[1][1].raw_type, + arg_types[2][1].raw_type) + when 4 create_validator_method_fast4(mod, original_method, method_sig, original_visibility, method_sig.return_type.raw_type, - method_sig.arg_types[0][1].raw_type, - method_sig.arg_types[1][1].raw_type, - method_sig.arg_types[2][1].raw_type, - method_sig.arg_types[3][1].raw_type) + arg_types[0][1].raw_type, + arg_types[1][1].raw_type, + arg_types[2][1].raw_type, + arg_types[3][1].raw_type) else raise 'should not happen' end @@ -343,26 +345,28 @@ def self.create_validator_method_fast4(mod, original_method, method_sig, origina def self.create_validator_procedure_fast(mod, original_method, method_sig, original_visibility) # trampoline to reduce stack frame size - if method_sig.arg_types.empty? + arg_types = method_sig.arg_types + case arg_types.length + when 0 create_validator_procedure_fast0(mod, original_method, method_sig, original_visibility) - elsif method_sig.arg_types.length == 1 + when 1 create_validator_procedure_fast1(mod, original_method, method_sig, original_visibility, - method_sig.arg_types[0][1].raw_type) - elsif method_sig.arg_types.length == 2 + arg_types[0][1].raw_type) + when 2 create_validator_procedure_fast2(mod, original_method, method_sig, original_visibility, - method_sig.arg_types[0][1].raw_type, - method_sig.arg_types[1][1].raw_type) - elsif method_sig.arg_types.length == 3 + arg_types[0][1].raw_type, + arg_types[1][1].raw_type) + when 3 create_validator_procedure_fast3(mod, original_method, method_sig, original_visibility, - method_sig.arg_types[0][1].raw_type, - method_sig.arg_types[1][1].raw_type, - method_sig.arg_types[2][1].raw_type) - elsif method_sig.arg_types.length == 4 + arg_types[0][1].raw_type, + arg_types[1][1].raw_type, + arg_types[2][1].raw_type) + when 4 create_validator_procedure_fast4(mod, original_method, method_sig, original_visibility, - method_sig.arg_types[0][1].raw_type, - method_sig.arg_types[1][1].raw_type, - method_sig.arg_types[2][1].raw_type, - method_sig.arg_types[3][1].raw_type) + arg_types[0][1].raw_type, + arg_types[1][1].raw_type, + arg_types[2][1].raw_type, + arg_types[3][1].raw_type) else raise 'should not happen' end @@ -608,26 +612,28 @@ def self.create_validator_method_medium(mod, original_method, method_sig, origin raise 'Should have used create_validator_procedure_medium' end # trampoline to reduce stack frame size - if method_sig.arg_types.empty? + arg_types = method_sig.arg_types + case arg_types.length + when 0 create_validator_method_medium0(mod, original_method, method_sig, original_visibility, method_sig.return_type) - elsif method_sig.arg_types.length == 1 + when 1 create_validator_method_medium1(mod, original_method, method_sig, original_visibility, method_sig.return_type, - method_sig.arg_types[0][1]) - elsif method_sig.arg_types.length == 2 + arg_types[0][1]) + when 2 create_validator_method_medium2(mod, original_method, method_sig, original_visibility, method_sig.return_type, - method_sig.arg_types[0][1], - method_sig.arg_types[1][1]) - elsif method_sig.arg_types.length == 3 + arg_types[0][1], + arg_types[1][1]) + when 3 create_validator_method_medium3(mod, original_method, method_sig, original_visibility, method_sig.return_type, - method_sig.arg_types[0][1], - method_sig.arg_types[1][1], - method_sig.arg_types[2][1]) - elsif method_sig.arg_types.length == 4 + arg_types[0][1], + arg_types[1][1], + arg_types[2][1]) + when 4 create_validator_method_medium4(mod, original_method, method_sig, original_visibility, method_sig.return_type, - method_sig.arg_types[0][1], - method_sig.arg_types[1][1], - method_sig.arg_types[2][1], - method_sig.arg_types[3][1]) + arg_types[0][1], + arg_types[1][1], + arg_types[2][1], + arg_types[3][1]) else raise 'should not happen' end @@ -940,26 +946,28 @@ def self.create_validator_method_medium4(mod, original_method, method_sig, origi def self.create_validator_procedure_medium(mod, original_method, method_sig, original_visibility) # trampoline to reduce stack frame size - if method_sig.arg_types.empty? + arg_types = method_sig.arg_types + case arg_types.length + when 0 create_validator_procedure_medium0(mod, original_method, method_sig, original_visibility) - elsif method_sig.arg_types.length == 1 + when 1 create_validator_procedure_medium1(mod, original_method, method_sig, original_visibility, - method_sig.arg_types[0][1]) - elsif method_sig.arg_types.length == 2 + arg_types[0][1]) + when 2 create_validator_procedure_medium2(mod, original_method, method_sig, original_visibility, - method_sig.arg_types[0][1], - method_sig.arg_types[1][1]) - elsif method_sig.arg_types.length == 3 + arg_types[0][1], + arg_types[1][1]) + when 3 create_validator_procedure_medium3(mod, original_method, method_sig, original_visibility, - method_sig.arg_types[0][1], - method_sig.arg_types[1][1], - method_sig.arg_types[2][1]) - elsif method_sig.arg_types.length == 4 + arg_types[0][1], + arg_types[1][1], + arg_types[2][1]) + when 4 create_validator_procedure_medium4(mod, original_method, method_sig, original_visibility, - method_sig.arg_types[0][1], - method_sig.arg_types[1][1], - method_sig.arg_types[2][1], - method_sig.arg_types[3][1]) + arg_types[0][1], + arg_types[1][1], + arg_types[2][1], + arg_types[3][1]) else raise 'should not happen' end diff --git a/gems/sorbet-runtime/lib/types/private/methods/decl_builder.rb b/gems/sorbet-runtime/lib/types/private/methods/decl_builder.rb index b48b1d5b68..e2902e192b 100644 --- a/gems/sorbet-runtime/lib/types/private/methods/decl_builder.rb +++ b/gems/sorbet-runtime/lib/types/private/methods/decl_builder.rb @@ -16,7 +16,6 @@ class BuilderError < StandardError; end end def initialize(mod, raw) - # TODO RUBYPLAT-1278 - with ruby 2.5, use kwargs here @decl = Declaration.new( mod, ARG_NOT_PROVIDED, # params @@ -32,15 +31,29 @@ def initialize(mod, raw) ) end - def params(**params) + def params(*unused_positional_params, **params) check_live! if !decl.params.equal?(ARG_NOT_PROVIDED) raise BuilderError.new("You can't call .params twice") end + if unused_positional_params.any? + some_or_only = params.any? ? "some" : "only" + raise BuilderError.new(<<~MSG) + 'params' was called with #{some_or_only} positional arguments, but it needs to be called with keyword arguments. + The keyword arguments' keys must match the name and order of the method's parameters. + MSG + end + if params.empty? - raise BuilderError.new("params expects keyword arguments") + raise BuilderError.new(<<~MSG) + 'params' was called without any arguments, but it needs to be called with keyword arguments. + The keyword arguments' keys must match the name and order of the method's parameters. + + Omit 'params' entirely for methods with no parameters. + MSG end + decl.params = params self @@ -66,7 +79,7 @@ def void raise BuilderError.new("You can't call .void after calling .returns.") end - decl.returns = T::Private::Types::Void.new + decl.returns = T::Private::Types::Void::Private::INSTANCE self end @@ -218,15 +231,17 @@ def finalize! decl.on_failure = nil end if decl.params.equal?(ARG_NOT_PROVIDED) - decl.params = {} + decl.params = FROZEN_HASH end if decl.type_parameters.equal?(ARG_NOT_PROVIDED) - decl.type_parameters = {} + decl.type_parameters = FROZEN_HASH end decl.finalized = true self end + + FROZEN_HASH = {}.freeze end end diff --git a/gems/sorbet-runtime/lib/types/private/methods/signature.rb b/gems/sorbet-runtime/lib/types/private/methods/signature.rb index bd15977bd5..d0ad9a92a4 100644 --- a/gems/sorbet-runtime/lib/types/private/methods/signature.rb +++ b/gems/sorbet-runtime/lib/types/private/methods/signature.rb @@ -8,17 +8,20 @@ class T::Private::Methods::Signature :check_level, :parameters, :on_failure, :override_allow_incompatible, :defined_raw + UNNAMED_REQUIRED_PARAMETERS = [[:req]].freeze + def self.new_untyped(method:, mode: T::Private::Methods::Modes.untyped, parameters: method.parameters) - # Using `Untyped` ensures we'll get an error if we ever try validation on these. - not_typed = T::Private::Types::NotTyped.new + # Using `NotTyped` ensures we'll get an error if we ever try validation on these. + not_typed = T::Private::Types::NotTyped::INSTANCE raw_return_type = not_typed # Map missing parameter names to "argN" positionally parameters = parameters.each_with_index.map do |(param_kind, param_name), index| [param_kind, param_name || "arg#{index}"] end - raw_arg_types = parameters.map do |_param_kind, param_name| - [param_name, not_typed] - end.to_h + raw_arg_types = {} + parameters.each do |_, param_name| + raw_arg_types[param_name] = not_typed + end self.new( method: method, @@ -57,28 +60,34 @@ def initialize(method:, method_name:, raw_arg_types:, raw_return_type:, bind:, m @override_allow_incompatible = override_allow_incompatible @defined_raw = defined_raw - declared_param_names = raw_arg_types.keys # If sig params are declared but there is a single parameter with a missing name # **and** the method ends with a "=", assume it is a writer method generated # by attr_writer or attr_accessor - writer_method = declared_param_names != [nil] && parameters == [[:req]] && 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 parameters = [[:req, method_name[0...-1].to_sym]] if writer_method - param_names = parameters.map {|_, name| name} - missing_names = param_names - declared_param_names - extra_names = declared_param_names - param_names - if !missing_names.empty? + is_name_missing = parameters.any? {|_, name| !raw_arg_types.key?(name)} + if is_name_missing + param_names = parameters.map {|_, name| name} + missing_names = param_names - raw_arg_types.keys raise "The declaration for `#{method.name}` is missing parameter(s): #{missing_names.join(', ')}" - end - if !extra_names.empty? - raise "The declaration for `#{method.name}` has extra parameter(s): #{extra_names.join(', ')}" + elsif parameters.length == raw_arg_types.size + else + param_names = parameters.map {|_, name| name} + has_extra_names = parameters.count {|_, name| raw_arg_types.key?(name)} < raw_arg_types.size + if has_extra_names + extra_names = raw_arg_types.keys - param_names + raise "The declaration for `#{method.name}` has extra parameter(s): #{extra_names.join(', ')}" + end end if parameters.size != raw_arg_types.size raise "The declaration for `#{method.name}` has arguments with duplicate names" end + i = 0 + raw_arg_types.each do |type_name, raw_type| + param_kind, param_name = parameters[i] - parameters.zip(raw_arg_types) do |(param_kind, param_name), (type_name, raw_type)| if type_name != param_name hint = "" # Ruby reorders params so that required keyword arguments @@ -92,8 +101,8 @@ def initialize(method:, method_name:, raw_arg_types:, raw_return_type:, bind:, m end raise "Parameter `#{type_name}` is declared out of order (declared as arg number " \ - "#{declared_param_names.index(type_name) + 1}, defined in the method as arg number " \ - "#{param_names.index(type_name) + 1}).#{hint}\nMethod: #{method_desc}" + "#{i + 1}, defined in the method as arg number " \ + "#{parameters.index {|_, name| name == type_name} + 1}).#{hint}\nMethod: #{method_desc}" end type = T::Utils.coerce(raw_type) @@ -134,6 +143,8 @@ def initialize(method:, method_name:, raw_arg_types:, raw_return_type:, bind:, m else raise "Unexpected param_kind: `#{param_kind}`. Method: #{method_desc}" end + + i += 1 end end @@ -179,15 +190,7 @@ def each_args_value_type(args) kwargs = EMPTY_HASH end - arg_types = @arg_types - - if @has_rest - rest_count = args_length - @arg_types.length - rest_count = 0 if rest_count.negative? - - arg_types += [[@rest_name, @rest_type]] * rest_count - - elsif (args_length < @req_arg_count) || (args_length > @arg_types.length) + if !@has_rest && ((args_length < @req_arg_count) || (args_length > @arg_types.length)) expected_str = @req_arg_count.to_s if @arg_types.length != @req_arg_count expected_str += "..#{@arg_types.length}" @@ -197,10 +200,23 @@ def each_args_value_type(args) begin it = 0 - while it < args_length - yield arg_types[it][0], args[it], arg_types[it][1] + + # Process given pre-rest args. When there are no rest args, + # this is just the given number of args. + while it < args_length && it < @arg_types.length + yield @arg_types[it][0], args[it], @arg_types[it][1] it += 1 end + + if @has_rest + rest_count = args_length - @arg_types.length + rest_count = 0 if rest_count.negative? + + rest_count.times do + yield @rest_name, args[it], @rest_type + it += 1 + end + end end kwargs.each do |name, val| @@ -208,6 +224,7 @@ def each_args_value_type(args) if !type && @has_keyrest type = @keyrest_type end + yield name, val, type if type end end diff --git a/gems/sorbet-runtime/lib/types/private/methods/signature_validation.rb b/gems/sorbet-runtime/lib/types/private/methods/signature_validation.rb index 16188e0bf3..0f6e58dc83 100644 --- a/gems/sorbet-runtime/lib/types/private/methods/signature_validation.rb +++ b/gems/sorbet-runtime/lib/types/private/methods/signature_validation.rb @@ -6,14 +6,63 @@ module T::Private::Methods::SignatureValidation Modes = Methods::Modes def self.validate(signature) + # Constructors in any language are always a bit weird: they're called in a + # static context, but their bodies are implemented by instance methods. So + # a mix of the rules that apply to instance methods and class methods + # apply. + # + # In languages like Java and Scala, static methods/companion object methods + # are never inherited. (In Java it almost looks like you can inherit them, + # because `Child.static_parent_method` works, but this method is simply + # resolved statically to `Parent.static_parent_method`). Even though most + # instance methods overrides have variance checking done, constructors are + # not treated like this, because static methods are never + # inherited/overridden, and the constructor can only ever be called + # indirectly by way of the static method. (Note: this is only a mental + # model--there's not actually a static method for the constructor in Java, + # there's an `invokespecial` JVM instruction that handles this). + # + # But Ruby is not like Java: singleton class methods in Ruby *are* + # inherited, unlike static methods in Java. In fact, this is similar to how + # JavaScript works. TypeScript simply then sidesteps the issue with + # structural typing: `typeof Parent` is not compatible with `typeof Child` + # if their constructors are different. (In a nominal type system, simply + # having Child descend from Parent should be the only factor in determining + # whether those types are compatible). + # + # Flow has nominal subtyping for classes. When overriding (static and + # instance) methods in a child class, the overrides must satisfy variance + # constraints. But it still carves out an exception for constructors, + # because then literally every class would have to have the same + # constructor. This is simply unsound. Hack does a similar thing--static + # method overrides are checked, but not constructors. Though what Hack + # *does* have is a way to opt into override checking for constructors with + # a special annotation. + # + # It turns out, Sorbet already has this special annotation: either + # `abstract` or `overridable`. At time of writing, *no* static override + # checking happens unless marked with these keywords (though at runtime, it + # always happens). Getting the static system to parity with the runtime by + # always checking overrides would be a great place to get to one day, but + # for now we can take advantage of it by only doing override checks for + # constructors if they've opted in. + # + # (When we get around to more widely checking overrides statically, we will + # need to build a matching special case for constructors statically.) + # + # Note that this breaks with tradition: normally, constructors are not + # allowed to be abstract. But that's kind of a side-effect of everything + # above: in Java/Scala, singleton class methods are never abstract because + # they're not inherited, and this extends to constructors. TypeScript + # simply rejects `new klass()` entirely if `klass` is + # `typeof AbstractClass`, requiring instead that you write + # `{ new(): AbstractClass }`. We may want to consider building some + # analogue to `T.class_of` in the future that works like this `{new(): + # ...}` type. if signature.method_name == :initialize && signature.method.owner.is_a?(Class) - # Constructors are special. They look like overrides in terms of a super_method existing, - # but in practice, you never call them polymorphically. Conceptually, they're standard - # methods (this is consistent with how they're treated in other languages, e.g. Java) - if signature.mode != Modes.standard - raise "`initialize` should not use `.abstract` or `.implementation` or any other inheritance modifiers." + if signature.mode == Modes.standard + return end - return end super_method = signature.method.super_method @@ -59,6 +108,19 @@ def self.validate_override_mode(signature, super_signature) case signature.mode when *Modes::OVERRIDE_MODES # Peaceful + when Modes.abstract + # Either the parent method is abstract, or it's not. + # + # If it's abstract, we want to allow overriding abstract with abstract to + # possibly narrow the type or provide more specific documentation. + # + # If it's not, then marking this method `abstract` will silently be a no-op. + # That's bad and we probably want to report an error, but fixing that + # will have to be a separate fix (that bad behavior predates this current + # comment, introduced when we fixed the abstract/abstract case). + # + # Therefore: + # Peaceful (mostly) when *Modes::NON_OVERRIDE_MODES if super_signature.mode == Modes.standard # Peaceful diff --git a/gems/sorbet-runtime/lib/types/private/runtime_levels.rb b/gems/sorbet-runtime/lib/types/private/runtime_levels.rb index 2454b21988..6747806d22 100644 --- a/gems/sorbet-runtime/lib/types/private/runtime_levels.rb +++ b/gems/sorbet-runtime/lib/types/private/runtime_levels.rb @@ -53,10 +53,29 @@ def self.default_checked_level=(default_checked_level) if @has_read_default_checked_level raise "Set the default checked level earlier. There are already some methods whose sig blocks have evaluated which would not be affected by the new default." end + if !LEVELS.include?(default_checked_level) + raise "Invalid `checked` level '#{default_checked_level}'. Use one of: #{LEVELS}." + end + @default_checked_level = default_checked_level end def self._toggle_checking_tests(checked) @check_tests = checked end + + private_class_method def self.set_enable_checking_in_tests_from_environment + if ENV['SORBET_RUNTIME_ENABLE_CHECKING_IN_TESTS'] + enable_checking_in_tests + end + end + set_enable_checking_in_tests_from_environment + + private_class_method def self.set_default_checked_level_from_environment + level = ENV['SORBET_RUNTIME_DEFAULT_CHECKED_LEVEL'] + if level + self.default_checked_level = level.to_sym + end + end + set_default_checked_level_from_environment end diff --git a/gems/sorbet-runtime/lib/types/private/types/not_typed.rb b/gems/sorbet-runtime/lib/types/private/types/not_typed.rb index 57196553b6..34f493fa72 100644 --- a/gems/sorbet-runtime/lib/types/private/types/not_typed.rb +++ b/gems/sorbet-runtime/lib/types/private/types/not_typed.rb @@ -20,4 +20,6 @@ def valid?(obj) private def subtype_of_single?(other) raise ERROR_MESSAGE end + + INSTANCE = ::T::Private::Types::NotTyped.new.freeze end diff --git a/gems/sorbet-runtime/lib/types/private/types/simple_pair_union.rb b/gems/sorbet-runtime/lib/types/private/types/simple_pair_union.rb index e4e7d9b875..910111a9ba 100644 --- a/gems/sorbet-runtime/lib/types/private/types/simple_pair_union.rb +++ b/gems/sorbet-runtime/lib/types/private/types/simple_pair_union.rb @@ -39,4 +39,17 @@ def types T::Types::Simple::Private::Pool.type_for_module(@raw_b), ] end + + # overrides Union + def unwrap_nilable + a_nil = @raw_a.equal?(NilClass) + b_nil = @raw_b.equal?(NilClass) + if a_nil + return types[1] + end + if b_nil + return types[0] + end + nil + end end diff --git a/gems/sorbet-runtime/lib/types/private/types/void.rb b/gems/sorbet-runtime/lib/types/private/types/void.rb index 310fc56abc..f1a65e0b6d 100644 --- a/gems/sorbet-runtime/lib/types/private/types/void.rb +++ b/gems/sorbet-runtime/lib/types/private/types/void.rb @@ -3,32 +3,38 @@ # A marking class for when methods return void. # Should never appear in types directly. -class T::Private::Types::Void < T::Types::Base - ERROR_MESSAGE = "Validation is being done on an `Void`. Please report this bug at https://github.com/sorbet/sorbet/issues" +module T::Private::Types + class Void < T::Types::Base + ERROR_MESSAGE = "Validation is being done on an `Void`. Please report this bug at https://github.com/sorbet/sorbet/issues" - # The actual return value of `.void` methods. - # - # Uses `module VOID` because this gives it a readable name when someone - # examines it in Pry or with `#inspect` like: - # - # T::Private::Types::Void::VOID - # - module VOID - freeze - end + # The actual return value of `.void` methods. + # + # Uses `module VOID` because this gives it a readable name when someone + # examines it in Pry or with `#inspect` like: + # + # T::Private::Types::Void::VOID + # + module VOID + freeze + end - # overrides Base - def name - "" - end + # overrides Base + def name + "" + end - # overrides Base - def valid?(obj) - raise ERROR_MESSAGE - end + # overrides Base + def valid?(obj) + raise ERROR_MESSAGE + end + + # overrides Base + private def subtype_of_single?(other) + raise ERROR_MESSAGE + end - # overrides Base - private def subtype_of_single?(other) - raise ERROR_MESSAGE + module Private + INSTANCE = Void.new.freeze + end end end diff --git a/gems/sorbet-runtime/lib/types/props/_props.rb b/gems/sorbet-runtime/lib/types/props/_props.rb index fae252aa04..8d81d30822 100644 --- a/gems/sorbet-runtime/lib/types/props/_props.rb +++ b/gems/sorbet-runtime/lib/types/props/_props.rb @@ -139,9 +139,9 @@ def const(name, cls_or_args, args={}) end if cls_or_args.is_a?(Hash) - self.prop(name, cls_or_args.merge(immutable: true)) + self.prop(name, **cls_or_args.merge(immutable: true)) else - self.prop(name, cls_or_args, args.merge(immutable: true)) + self.prop(name, cls_or_args, **args.merge(immutable: true)) end end diff --git a/gems/sorbet-runtime/lib/types/props/custom_type.rb b/gems/sorbet-runtime/lib/types/props/custom_type.rb index 0df6a0d863..a09b6a76c5 100644 --- a/gems/sorbet-runtime/lib/types/props/custom_type.rb +++ b/gems/sorbet-runtime/lib/types/props/custom_type.rb @@ -57,12 +57,12 @@ def self.included(_base) raise 'Please use "extend", not "include" to attach this module' end - sig(:final) {params(val: Object).returns(T::Boolean).checked(:never)} + sig(:final) {params(val: T.untyped).returns(T::Boolean).checked(:never)} def self.scalar_type?(val) # We don't need to check for val's included modules in # T::Configuration.scalar_types, because T::Configuration.scalar_types # are all classes. - klass = T.let(val.class, T.nilable(Class)) + klass = val.class until klass.nil? return true if T::Configuration.scalar_types.include?(klass.to_s) klass = klass.superclass diff --git a/gems/sorbet-runtime/lib/types/props/decorator.rb b/gems/sorbet-runtime/lib/types/props/decorator.rb index 8013495fd8..c55b8a6f94 100644 --- a/gems/sorbet-runtime/lib/types/props/decorator.rb +++ b/gems/sorbet-runtime/lib/types/props/decorator.rb @@ -17,7 +17,7 @@ class T::Props::Decorator class NoRulesError < StandardError; end - EMPTY_PROPS = T.let({}.freeze, T::Hash[Symbol, Rules]) + EMPTY_PROPS = T.let({}.freeze, T::Hash[Symbol, Rules], checked: false) private_constant :EMPTY_PROPS sig {params(klass: T.untyped).void.checked(:never)} @@ -26,7 +26,7 @@ def initialize(klass) @class.plugins.each do |mod| T::Props::Plugin::Private.apply_decorator_methods(mod, self) end - @props = T.let(EMPTY_PROPS, T::Hash[Symbol, Rules]) + @props = T.let(EMPTY_PROPS, T::Hash[Symbol, Rules], checked: false) end # checked(:never) - O(prop accesses) @@ -50,9 +50,9 @@ def add_prop_definition(prop, rules) override = rules.delete(:override) if props.include?(prop) && !override - raise ArgumentError.new("Attempted to redefine prop #{prop.inspect} that's already defined without specifying :override => true: #{prop_rules(prop)}") + raise ArgumentError.new("Attempted to redefine prop #{prop.inspect} on class #{@class} that's already defined without specifying :override => true: #{prop_rules(prop)}") elsif !props.include?(prop) && override - raise ArgumentError.new("Attempted to override a prop #{prop.inspect} that doesn't already exist") + raise ArgumentError.new("Attempted to override a prop #{prop.inspect} on class #{@class} that doesn't already exist") end @props = @props.merge(prop => rules.freeze).freeze @@ -79,7 +79,7 @@ def add_prop_definition(prop, rules) extra setter_validate _tnilable - ].map {|k| [k, true]}.to_h.freeze, T::Hash[Symbol, T::Boolean]) + ].to_h {|k| [k, true]}.freeze, T::Hash[Symbol, T::Boolean], checked: false) private_constant :VALID_RULE_KEYS sig {params(key: Symbol).returns(T::Boolean).checked(:never)} @@ -205,7 +205,7 @@ def foreign_prop_get(instance, prop, foreign_class, rules=prop_rules(prop), opts end # 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]) + 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 sig do @@ -247,10 +247,10 @@ def prop_validate_definition!(name, cls, rules, type) nil end - SAFE_NAME = T.let(/\A[A-Za-z_][A-Za-z0-9_-]*\z/.freeze, Regexp) + 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 - sig {params(name: T.any(Symbol, String)).void} + sig {params(name: T.any(Symbol, String)).void.checked(:never)} private def validate_prop_name(name) if !name.match?(SAFE_NAME) raise ArgumentError.new("Invalid prop name in #{@class.name}: #{name}") @@ -258,7 +258,7 @@ def prop_validate_definition!(name, cls, rules, type) end # This converts the type from a T::Type to a regular old ruby class. - sig {params(type: T::Types::Base).returns(Module)} + sig {params(type: T::Types::Base).returns(Module).checked(:never)} private def convert_type_to_class(type) case type when T::Types::TypedArray, T::Types::FixedArray @@ -338,34 +338,34 @@ def prop_defined(name, cls, rules={}) # Retrive the possible underlying object with T.nilable. type = T::Utils::Nilable.get_underlying_type(type) - sensitivity_and_pii = {sensitivity: rules[:sensitivity]} - normalize = T::Configuration.normalize_sensitivity_and_pii_handler - if normalize - sensitivity_and_pii = normalize.call(sensitivity_and_pii) - - # We check for Class so this is only applied on concrete - # documents/models; We allow mixins containing props to not - # specify their PII nature, as long as every class into which they - # are ultimately included does. - # - if sensitivity_and_pii[:pii] && @class.is_a?(Class) && !T.unsafe(@class).contains_pii? - raise ArgumentError.new( - 'Cannot include a pii prop in a class that declares `contains_no_pii`' - ) + rules_sensitivity = rules[:sensitivity] + sensitivity_and_pii = {sensitivity: rules_sensitivity} + if !rules_sensitivity.nil? + normalize = T::Configuration.normalize_sensitivity_and_pii_handler + if normalize + sensitivity_and_pii = normalize.call(sensitivity_and_pii) + + # We check for Class so this is only applied on concrete + # documents/models; We allow mixins containing props to not + # specify their PII nature, as long as every class into which they + # are ultimately included does. + # + if sensitivity_and_pii[:pii] && @class.is_a?(Class) && !T.unsafe(@class).contains_pii? + raise ArgumentError.new( + 'Cannot include a pii prop in a class that declares `contains_no_pii`' + ) + end end end - rules = rules.merge( - # TODO: The type of this element is confusing. We should refactor so that - # it can be always `type_object` (a PropType) or always `cls` (a Module) - type: type, - type_object: type_object, - accessor_key: "@#{name}".to_sym, - sensitivity: sensitivity_and_pii[:sensitivity], - pii: sensitivity_and_pii[:pii], - # extra arbitrary metadata attached by the code defining this property - extra: rules[:extra]&.freeze, - ) + rules[:type] = type + rules[:type_object] = type_object + rules[:accessor_key] = "@#{name}".to_sym + rules[:sensitivity] = sensitivity_and_pii[:sensitivity] + rules[:pii] = sensitivity_and_pii[:pii] + rules[:extra] = rules[:extra]&.freeze + + # extra arbitrary metadata attached by the code defining this property validate_not_missing_sensitivity(name, rules) @@ -419,6 +419,7 @@ def prop_defined(name, cls, rules={}) sig do params(type: PropTypeOrClass, enum: T.untyped) .returns(T::Types::Base) + .checked(:never) end private def smart_coerce(type, enum:) # Backwards compatibility for pre-T::Types style @@ -471,6 +472,7 @@ def prop_defined(name, cls, rules={}) redaction: T.untyped, ) .void + .checked(:never) end private def handle_redaction_option(prop_name, redaction) redacted_method = "#{prop_name}_redacted" @@ -492,6 +494,7 @@ def prop_defined(name, cls, rules={}) valid_type_msg: String, ) .void + .checked(:never) end private def validate_foreign_option(option_sym, foreign, valid_type_msg:) if foreign.is_a?(Symbol) || foreign.is_a?(String) @@ -523,8 +526,8 @@ def prop_defined(name, cls, rules={}) # here, but we're baking in `allow_direct_mutation` since we # *haven't* allowed additional options in the past and want to # default to keeping this interface narrow. + foreign = T.let(foreign, T.untyped, checked: false) @class.send(:define_method, fk_method) do |allow_direct_mutation: nil| - foreign = T.let(foreign, T.untyped) if foreign.is_a?(Proc) resolved_foreign = foreign.call if !resolved_foreign.respond_to?(:load) diff --git a/gems/sorbet-runtime/lib/types/props/pretty_printable.rb b/gems/sorbet-runtime/lib/types/props/pretty_printable.rb index e821cd3435..496fca8d1d 100644 --- a/gems/sorbet-runtime/lib/types/props/pretty_printable.rb +++ b/gems/sorbet-runtime/lib/types/props/pretty_printable.rb @@ -1,18 +1,49 @@ # frozen_string_literal: true # typed: true +require 'pp' module T::Props::PrettyPrintable include T::Props::Plugin - # Return a string representation of this object and all of its props + # Override the PP gem with something that's similar, but gives us a hook to do redaction and customization + def pretty_print(pp) + clazz = T.unsafe(T.cast(self, Object).class).decorator + multiline = pp.is_a?(PP) + pp.group(1, "<#{clazz.inspect_class_with_decoration(self)}", ">") do + clazz.all_props.sort.each do |prop| + pp.breakable + val = clazz.get(self, prop) + rules = clazz.prop_rules(prop) + pp.text("#{prop}=") + if (custom_inspect = rules[:inspect]) + inspected = if T::Utils.arity(custom_inspect) == 1 + custom_inspect.call(val) + else + custom_inspect.call(val, {multiline: multiline}) + end + pp.text(inspected.nil? ? "nil" : inspected) + elsif rules[:sensitivity] && !rules[:sensitivity].empty? && !val.nil? + pp.text("") + else + val.pretty_print(pp) + end + end + clazz.pretty_print_extra(self, pp) + end + end + + # Return a string representation of this object and all of its props in a single line def inspect - T.unsafe(T.cast(self, Object).class).decorator.inspect_instance(self) + string = +"" + PP.singleline_pp(self, string) + string end - # Override the PP gem with something that's similar, but gives us a hook - # to do redaction + # Return a pretty string representation of this object and all of its props def pretty_inspect - T.unsafe(T.cast(self, Object).class).decorator.inspect_instance(self, multiline: true) + string = +"" + PP.pp(self, string) + string end module DecoratorMethods @@ -23,85 +54,16 @@ def valid_rule_key?(key) super || key == :inspect end - sig do - params(instance: T::Props::PrettyPrintable, multiline: T::Boolean, indent: String) - .returns(String) + # 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 overriden + sig {params(instance: T::Props::PrettyPrintable).returns(String)} + def inspect_class_with_decoration(instance) + T.unsafe(instance).class.to_s end - def inspect_instance(instance, multiline: false, indent: ' ') - components = - inspect_instance_components( - instance, - multiline: multiline, - indent: indent - ) - .reject(&:empty?) - - # Not using #<> here as that makes pry highlight these objects - # as if they were all comments, whereas this makes them look - # like the structured thing they are. - if multiline - "#{components[0]}:\n" + T.must(components[1..-1]).join("\n") - else - "<#{components.join(' ')}>" - end - end - - sig do - params(instance: T::Props::PrettyPrintable, multiline: T::Boolean, indent: String) - .returns(T::Array[String]) - end - private def inspect_instance_components(instance, multiline:, indent:) - pretty_props = T.unsafe(self).all_props.map do |prop| - [prop, inspect_prop_value(instance, prop, multiline: multiline, indent: indent)] - end - - joined_props = join_props_with_pretty_values( - pretty_props, - multiline: multiline, - indent: indent - ) - [ - T.unsafe(self).decorated_class.to_s, - joined_props, - ] - end - - sig do - params(instance: T::Props::PrettyPrintable, prop: Symbol, multiline: T::Boolean, indent: String) - .returns(String) - .checked(:never) - end - private def inspect_prop_value(instance, prop, multiline:, indent:) - val = T.unsafe(self).get(instance, prop) - rules = T.unsafe(self).prop_rules(prop) - if (custom_inspect = rules[:inspect]) - if T::Utils.arity(custom_inspect) == 1 - custom_inspect.call(val) - else - custom_inspect.call(val, {multiline: multiline, indent: indent}) - end - elsif rules[:sensitivity] && !rules[:sensitivity].empty? && !val.nil? - "" - else - val.inspect - end - end - - sig do - params(pretty_kvs: T::Array[[Symbol, String]], multiline: T::Boolean, indent: String) - .returns(String) - end - private def join_props_with_pretty_values(pretty_kvs, multiline:, indent: ' ') - pairs = pretty_kvs - .sort_by {|k, _v| k.to_s} - .map {|k, v| "#{k}=#{v}"} - - if multiline - indent + pairs.join("\n#{indent}") - else - pairs.join(', ') - end - end + # 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` + sig {params(instance: T::Props::PrettyPrintable, pp: T.any(PrettyPrint, PP::SingleLine)).void} + def pretty_print_extra(instance, pp); end end end diff --git a/gems/sorbet-runtime/lib/types/props/serializable.rb b/gems/sorbet-runtime/lib/types/props/serializable.rb index 2e2ea1b749..4f886d59d8 100644 --- a/gems/sorbet-runtime/lib/types/props/serializable.rb +++ b/gems/sorbet-runtime/lib/types/props/serializable.rb @@ -223,9 +223,10 @@ def serialized_form_prop(serialized_form) end def add_prop_definition(prop, rules) - rules[:serialized_form] = rules.fetch(:name, prop.to_s) + serialized_form = rules.fetch(:name, prop.to_s) + rules[:serialized_form] = serialized_form res = super - prop_by_serialized_forms[rules[:serialized_form]] = prop + prop_by_serialized_forms[serialized_form] = prop 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_deserialize) {generate_deserialize2} @@ -338,14 +339,21 @@ def extra_props(instance) end end - # overrides T::Props::PrettyPrintable - private def inspect_instance_components(instance, multiline:, indent:) + # adds to the default result of T::Props::PrettyPrintable + def pretty_print_extra(instance, pp) + # This is to maintain backwards compatibility with Stripe's codebase, where only the single line (through `inspect`) + # version is expected to add anything extra + return if !pp.is_a?(PP::SingleLine) if (extra_props = extra_props(instance)) && !extra_props.empty? - pretty_kvs = extra_props.map {|k, v| [k.to_sym, v.inspect]} - extra = join_props_with_pretty_values(pretty_kvs, multiline: false) - super + ["@_extra_props=<#{extra}>"] - else - super + pp.breakable + pp.text("@_extra_props=") + pp.group(1, "<", ">") do + extra_props.each_with_index do |(prop, value), i| + pp.breakable unless i.zero? + pp.text("#{prop}=") + value.pretty_print(pp) + end + end end end end diff --git a/gems/sorbet-runtime/lib/types/props/type_validation.rb b/gems/sorbet-runtime/lib/types/props/type_validation.rb index ae3557783d..8a7dc9a4df 100644 --- a/gems/sorbet-runtime/lib/types/props/type_validation.rb +++ b/gems/sorbet-runtime/lib/types/props/type_validation.rb @@ -16,6 +16,7 @@ def valid_rule_key?(key) super || key == :DEPRECATED_underspecified_type end + # checked(:never) - Rules hash is expensive to check sig do params( name: T.any(Symbol, String), @@ -24,12 +25,13 @@ def valid_rule_key?(key) type: T.any(T::Types::Base, Module) ) .void + .checked(:never) end def prop_validate_definition!(name, _cls, rules, type) super if !rules[:DEPRECATED_underspecified_type] - validate_type(type, field_name: name) + validate_type(type, name) elsif rules[:DEPRECATED_underspecified_type] && find_invalid_subtype(type).nil? raise ArgumentError.new("DEPRECATED_underspecified_type set unnecessarily for #{@class.name}.#{name} - #{type} is a valid type") end @@ -41,8 +43,9 @@ def prop_validate_definition!(name, _cls, rules, type) field_name: T.any(Symbol, String), ) .void + .checked(:never) end - private def validate_type(type, field_name:) + private def validate_type(type, field_name) if (invalid_subtype = find_invalid_subtype(type)) raise UnderspecifiedType.new(type_error_message(invalid_subtype, field_name, type)) end diff --git a/gems/sorbet-runtime/lib/types/types/anything.rb b/gems/sorbet-runtime/lib/types/types/anything.rb new file mode 100644 index 0000000000..8374daba1f --- /dev/null +++ b/gems/sorbet-runtime/lib/types/types/anything.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true +# typed: true + +module T::Types + # The top type + class Anything < Base + def initialize; end + + # overrides Base + def name + "T.anything" + end + + # overrides Base + def valid?(obj) + true + end + + # overrides Base + private def subtype_of_single?(other) + case other + when T::Types::Anything then true + else false + end + end + + module Private + INSTANCE = Anything.new.freeze + end + end +end diff --git a/gems/sorbet-runtime/lib/types/types/base.rb b/gems/sorbet-runtime/lib/types/types/base.rb index 8cbe36b18d..2096bbc99a 100644 --- a/gems/sorbet-runtime/lib/types/types/base.rb +++ b/gems/sorbet-runtime/lib/types/types/base.rb @@ -50,10 +50,20 @@ def subtype_of?(t2) t2 = t2.aliased_type end + if t2.is_a?(T::Types::Anything) + return true + end + if t1.is_a?(T::Private::Types::TypeAlias) return t1.aliased_type.subtype_of?(t2) end + if t1.is_a?(T::Types::TypeVariable) || t2.is_a?(T::Types::TypeVariable) + # Generics are erased at runtime. Let's treat them like `T.untyped` for + # the purpose of things like override checking. + return true + end + # pairs to cover: 1 (_, _) # 2 (_, And) # 3 (_, Or) diff --git a/gems/sorbet-runtime/lib/types/types/class_of.rb b/gems/sorbet-runtime/lib/types/types/class_of.rb index d56f21d4c7..3ffb3d90ce 100644 --- a/gems/sorbet-runtime/lib/types/types/class_of.rb +++ b/gems/sorbet-runtime/lib/types/types/class_of.rb @@ -27,6 +27,8 @@ def subtype_of_single?(other) @type <= other.type when Simple @type.is_a?(other.raw_type) + when TypedClass + true else false end diff --git a/gems/sorbet-runtime/lib/types/types/fixed_array.rb b/gems/sorbet-runtime/lib/types/types/fixed_array.rb index 3bed082bcc..645a617f1a 100644 --- a/gems/sorbet-runtime/lib/types/types/fixed_array.rb +++ b/gems/sorbet-runtime/lib/types/types/fixed_array.rb @@ -59,6 +59,19 @@ def valid?(obj) @types.size == other.types.size && @types.zip(other.types).all? do |t1, t2| t1.subtype_of?(t2) end + when TypedArray + # warning: covariant arrays + + value1, value2, *values_rest = types + value_type = if !value2.nil? + T::Types::Union::Private::Pool.union_of_types(value1, value2, values_rest) + elsif value1.nil? + T.untyped + else + value1 + end + + T::Types::TypedArray.new(value_type).subtype_of?(other) else false end diff --git a/gems/sorbet-runtime/lib/types/types/fixed_hash.rb b/gems/sorbet-runtime/lib/types/types/fixed_hash.rb index d8fddb3703..4c2929586c 100644 --- a/gems/sorbet-runtime/lib/types/types/fixed_hash.rb +++ b/gems/sorbet-runtime/lib/types/types/fixed_hash.rb @@ -38,6 +38,28 @@ def valid?(obj) when FixedHash # Using `subtype_of?` here instead of == would be unsound @types == other.types + when TypedHash + # warning: covariant hashes + + key1, key2, *keys_rest = types.keys.map {|key| T::Utils.coerce(key.class)} + key_type = if !key2.nil? + T::Types::Union::Private::Pool.union_of_types(key1, key2, keys_rest) + elsif key1.nil? + T.untyped + else + key1 + end + + value1, value2, *values_rest = types.values + value_type = if !value2.nil? + T::Types::Union::Private::Pool.union_of_types(value1, value2, values_rest) + elsif value1.nil? + T.untyped + else + value1 + end + + T::Types::TypedHash.new(keys: key_type, values: value_type).subtype_of?(other) else false end diff --git a/gems/sorbet-runtime/lib/types/types/noreturn.rb b/gems/sorbet-runtime/lib/types/types/noreturn.rb index c285797480..f784450b48 100644 --- a/gems/sorbet-runtime/lib/types/types/noreturn.rb +++ b/gems/sorbet-runtime/lib/types/types/noreturn.rb @@ -4,7 +4,6 @@ module T::Types # The bottom type class NoReturn < Base - def initialize; end # overrides Base diff --git a/gems/sorbet-runtime/lib/types/types/simple.rb b/gems/sorbet-runtime/lib/types/types/simple.rb index 20c5ca6055..1c11ebe5a6 100644 --- a/gems/sorbet-runtime/lib/types/types/simple.rb +++ b/gems/sorbet-runtime/lib/types/types/simple.rb @@ -60,6 +60,13 @@ def to_nilable module Private module Pool + CACHE_FROZEN_OBJECTS = begin + ObjectSpace::WeakMap.new[1] = 1 + true # Ruby 2.7 and newer + rescue ArgumentError # Ruby 2.6 and older + false + end + @cache = ObjectSpace::WeakMap.new def self.type_for_module(mod) @@ -83,12 +90,14 @@ def self.type_for_module(mod) end # Unfortunately, we still need to check if the module is frozen, - # since WeakMap adds a finalizer to the key that is added + # since on 2.6 and older WeakMap adds a finalizer to the key that is added # to the map, so that it can clear the map entry when the key is # garbage collected. # For a frozen object, though, adding a finalizer is not a valid # operation, so this still raises if `mod` is frozen. - @cache[mod] = type unless mod.frozen? + if CACHE_FROZEN_OBJECTS || (!mod.frozen? && !type.frozen?) + @cache[mod] = type + end type end end diff --git a/gems/sorbet-runtime/lib/types/types/type_parameter.rb b/gems/sorbet-runtime/lib/types/types/type_parameter.rb index ea82a07cc3..4edf5368c3 100644 --- a/gems/sorbet-runtime/lib/types/types/type_parameter.rb +++ b/gems/sorbet-runtime/lib/types/types/type_parameter.rb @@ -3,11 +3,30 @@ module T::Types class TypeParameter < Base + module Private + @pool = {} + + def self.cached_entry(name) + @pool[name] + end + + def self.set_entry_for(name, type) + @pool[name] = type + end + end + def initialize(name) raise ArgumentError.new("not a symbol: #{name}") unless name.is_a?(Symbol) @name = name end + def self.make(name) + cached = Private.cached_entry(name) + return cached if cached + + Private.set_entry_for(name, new(name)) + end + def valid?(obj) true end diff --git a/gems/sorbet-runtime/lib/types/types/typed_array.rb b/gems/sorbet-runtime/lib/types/types/typed_array.rb index 0b5f3541a9..9e4d4152c1 100644 --- a/gems/sorbet-runtime/lib/types/types/typed_array.rb +++ b/gems/sorbet-runtime/lib/types/types/typed_array.rb @@ -26,6 +26,31 @@ def new(*args) Array.new(*T.unsafe(args)) end + module Private + module Pool + CACHE_FROZEN_OBJECTS = begin + ObjectSpace::WeakMap.new[1] = 1 + true # Ruby 2.7 and newer + rescue ArgumentError # Ruby 2.6 and older + false + end + + @cache = ObjectSpace::WeakMap.new + + def self.type_for_module(mod) + cached = @cache[mod] + return cached if cached + + type = TypedArray.new(mod) + + if CACHE_FROZEN_OBJECTS || (!mod.frozen? && !type.frozen?) + @cache[mod] = type + end + type + end + end + end + class Untyped < TypedArray def initialize super(T.untyped) @@ -34,6 +59,10 @@ def initialize def valid?(obj) obj.is_a?(Array) end + + module Private + INSTANCE = Untyped.new.freeze + end end end end diff --git a/gems/sorbet-runtime/lib/types/types/typed_class.rb b/gems/sorbet-runtime/lib/types/types/typed_class.rb new file mode 100644 index 0000000000..0088300841 --- /dev/null +++ b/gems/sorbet-runtime/lib/types/types/typed_class.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true +# typed: true + +module T::Types + class TypedClass < T::Types::Base + attr_reader :type + + def initialize(type) + @type = T::Utils.coerce(type) + end + + # overrides Base + def name + "T::Class[#{@type.name}]" + end + + def underlying_class + Class + end + + # overrides Base + def valid?(obj) + Class.===(obj) + end + + # overrides Base + private def subtype_of_single?(type) + case type + when TypedClass + # treat like generics are erased + true + when Simple + Class <= type.raw_type + else + false + end + end + + module Private + module Pool + CACHE_FROZEN_OBJECTS = + begin + ObjectSpace::WeakMap.new[1] = 1 + true # Ruby 2.7 and newer + rescue ArgumentError + false # Ruby 2.6 and older + end + + @cache = ObjectSpace::WeakMap.new + + def self.type_for_module(mod) + cached = @cache[mod] + return cached if cached + + type = TypedClass.new(mod) + + if CACHE_FROZEN_OBJECTS || (!mod.frozen? && !type.frozen?) + @cache[mod] = type + end + type + end + end + end + + class Untyped < TypedClass + def initialize + super(T.untyped) + end + + module Private + INSTANCE = Untyped.new.freeze + end + end + + class Anything < TypedClass + def initialize + super(T.anything) + end + + module Private + INSTANCE = Anything.new.freeze + end + end + end +end diff --git a/gems/sorbet-runtime/lib/types/types/typed_enumerable.rb b/gems/sorbet-runtime/lib/types/types/typed_enumerable.rb index 65c2631f0a..39fe5571b5 100644 --- a/gems/sorbet-runtime/lib/types/types/typed_enumerable.rb +++ b/gems/sorbet-runtime/lib/types/types/typed_enumerable.rb @@ -54,6 +54,9 @@ def recursively_valid?(obj) when Enumerator::Lazy # Enumerators can be unbounded: see `[:foo, :bar].cycle` true + when Enumerator::Chain + # Enumerators can be unbounded: see `[:foo, :bar].cycle` + true when Enumerator # Enumerators can be unbounded: see `[:foo, :bar].cycle` true @@ -88,6 +91,8 @@ def recursively_valid?(obj) # both reading and writing. However, Sorbet treats *all* # Enumerable subclasses as covariant for ease of adoption. @type.subtype_of?(other.type) + elsif other.class <= Simple + underlying_class <= other.raw_type else false end @@ -145,6 +150,8 @@ def describe_obj(obj) end when Enumerator::Lazy T::Enumerator::Lazy[type_from_instances(obj)] + when Enumerator::Chain + T::Enumerator::Chain[type_from_instances(obj)] when Enumerator T::Enumerator[type_from_instances(obj)] when Set diff --git a/gems/sorbet-runtime/lib/types/types/typed_enumerator_chain.rb b/gems/sorbet-runtime/lib/types/types/typed_enumerator_chain.rb new file mode 100644 index 0000000000..f85b6ea01f --- /dev/null +++ b/gems/sorbet-runtime/lib/types/types/typed_enumerator_chain.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true +# typed: true + +module T::Types + class TypedEnumeratorChain < TypedEnumerable + attr_reader :type + + def underlying_class + Enumerator::Chain + end + + # overrides Base + def name + "T::Enumerator::Chain[#{@type.name}]" + end + + # overrides Base + def recursively_valid?(obj) + obj.is_a?(Enumerator::Chain) && super + end + + # overrides Base + def valid?(obj) + obj.is_a?(Enumerator::Chain) + end + + def new(*args, &blk) + T.unsafe(Enumerator::Chain).new(*args, &blk) + end + + class Untyped < TypedEnumeratorChain + def initialize + super(T.untyped) + end + + def valid?(obj) + obj.is_a?(Enumerator::Chain) + end + end + end +end diff --git a/gems/sorbet-runtime/lib/types/types/union.rb b/gems/sorbet-runtime/lib/types/types/union.rb index 1d6b1361be..a2f160a82b 100644 --- a/gems/sorbet-runtime/lib/types/types/union.rb +++ b/gems/sorbet-runtime/lib/types/types/union.rb @@ -64,6 +64,17 @@ def valid?(obj) raise "This should never be reached if you're going through `subtype_of?` (and you should be)" end + def unwrap_nilable + non_nil_types = types.reject {|t| t == T::Utils::Nilable::NIL_TYPE} + return nil if types.length == non_nil_types.length + case non_nil_types.length + when 0 then nil + when 1 then non_nil_types.first + else + T::Types::Union::Private::Pool.union_of_types(non_nil_types[0], non_nil_types[1], non_nil_types[2..-1]) + end + end + module Private module Pool EMPTY_ARRAY = [].freeze diff --git a/gems/sorbet-runtime/lib/types/utils.rb b/gems/sorbet-runtime/lib/types/utils.rb index 8adffaac31..3c72e14915 100644 --- a/gems/sorbet-runtime/lib/types/utils.rb +++ b/gems/sorbet-runtime/lib/types/utils.rb @@ -98,14 +98,7 @@ def self.resolve_alias(type) def self.unwrap_nilable(type) case type when T::Types::Union - non_nil_types = type.types.reject {|t| t == Nilable::NIL_TYPE} - return nil if type.types.length == non_nil_types.length - case non_nil_types.length - when 0 then nil - when 1 then non_nil_types.first - else - T::Types::Union::Private::Pool.union_of_types(non_nil_types[0], non_nil_types[1], non_nil_types[2..-1]) - end + type.unwrap_nilable else nil end @@ -179,7 +172,7 @@ module Nilable def self.get_type_info(prop_type) if prop_type.is_a?(T::Types::Union) - non_nilable_type = T::Utils.unwrap_nilable(prop_type) + non_nilable_type = prop_type.unwrap_nilable if non_nilable_type&.is_a?(T::Types::Simple) non_nilable_type = non_nilable_type.raw_type end @@ -193,9 +186,12 @@ def self.get_type_info(prop_type) # - if the type is A, the function returns A # - if the type is T.nilable(A), the function returns A def self.get_underlying_type(prop_type) - type_info = get_type_info(prop_type) - if type_info.is_union_type - type_info.non_nilable_type || prop_type + if prop_type.is_a?(T::Types::Union) + non_nilable_type = prop_type.unwrap_nilable + if non_nilable_type&.is_a?(T::Types::Simple) + non_nilable_type = non_nilable_type.raw_type + end + non_nilable_type || prop_type elsif prop_type.is_a?(T::Types::Simple) prop_type.raw_type else diff --git a/gems/sorbet-runtime/test/types/builder_syntax.rb b/gems/sorbet-runtime/test/types/builder_syntax.rb index 533bf5c80a..c9243f6779 100644 --- a/gems/sorbet-runtime/test/types/builder_syntax.rb +++ b/gems/sorbet-runtime/test/types/builder_syntax.rb @@ -31,6 +31,20 @@ def self.fn(x); assert_equal(true, builder.decl.finalized) end + it 'requires params not have any positional args' do + ex = assert_raises do + Class.new do + extend T::Sig + sig {params(Integer, s: String).void} + def self.foo(s); end; foo + end + end + assert_includes(ex.message, <<~MSG.chomp) + 'params' was called with some positional arguments, but it needs to be called with keyword arguments. + The keyword arguments' keys must match the name and order of the method's parameters. + MSG + end + it 'requires params be keyword args' do ex = assert_raises do Class.new do @@ -39,7 +53,10 @@ def self.fn(x); def self.foo; end; foo end end - assert_includes(ex.message, "wrong number of arguments (given 1, expected 0)") + assert_includes(ex.message, <<~MSG.chomp) + 'params' was called with only positional arguments, but it needs to be called with keyword arguments. + The keyword arguments' keys must match the name and order of the method's parameters. + MSG end it 'requires params have an arg' do @@ -50,7 +67,12 @@ def self.foo; end; foo def self.foo; end; foo end end - assert_includes(ex.message, "params expects keyword arguments") + assert_includes(ex.message, <<~MSG.chomp) + 'params' was called without any arguments, but it needs to be called with keyword arguments. + The keyword arguments' keys must match the name and order of the method's parameters. + + Omit 'params' entirely for methods with no parameters. + MSG end describe 'modes' do diff --git a/gems/sorbet-runtime/test/types/configuration.rb b/gems/sorbet-runtime/test/types/configuration.rb index 24d7f1f509..c6dcfb36e4 100644 --- a/gems/sorbet-runtime/test/types/configuration.rb +++ b/gems/sorbet-runtime/test/types/configuration.rb @@ -389,5 +389,39 @@ def @mod.foo(a) end end end + + describe 'default_checked_level=' do + before do + @orig_has_read_default_checked_level = T::Private::RuntimeLevels.instance_variable_get(:@has_read_default_checked_level) + @orig_default_checked_level = T::Private::RuntimeLevels.instance_variable_get(:@default_checked_level) + + # Within these specs pretend we haven't yet read this value + T::Private::RuntimeLevels.instance_variable_set(:@has_read_default_checked_level, false) + end + + after do + T::Private::RuntimeLevels.instance_variable_set(:@default_checked_level, @orig_default_checked_level) + T::Private::RuntimeLevels.instance_variable_set(:@has_read_default_checked_level, @orig_has_read_default_checked_level) + end + + it 'fails when given the wrong typed level' do + ex = assert_raises do + T::Configuration.default_checked_level = :foo + end + assert_includes(ex.message, "Invalid `checked` level 'foo'. Use one of: [:always, :tests, :never, :compiled].") + end + + it 'fails when default_checked_level has already been read' do + T::Configuration.default_checked_level = :never + T::Configuration.default_checked_level = :tests + + assert_equal(T::Private::RuntimeLevels.default_checked_level, :tests) + + ex = assert_raises do + T::Configuration.default_checked_level = :never + end + assert_includes(ex.message, "Set the default checked level earlier. There are already some methods whose sig blocks have evaluated which would not be affected by the new default.") + end + end end end diff --git a/gems/sorbet-runtime/test/types/edge_cases.rb b/gems/sorbet-runtime/test/types/edge_cases.rb index aa50b4dc7d..48f8432178 100644 --- a/gems/sorbet-runtime/test/types/edge_cases.rb +++ b/gems/sorbet-runtime/test/types/edge_cases.rb @@ -902,4 +902,13 @@ def a_private_method %i[a_public_method public], ], unique_method_redefinitions.sort) end + + it "can mark a class abstract! even if it defines a method called method" do + Class.new do + extend T::Helpers + # bad override of Object#method + def self.method; end + abstract! + end + end end diff --git a/gems/sorbet-runtime/test/types/method_patches.rb b/gems/sorbet-runtime/test/types/method_patches.rb index 47457d6974..4fc44fd7c0 100644 --- a/gems/sorbet-runtime/test/types/method_patches.rb +++ b/gems/sorbet-runtime/test/types/method_patches.rb @@ -5,7 +5,12 @@ module Opus::Types::Test class MethodPatchesTest < Critic::Unit::UnitTest module MethodRefinement refine Method do - prepend(T::CompatibilityPatches::MethodExtensions) + T::CompatibilityPatches::MethodExtensions.instance_methods(false).each do |method| + define_method( + method, + T::CompatibilityPatches::MethodExtensions.instance_method(method), + ) + end end end diff --git a/gems/sorbet-runtime/test/types/props/_props.rb b/gems/sorbet-runtime/test/types/props/_props.rb index e602717030..1d0f80cfab 100644 --- a/gems/sorbet-runtime/test/types/props/_props.rb +++ b/gems/sorbet-runtime/test/types/props/_props.rb @@ -379,4 +379,36 @@ class TypeValidating end end + describe 'override checking' do + class OverrideProps + include T::Props + prop :a, String + end + + it 'errors if a prop is overriden without override => true' do + error = assert_raises(ArgumentError) do + class OverrideProps1 < OverrideProps + prop :a, Integer + end + end + + assert(error.message.include?("Attempted to redefine prop :a on class Opus::Types::Test::Props::PropsTest::OverrideProps1 that's already defined without specifying :override => true")) + end + + it 'allows overriding with override => true' do + class OverrideProps2 < OverrideProps + prop :a, Integer, override: true + end + end + + it 'errors if a prop has override => true but does not exist' do + error = assert_raises(ArgumentError) do + class OverrideProps3 < OverrideProps + prop :b, Integer, override: true + end + end + + assert(error.message.include?("Attempted to override a prop :b on class Opus::Types::Test::Props::PropsTest::OverrideProps3 that doesn't already exist")) + end + end end diff --git a/gems/sorbet-runtime/test/types/props/constructor.rb b/gems/sorbet-runtime/test/types/props/constructor.rb index 10f03e57ed..a5e0f5ffda 100644 --- a/gems/sorbet-runtime/test/types/props/constructor.rb +++ b/gems/sorbet-runtime/test/types/props/constructor.rb @@ -62,4 +62,87 @@ class UntypedField < T::Struct it 'can default untyped fields' do UntypedField.new end + + class WeakConstructorCustomInitializeStruct + include T::Props + include T::Props::Serializable + include T::Props::WeakConstructor + + prop :name, String + prop :greeting, String, default: "Hi" + prop :farewell, String, default: "Bye" + prop :bool, T::Boolean + prop :color, T.nilable(String) + prop :type, T.nilable(String), raise_on_nil_write: true + + def initialize(hash={}) + @name = 'Doe' + @greeting = nil + @farewell = 'Ciao' + @bool = false + @color = 'red' + @type = 'value' + super + end + end + + it 'does not clobber custom initialize T::Props::WeakConstructor' do + c = WeakConstructorCustomInitializeStruct.new + assert_equal('Doe', c.name) + assert_equal('Hi', c.greeting) + assert_equal('Bye', c.farewell) + assert_equal(false, c.bool) + assert_equal('red', c.color) + assert_equal('value', c.type) + + c = WeakConstructorCustomInitializeStruct.new(name: 'Alex', greeting: 'hello', farewell: 'goodbye', bool: true, color: 'blue', type: 'other') + assert_equal('Alex', c.name) + assert_equal('hello', c.greeting) + assert_equal('goodbye', c.farewell) + assert_equal(true, c.bool) + assert_equal('blue', c.color) + assert_equal('other', c.type) + end + + class CustomInitializeStruct < T::Struct + prop :name, String + prop :greeting, String, default: "Hi" + prop :farewell, String, default: "Bye" + prop :bool, T::Boolean + prop :color, T.nilable(String) + prop :type, T.nilable(String), raise_on_nil_write: true + + def initialize(hash={}) + @name = 'Doe' + @greeting = nil + @farewell = 'Ciao' + @bool = false + @color = 'red' + @type = 'value' + super + end + end + + it 'does not clobber custom initialize for T::Struct' do + c = CustomInitializeStruct.new(name: 'Alex', bool: true, type: 'other') + assert_equal('Alex', c.name) + assert_equal('Hi', c.greeting) + assert_equal('Bye', c.farewell) + assert_equal(true, c.bool) + assert_nil(c.color) + assert_equal('other', c.type) + + c = CustomInitializeStruct.new(name: 'Alex', greeting: 'hello', farewell: 'goodbye', bool: true, color: 'blue', type: 'other') + assert_equal('Alex', c.name) + assert_equal('hello', c.greeting) + assert_equal('goodbye', c.farewell) + assert_equal(true, c.bool) + assert_equal('blue', c.color) + assert_equal('other', c.type) + + err = assert_raises(ArgumentError) do + CustomInitializeStruct.new(bool: true, type: 'other') + end + assert_equal("Missing required prop `name` for class `Opus::Types::Test::Props::ConstructorTest::CustomInitializeStruct`", err.message) + end end diff --git a/gems/sorbet-runtime/test/types/props/private/setter_factory.rb b/gems/sorbet-runtime/test/types/props/private/setter_factory.rb index 061109fef3..28f1f16cbc 100644 --- a/gems/sorbet-runtime/test/types/props/private/setter_factory.rb +++ b/gems/sorbet-runtime/test/types/props/private/setter_factory.rb @@ -6,9 +6,10 @@ class TestSetValidate include T::Props include T::Props::WeakConstructor - prop :validated, T.untyped, setter_validate: ->(_prop, _value) {raise Error.new 'invalid'} + prop :validated, Integer, setter_validate: ->(_prop, _value) {raise Error.new 'invalid'} prop :nilable_validated, T.nilable(Integer), setter_validate: ->(_prop, _value) {raise Error.new 'invalid'} - prop :unvalidated, T.untyped, setter_validate: ->(prop, _value) {raise Error.new 'bad prop' unless prop == :unvalidated} + prop :unvalidated, Integer, setter_validate: ->(prop, _value) {raise Error.new 'bad prop' unless prop == :unvalidated} + prop :untyped, T.untyped, setter_validate: ->(_prop, _value) {raise Error.new 'invalid'} end @@ -43,6 +44,15 @@ class TestSetValidate assert_equal('invalid', ex.message) end + it 'runs on T.untyped' do + obj = TestSetValidate.new + ex = assert_raises {obj.untyped = 5} + assert_equal('invalid', ex.message) + + ex = assert_raises {TestSetValidate.new(untyped: 5)} + assert_equal('invalid', ex.message) + end + it 'does not run when validate_prop_value is called when a nilable is nil' do TestSetValidate.validate_prop_value(:nilable_validated, nil) end diff --git a/gems/sorbet-runtime/test/types/props/serializable.rb b/gems/sorbet-runtime/test/types/props/serializable.rb index 91abde376a..3c30135967 100644 --- a/gems/sorbet-runtime/test/types/props/serializable.rb +++ b/gems/sorbet-runtime/test/types/props/serializable.rb @@ -20,6 +20,25 @@ class MySerializable prop :foo, T.nilable(T::Hash[T.any(String, Symbol), Object]) end + module ExtraProperties + include T::Props::Plugin + module DecoratorMethods + extend T::Sig + + sig {params(instance: T::Props::PrettyPrintable, pp: T.any(PrettyPrint, PP::SingleLine)).void} + def pretty_print_extra(instance, pp) + super(instance, pp) + pp.breakable + pp.text("extra=true") + end + end + end + + class MySerializableWithCustomExtraProps + include T::Props::Serializable + include ExtraProperties + end + def a_serializable m = MySerializable.new m.name = "Bob" @@ -148,20 +167,52 @@ class ChildWithDefault < ParentWithNoDefault it 'inspects' do obj = a_serializable str = obj.inspect - assert_equal('7, "color"=>"red"}, name="Bob">', str) + assert_equal('7, "color"=>"red"} name="Bob">', str) end it 'inspects with extra props' do obj = a_serializable obj = obj.class.from_hash(obj.serialize.merge('not_a_prop' => 'but_here_anyway')) str = obj.inspect - assert_equal('7, "color"=>"red"}, name="Bob" @_extra_props=>', str) + assert_equal('7, "color"=>"red"} name="Bob" @_extra_props=>', str) + end + + it 'inspects with custom extra props' do + obj = MySerializableWithCustomExtraProps.new + obj = obj.class.from_hash(obj.serialize.merge('not_a_prop' => 'but_here_anyway')) + str = obj.inspect + assert_equal(' extra=true>', str) end it 'inspects frozen structs' do obj = a_serializable.freeze str = obj.inspect - assert_equal('7, "color"=>"red"}, name="Bob">', str) + assert_equal('7, "color"=>"red"} name="Bob">', str) + end + end + + describe '.pretty_inspect' do + it 'excludes extra props' do + obj = a_serializable + obj = obj.class.from_hash(obj.serialize.merge('not_a_prop' => 'but_here_anyway')) + expected_result = <<~INSPECT + 7, "color"=>"red"} + name="Bob"> + INSPECT + str = obj.pretty_inspect + assert_equal(expected_result, str) + end + + it 'inspects with custom extra props' do + obj = MySerializableWithCustomExtraProps.new + obj = obj.class.from_hash(obj.serialize.merge('not_a_prop' => 'but_here_anyway')) + expected_result = <<~INSPECT + + INSPECT + str = obj.pretty_inspect + assert_equal(expected_result, str) end end diff --git a/gems/sorbet-runtime/test/types/runtime_levels.rb b/gems/sorbet-runtime/test/types/runtime_levels.rb new file mode 100644 index 0000000000..563f973df0 --- /dev/null +++ b/gems/sorbet-runtime/test/types/runtime_levels.rb @@ -0,0 +1,94 @@ +# frozen_string_literal: true +require_relative '../test_helper' + +module Opus::Types::Test + class RuntimeLevelsTest < Critic::Unit::UnitTest + describe 'set_enable_checking_in_tests_from_environment' do + before do + @orig_wrapped_tests_with_validation = T::Private::RuntimeLevels.instance_variable_get(:@wrapped_tests_with_validation) + @orig_check_tests = T::Private::RuntimeLevels.instance_variable_get(:@check_tests) + @orig_sorbet_runtime_enable_checking_in_tests = ENV['SORBET_RUNTIME_ENABLE_CHECKING_IN_TESTS'] + + # Within these specs pretend we haven't yet read this value and checked_tests is false + T::Private::RuntimeLevels.instance_variable_set(:@wrapped_tests_with_validation, false) + T::Private::RuntimeLevels.instance_variable_set(:@check_tests, false) + end + + after do + T::Private::RuntimeLevels.instance_variable_set(:@check_tests, @orig_check_tests) + T::Private::RuntimeLevels.instance_variable_set(:@wrapped_tests_with_validation, @orig_wrapped_tests_with_validation) + ENV['SORBET_RUNTIME_ENABLE_CHECKING_IN_TESTS'] = @orig_sorbet_runtime_enable_checking_in_tests + end + + describe 'when SORBET_RUNTIME_ENABLE_CHECKING_IN_TESTS env variable is not set' do + before do + ENV['SORBET_RUNTIME_ENABLE_CHECKING_IN_TESTS'] = nil + end + + it 'does not change check_tests' do + # Reaching into a private method for testing purposes + T::Private::RuntimeLevels.send(:set_enable_checking_in_tests_from_environment) + + assert_equal(T::Private::RuntimeLevels.check_tests?, false) + end + end + + describe 'when SORBET_RUNTIME_ENABLE_CHECKING_IN_TESTS env variable is set' do + before do + ENV['SORBET_RUNTIME_ENABLE_CHECKING_IN_TESTS'] = '1' + end + + it 'updates check_tests' do + # Reaching into a private method for testing purposes + T::Private::RuntimeLevels.send(:set_enable_checking_in_tests_from_environment) + + assert_equal(T::Private::RuntimeLevels.check_tests?, true) + end + end + end + + describe 'set_default_checked_level_from_environment' do + before do + @orig_has_read_default_checked_level = T::Private::RuntimeLevels.instance_variable_get(:@has_read_default_checked_level) + @orig_default_checked_level = T::Private::RuntimeLevels.instance_variable_get(:@default_checked_level) + @orig_sorbet_runtime_default_checked_level = ENV['SORBET_RUNTIME_DEFAULT_CHECKED_LEVEL'] + + # Within these specs pretend we haven't yet read this value and default_checked_level is always + T::Private::RuntimeLevels.instance_variable_set(:@has_read_default_checked_level, false) + T::Private::RuntimeLevels.instance_variable_set(:@default_checked_level, :always) + end + + after do + T::Private::RuntimeLevels.instance_variable_set(:@default_checked_level, @orig_default_checked_level) + T::Private::RuntimeLevels.instance_variable_set(:@has_read_default_checked_level, @orig_has_read_default_checked_level) + ENV['SORBET_RUNTIME_DEFAULT_CHECKED_LEVEL'] = @orig_sorbet_runtime_default_checked_level + end + + describe 'when SORBET_RUNTIME_DEFAULT_CHECKED_LEVEL env variable is not set' do + before do + ENV['SORBET_RUNTIME_DEFAULT_CHECKED_LEVEL'] = nil + end + + it 'does not change default_typed_level' do + # Reaching into a private method for testing purposes + T::Private::RuntimeLevels.send(:set_default_checked_level_from_environment) + + assert_equal(T::Private::RuntimeLevels.default_checked_level, :always) + end + end + + describe 'when SORBET_RUNTIME_DEFAULT_CHECKED_LEVEL env variable is set' do + before do + ENV['SORBET_RUNTIME_DEFAULT_CHECKED_LEVEL'] = 'never' + end + + it 'updates default_typed_level' do + # Reaching into a private method for testing purposes + T::Private::RuntimeLevels.send(:set_default_checked_level_from_environment) + + assert_equal(T::Private::RuntimeLevels.default_checked_level, :never) + end + end + end + end +end diff --git a/gems/sorbet-runtime/test/types/struct.rb b/gems/sorbet-runtime/test/types/struct.rb index 2db3fa8a17..1d0e210dd4 100644 --- a/gems/sorbet-runtime/test/types/struct.rb +++ b/gems/sorbet-runtime/test/types/struct.rb @@ -76,4 +76,71 @@ class Opus::Types::Test::StructValidationTest < Critic::Unit::UnitTest end end end + + class NestedStruct < T::Struct + const :data, T::Hash[Symbol, String] + const :sensitive, T.nilable(String), sensitivity: ['reason'] + const :custom, T.nilable(String), inspect: proc {|value, opts| "\"Inspected '#{value}' (opts: #{opts})\"" unless value.nil?} + const :nested, T.nilable(NestedStruct) + end + + module DecoratedClassName + include T::Props::Plugin + module DecoratorMethods + extend T::Sig + sig {params(instance: T::Props::PrettyPrintable).returns(String)} + def inspect_class_with_decoration(instance) + "#{instance.class}[decorated]" + end + end + end + + class StructWithDecoratedName < T::Struct + include DecoratedClassName + const :data, String + end + + describe "inspection" do + def make_nested_struct + NestedStruct.new( + data: { + one: "one", + two: "two", + }, + custom: "something", + nested: NestedStruct.new(data: {three: "three"}, sensitive: "something sensitive") + ) + end + + it "inspects in a single line" do + struct = make_nested_struct + expected_result = "false})\" data={:one=>\"one\", :two=>\"two\"} " \ + "nested=\"three\"} " \ + "nested=nil sensitive=> sensitive=nil>" + assert_equal(expected_result, struct.inspect) + end + + it "pretty inspects" do + struct = make_nested_struct + expected_result = <<~INSPECT + "one", :two=>"two"} + nested="three"} + nested=nil + sensitive=> + sensitive=nil> + INSPECT + assert_equal(expected_result, struct.pretty_inspect) + end + + it "supports decorating the class name" do + struct = StructWithDecoratedName.new(data: 'test') + expected_result = "" + assert_equal(expected_result, struct.inspect) + end + end end diff --git a/gems/sorbet-runtime/test/types/types.rb b/gems/sorbet-runtime/test/types/types.rb index feb84d25a5..88c1dab2cf 100644 --- a/gems/sorbet-runtime/test/types/types.rb +++ b/gems/sorbet-runtime/test/types/types.rb @@ -682,7 +682,7 @@ def each; end end - describe "TypedEnumerator" do + describe "TypedEnumeratorLazy" do it 'describes enumerators' do t = T::Enumerator::Lazy[Integer] assert_equal( @@ -704,6 +704,28 @@ def each; end end + describe "TypedEnumeratorChain" do + it 'describes enumerators' do + t = T::Enumerator::Chain[Integer] + assert_equal( + "T::Enumerator::Chain[Integer]", + t.describe_obj([1, 2].chain([3]))) + end + + it 'works if the type is right' do + type = T::Enumerator::Chain[Integer] + value = [1, 2].chain([3]) + msg = check_error_message_for_obj(type, value) + assert_nil(msg) + end + + it 'can have its metatype instantiated' do + assert_equal([2, 4, 6], T::Enumerator::Chain[Integer].new([1, 2], [3]).map do |value| + value * 2 + end.to_a) + end + end + describe "TypedRange" do it 'describes ranges' do t = T::Range[Integer] @@ -918,6 +940,13 @@ def each; assert_nil(msg) end + it 'does not check chain enumerables (for now)' do + type = T::Enumerable[Integer] + value = ["bad"].chain([]) + msg = check_error_message_for_obj(type, value) + assert_nil(msg) + end + it 'does not check potentially non-finite enumerables' do type = T::Enumerable[Integer] value = ["bad"].cycle @@ -948,6 +977,53 @@ def each end end + describe "TypedClass" do + it 'works if the type is right' do + type = T::Class[Base] + value = Base + msg = check_error_message_for_obj(type, value) + assert_nil(msg) + end + + it 'works if the type is wrong, but a class' do + type = T::Class[Sub] + value = Base + msg = check_error_message_for_obj(type, value) + assert_nil(msg) + end + + it 'cannot have its metatype instantiated' do + # People might assume that this creates a class with a supertype of + # `Base`. It doesn't, because generics are erased, and also `[...]` can + # hold an arbitrary type, not necessarily a class type. + # + # Also, `Class.new` already has a sig that infers a _better_ type: + # Class.new(Base) has an inferred type of `T.class_of(Base)`, which is + # more narrow. + assert_raises(NoMethodError) do + T::Class[Base].new + end + end + + it 'is not coerced from plain class literal' do + # This is for backwards compatibility. If this poses problems for the + # sake of runtime checking and reflection, we may want to make this + # behavior more like the static system, where `::Class` has type + # `T.class_of(Class)`. It looks like we already don't treat `::A` as + # coercing to `T.class_of(A)`, which is why I don't know whether it + # particularly matters. + # + # (It's also worth noting: Sorbet doesn't have a separate notion of + # `T::Types::Simple` and `T::Types::ClassOf`. A ClassType is used to + # model `T::Types::Simple` and _was_ used to model `T.class_of(...)` + # until we made all singleton classes generic with ``, + # when they became AppliedType.) + type = T::Utils.coerce(::Class) + assert_instance_of(T::Types::Simple, type) + assert_equal(::Class, type.raw_type) + end + end + describe 'TypeAlias' do it 'delegates name' do type = T.type_alias {T.any(Integer, String)} @@ -1448,6 +1524,31 @@ def refute_subtype(lhs, rhs) refute_subtype([String, Numeric], [String, Integer]) refute_subtype([String], [String, Object]) end + + it 'compares upwards to TypedArray' do + assert_subtype([], T::Array[Integer]) + assert_subtype([], T::Array[String]) + assert_subtype([Integer], T::Array[Integer]) + assert_subtype([Integer, String], T::Array[T.any(Integer, String)]) + + refute_subtype([Integer], T::Array[String]) + refute_subtype([Integer, String], T::Array[Integer]) + end + end + + describe 'shapes' do + it 'compares upwards to TypedHash' do + assert_subtype({}, T::Hash[Integer, String]) + assert_subtype({}, T::Hash[String, Symbol]) + assert_subtype({key: Integer}, T::Hash[Symbol, Integer]) + assert_subtype({'key' => Integer}, T::Hash[String, Integer]) + assert_subtype({key: Integer, 'another' => Float}, T::Hash[T.any(Symbol, String), T.any(Integer, Float)]) + + refute_subtype({key: Integer}, T::Hash[String, Integer]) + refute_subtype({key: Integer}, T::Hash[Symbol, Float]) + refute_subtype({key: Integer, 'another' => Float}, T::Hash[Symbol, T.any(Integer, Float)]) + refute_subtype({key: Integer, 'another' => Float}, T::Hash[T.any(Symbol, String), Integer]) + end end describe 'untyped' do @@ -1466,6 +1567,47 @@ def refute_subtype(lhs, rhs) end end + describe 'noreturn' do + it 'is a subtype of things' do + assert_subtype(T.noreturn, Integer) + assert_subtype(T.noreturn, Numeric) + assert_subtype(T.noreturn, [String, String]) + assert_subtype(T.noreturn, T::Array[Integer]) + assert_subtype(T.noreturn, T.untyped) + end + + it 'other things are not a subtype of it' do + refute_subtype(Integer, T.noreturn) + refute_subtype(Numeric, T.noreturn) + refute_subtype([String, String], T.noreturn) + refute_subtype(T::Array[Integer], T.noreturn) + + # except this one + assert_subtype(T.untyped, T.noreturn) + end + end + + describe 'anything' do + it 'is not a subtype of things' do + refute_subtype(T.anything, Integer) + refute_subtype(T.anything, Numeric) + refute_subtype(T.anything, [String, String]) + refute_subtype(T.anything, T::Array[Integer]) + + # except this one + assert_subtype(T.anything, T.untyped) + end + + it 'other things are a subtype of it' do + assert_subtype(Integer, T.anything) + assert_subtype(Numeric, T.anything) + assert_subtype([String, String], T.anything) + assert_subtype(T::Array[Integer], T.anything) + + assert_subtype(T.untyped, T.anything) + end + end + describe 'type variables' do it 'type members are subtypes of everything' do assert_subtype(T::Types::TypeMember.new(:in), T.untyped) @@ -1474,12 +1616,25 @@ def refute_subtype(lhs, rhs) T::Types::TypeMember.new(:out)) end + it 'everything is a subtype of type members' do + assert_subtype(T.untyped, T::Types::TypeMember.new(:in)) + assert_subtype(String, T::Types::TypeMember.new(:in)) + assert_subtype(T::Types::TypeMember.new(:out), + T::Types::TypeMember.new(:in)) + end + it 'type parameters are subtypes of everything' do assert_subtype(T::Types::TypeParameter.new(:T), T.untyped) assert_subtype(T::Types::TypeParameter.new(:T), String) assert_subtype(T::Types::TypeParameter.new(:T), T::Types::TypeParameter.new(:V)) end + + it 'pools' do + assert_equal(T.type_parameter(:T).object_id, T.type_parameter(:T).object_id) + refute_equal(T.type_parameter(:T).object_id, T.type_parameter(:U).object_id) + refute_equal(T::Types::TypeParameter.new(:T).object_id, T::Types::TypeParameter.new(:T).object_id) + end end describe 'untyped containers' do diff --git a/gems/sorbet-runtime/test/types/utils.rb b/gems/sorbet-runtime/test/types/utils.rb index 3f5f56a0ac..615110da3e 100644 --- a/gems/sorbet-runtime/test/types/utils.rb +++ b/gems/sorbet-runtime/test/types/utils.rb @@ -11,6 +11,11 @@ class UtilsTest < Critic::Unit::UnitTest assert(T.any(String, Float).subtype_of?(unwrapped)) assert(unwrapped.subtype_of?(T.any(String, Float))) end + + it 'unwraps with a simple pair' do + type = T.any(String, Float) + assert_nil(T::Utils.unwrap_nilable(type)) + end end describe 'T::Utils.signature_for_method' do diff --git a/gems/sorbet-runtime/test/types/validate_override_shape.rb b/gems/sorbet-runtime/test/types/validate_override_shape.rb index c127568b36..f9e5d39087 100644 --- a/gems/sorbet-runtime/test/types/validate_override_shape.rb +++ b/gems/sorbet-runtime/test/types/validate_override_shape.rb @@ -3,6 +3,16 @@ module Opus::Types::Test class ValidateOverrideShapeTest < Critic::Unit::UnitTest + class AbstractFoo + extend T::Sig + extend T::Helpers + + abstract! + + sig {abstract.returns(Integer)} + def foo; end + end + class Base extend T::Sig sig do @@ -13,6 +23,12 @@ class Base def foo(req, opt=nil, kwreq:, kwopt: nil, &blk); end end + class AbstractBase + extend T::Sig + sig {abstract.void} + def initialize; end + end + it "succeeds if the override matches the shape" do klass = Class.new(Base) do extend T::Sig @@ -26,6 +42,26 @@ def foo(req, opt=nil, kwreq:, kwopt: nil, &blk); end klass.new.foo(1, kwreq: 3) {} end + it "succeeds specifically for abstract/abstract" do + klass = Class.new(AbstractFoo) do + extend T::Sig + extend T::Helpers + abstract! + + sig {abstract.returns(Integer)} + def foo; end + end + another = Class.new(klass) do + extend T::Sig + + sig {override.returns(Integer)} + def foo + 0 + end + end + assert_equal(0, another.new.foo) + end + it "succeeds if the override has additional optional args and kwargs" do klass = Class.new(Base) do extend T::Sig @@ -178,5 +214,34 @@ def foo; end end klass.new.foo end + + it "does opt-in override checking on initialize" do + klass = Class.new(AbstractBase) do + extend T::Sig + sig {override.void} + def initialize; end + + def foo + 0 + end + end + assert_equal(0, klass.new.foo) + end + + it "raises if initialize is not compatible with parent" do + klass = Class.new(AbstractBase) do + extend T::Sig + sig do + override + .params(x: Integer) + .void + end + def initialize(x); end + end + err = assert_raises(RuntimeError) do + klass.new(0) + end + assert_includes(err.message, "must have no more than 0 required argument(s) to be compatible") + end end end diff --git a/gems/sorbet-runtime/test/types/validate_override_types.rb b/gems/sorbet-runtime/test/types/validate_override_types.rb index 6be3546f78..1c2d81a6f0 100644 --- a/gems/sorbet-runtime/test/types/validate_override_types.rb +++ b/gems/sorbet-runtime/test/types/validate_override_types.rb @@ -108,5 +108,53 @@ def foo(pos, kw:); end assert_includes(err.message, "Incompatible return type in signature for override of method `foo`") end + it "allows T::Class to be compatible with itself" do + parent = Class.new do + extend T::Sig + sig {overridable.returns(T::Class[T.anything])} + def example; Object; end + end + + child = Class.new(parent) do + extend T::Sig + sig {override.returns(T::Class[T.anything])} + def example; Object; end + end + + child.new.example + end + + it "allows T::Class to be compatible with T.class_of in child" do + parent = Class.new do + extend T::Sig + sig {overridable.returns(T::Class[T.anything])} + def example; Object; end + end + + child = Class.new(parent) do + extend T::Sig + sig {override.returns(T.class_of(Object))} + def example; Object; end + end + + child.new.example + end + + it "allows Object to be compatible with T::Hash in child" do + parent = Class.new do + extend T::Sig + sig {overridable.returns(Object)} + def example; nil; end + end + + child = Class.new(parent) do + extend T::Sig + sig {override.returns(T::Hash[Symbol, T.untyped])} + def example; {}; end + end + + child.new.example + end + end end diff --git a/gems/sorbet-runtime/test/wholesome/Gemfile b/gems/sorbet-runtime/test/wholesome/Gemfile new file mode 100644 index 0000000000..6c74fe61d1 --- /dev/null +++ b/gems/sorbet-runtime/test/wholesome/Gemfile @@ -0,0 +1,7 @@ +source 'https://rubygems.org' + +gem 'minitest' +gem 'mocha' +gem 'oj' +gem 'rake' +gem 'sorbet-runtime', path: '../../' diff --git a/gems/sorbet-runtime/test/wholesome/Gemfile.lock b/gems/sorbet-runtime/test/wholesome/Gemfile.lock new file mode 100644 index 0000000000..27e5692fa9 --- /dev/null +++ b/gems/sorbet-runtime/test/wholesome/Gemfile.lock @@ -0,0 +1,27 @@ +PATH + remote: ../.. + specs: + sorbet-runtime (0.0.0) + +GEM + remote: https://rubygems.org/ + specs: + minitest (5.17.0) + mocha (2.0.2) + ruby2_keywords (>= 0.0.5) + oj (3.14.2) + rake (13.0.6) + ruby2_keywords (0.0.5) + +PLATFORMS + x86_64-linux + +DEPENDENCIES + minitest + mocha + oj + rake + sorbet-runtime! + +BUNDLED WITH + 2.4.1 diff --git a/gems/sorbet-runtime/test/wholesome/Rakefile b/gems/sorbet-runtime/test/wholesome/Rakefile new file mode 100644 index 0000000000..f8c6dc7150 --- /dev/null +++ b/gems/sorbet-runtime/test/wholesome/Rakefile @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +task default: %i[test] + +def require_tests + Dir.glob('./**/*.test.rb').sort.each(&method(:require)) +end + +task :test do + require_tests +end diff --git a/gems/sorbet-runtime/test/wholesome/tenum_oj.test.rb b/gems/sorbet-runtime/test/wholesome/tenum_oj.test.rb new file mode 100644 index 0000000000..6c7d7aef70 --- /dev/null +++ b/gems/sorbet-runtime/test/wholesome/tenum_oj.test.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'minitest/autorun' +require 'minitest/spec' +require 'mocha/minitest' + +require 'sorbet-runtime' +require 'oj' + +module Opus + module Types + module Test + module Wholesome; end + end + end +end + +class Opus::Types::Test::Wholesome::TEnumOj < MiniTest::Spec + class MyEnum < T::Enum + enums do + X = new + end + end + + def assert_equal(exp, act, msg=nil) + msg = message(msg, "") {diff exp, act} + assert(exp.eql?(act), msg) + end + + it 'works on a class' do + serialized = Oj.dump(MyEnum::X) + x_deser = Oj.load(serialized) + + assert_equal(MyEnum::X, x_deser) + end +end diff --git a/gems/sorbet-runtime/tools/generate_call_validation.cc b/gems/sorbet-runtime/tools/generate_call_validation.cc index 04b7342c1d..f0fa935f7f 100644 --- a/gems/sorbet-runtime/tools/generate_call_validation.cc +++ b/gems/sorbet-runtime/tools/generate_call_validation.cc @@ -94,12 +94,10 @@ void generateCreateValidatorFastDispatcher(ValidatorKind kind, TypeKind type) { } fmt::print(" # trampoline to reduce stack frame size\n"); + fmt::print(" arg_types = method_sig.arg_types\n"); + fmt::print(" case arg_types.length\n"); for (size_t arity = 0; arity <= MAX_ARITY; arity++) { - if (arity == 0) { - fmt::print(" if method_sig.arg_types.empty?\n"); - } else { - fmt::print(" elsif method_sig.arg_types.length == {}\n", arity); - } + fmt::print(" when {}\n", arity); fmt::print(" create_validator_{}_{}{}(mod, original_method, method_sig, original_visibility", kindString, typeString, arity); @@ -110,7 +108,7 @@ void generateCreateValidatorFastDispatcher(ValidatorKind kind, TypeKind type) { for (size_t i = 0; i < arity; i++) { fmt::print(",\n"); - fmt::print(" method_sig.arg_types[{}][1]{}", i, rawTypeMethodCall); + fmt::print(" arg_types[{}][1]{}", i, rawTypeMethodCall); } fmt::print(")\n"); } diff --git a/gems/sorbet/lib/create-config.rb b/gems/sorbet/lib/create-config.rb index ba12240d54..f3382c0977 100755 --- a/gems/sorbet/lib/create-config.rb +++ b/gems/sorbet/lib/create-config.rb @@ -24,6 +24,7 @@ def self.main File.open(SORBET_CONFIG_FILE, 'w') do |f| f.puts('--dir') f.puts('.') + f.puts('--ignore=/tmp/') f.puts('--ignore=/vendor/bundle') end end diff --git a/gems/sorbet/lib/t.rb b/gems/sorbet/lib/t.rb index 68f558de13..9e61496aae 100644 --- a/gems/sorbet/lib/t.rb +++ b/gems/sorbet/lib/t.rb @@ -48,6 +48,18 @@ module Enumerable def self.[](type); end end + module Enumerator + def self.[](type); end + + module Lazy + def self.[](type); end + end + + module Chain + def self.[](type); end + end + end + module Range def self.[](type); end end diff --git a/hashing/hashing.cc b/hashing/hashing.cc index 35f867d409..9e30198528 100644 --- a/hashing/hashing.cc +++ b/hashing/hashing.cc @@ -90,7 +90,6 @@ core::FileRef makeEmptyGlobalStateForFile(spdlog::logger &logger, shared_ptrrequiresAncestorEnabled = hashingOpts.requiresAncestorEnabled; lgs->ruby3KeywordArgs = hashingOpts.ruby3KeywordArgs; - lgs->lspExperimentalFastPathEnabled = hashingOpts.lspExperimentalFastPathEnabled; { core::UnfreezeFileTable fileTableAccess(*lgs); auto fref = lgs->enterFile(forWhat); diff --git a/infer/BUILD b/infer/BUILD index 6ff8273213..fe67803523 100644 --- a/infer/BUILD +++ b/infer/BUILD @@ -45,7 +45,7 @@ cc_test( "//namer", "//resolver", "//rewriter", - "@doctest", - "@doctest//:doctest_main", + "@doctest//doctest", + "@doctest//doctest:main", ], ) diff --git a/infer/SigSuggestion.cc b/infer/SigSuggestion.cc index 9c2ffa1aaa..86b98c32f0 100644 --- a/infer/SigSuggestion.cc +++ b/infer/SigSuggestion.cc @@ -2,6 +2,7 @@ #include "common/common.h" #include "core/Loc.h" #include "core/TypeConstraint.h" +#include "core/TypeErrorDiagnostics.h" #include "core/lsp/QueryResponse.h" #include @@ -11,51 +12,6 @@ namespace sorbet::infer { namespace { -bool extendsTSig(core::Context ctx, core::ClassOrModuleRef enclosingClass) { - ENFORCE(enclosingClass.exists()); - auto enclosingSingletonClass = enclosingClass.data(ctx)->lookupSingletonClass(ctx); - ENFORCE(enclosingSingletonClass.exists()); - return enclosingSingletonClass.data(ctx)->derivesFrom(ctx, core::Symbols::T_Sig()); -} - -optional maybeSuggestExtendTSig(core::Context ctx, core::MethodRef methodSymbol) { - auto method = methodSymbol.data(ctx); - - auto enclosingClass = methodSymbol.enclosingClass(ctx).data(ctx)->topAttachedClass(ctx); - if (extendsTSig(ctx, enclosingClass)) { - // No need to suggest here, because it already has 'extend T::Sig' - return nullopt; - } - - auto inFileOfMethod = [&](const auto &loc) { return loc.file() == method->loc().file(); }; - auto &classLocs = enclosingClass.data(ctx)->locs(); - auto classLoc = absl::c_find_if(classLocs, inFileOfMethod); - - if (classLoc == classLocs.end()) { - // Couldn't a loc for the enclosing class in this file, give up. - // An alternative heuristic here might be "found a file that we can write to" - return nullopt; - } - - auto [classStart, classEnd] = classLoc->position(ctx); - - core::Loc::Detail thisLineStart = {classStart.line, 1}; - auto thisLineLoc = core::Loc::fromDetails(ctx, classLoc->file(), thisLineStart, thisLineStart); - ENFORCE(thisLineLoc.has_value()); - auto [_, thisLinePadding] = thisLineLoc.value().findStartOfLine(ctx); - - core::Loc::Detail nextLineStart = {classStart.line + 1, 1}; - auto nextLineLoc = core::Loc::fromDetails(ctx, classLoc->file(), nextLineStart, nextLineStart); - if (!nextLineLoc.has_value()) { - return nullopt; - } - auto [replacementLoc, nextLinePadding] = nextLineLoc.value().findStartOfLine(ctx); - - // Preserve the indentation of the line below us. - string prefix(max(thisLinePadding + 2, nextLinePadding), ' '); - return core::AutocorrectSuggestion::Edit{nextLineLoc.value(), fmt::format("{}extend T::Sig\n", prefix)}; -} - core::TypePtr extractArgType(core::Context ctx, cfg::Send &send, core::DispatchComponent &component, optional keyword, int argId) { ENFORCE(component.method.exists()); @@ -474,7 +430,11 @@ optional SigSuggestion::maybeSuggestSig(core::Conte } fmt::format_to(std::back_inserter(ss), ")."); } - if (!guessedSomethingUseful) { + if (!guessedSomethingUseful && !ctx.state.suggestUnsafe.has_value()) { + // We don't want to condition people to start inserting a bunch of useless signatures filled + // with `T.untyped`, unless they've explicitly opted into the behavior with + // `--suggest-unsafe` (which usually suggests that they're doing some sort of codemod and + // they know what they're asking for). return nullopt; } @@ -485,7 +445,8 @@ optional SigSuggestion::maybeSuggestSig(core::Conte if (suggestsVoid) { fmt::format_to(std::back_inserter(ss), "void }}"); } else { - fmt::format_to(std::back_inserter(ss), "returns({}) }}", guessedReturnType.show(ctx)); + auto options = core::ShowOptions().withShowForRBI(); + fmt::format_to(std::back_inserter(ss), "returns({}) }}", guessedReturnType.show(ctx, options)); } auto [replacementLoc, padding] = loc.findStartOfLine(ctx); @@ -511,8 +472,15 @@ optional SigSuggestion::maybeSuggestSig(core::Conte ctx, core::lsp::EditResponse(replacementLoc, std::move(replacementContents))); } - if (auto edit = maybeSuggestExtendTSig(ctx, methodSymbol)) { - edits.emplace_back(edit.value()); + auto topAttachedClass = enclosingClass.data(ctx)->topAttachedClass(ctx); + if (auto edit = core::TypeErrorDiagnostics::editForDSLMethod(ctx, ctx.file, replacementLoc, topAttachedClass, + core::Symbols::T_Sig(), "")) { + if (edit->loc == edits.back().loc) { + // Merge edits if we need to insert the `extend T::Sig` at the same point as the `sig`, + edits.back().replacement = edit->replacement + edits.back().replacement; + } else { + edits.emplace_back(edit.value()); + } } return core::AutocorrectSuggestion{fmt::format("Add `{}`", sig), edits}; diff --git a/infer/environment.cc b/infer/environment.cc index 6939f5683d..87ee8c2b79 100644 --- a/infer/environment.cc +++ b/infer/environment.cc @@ -1,7 +1,7 @@ #include "environment.h" #include "absl/strings/match.h" -#include "common/formatting.h" -#include "common/sort.h" +#include "common/sort/sort.h" +#include "common/strings/formatting.h" #include "common/typecase.h" #include "core/GlobalState.h" #include "core/TypeConstraint.h" @@ -530,7 +530,10 @@ void Environment::updateKnowledge(core::Context ctx, cfg::LocalRef local, core:: whoKnows.falsy().addNoTypeTest(local, typeTestsWithVar, send->recv.variable, core::Types::falsyTypes()); whoKnows.sanityCheck(); - } else if (send->fun == core::Names::nil_p()) { + return; + } + + if (send->fun == core::Names::nil_p()) { if (!knowledgeFilter.isNeeded(local)) { return; } @@ -538,7 +541,10 @@ void Environment::updateKnowledge(core::Context ctx, cfg::LocalRef local, core:: whoKnows.truthy().addYesTypeTest(local, typeTestsWithVar, send->recv.variable, core::Types::nilClass()); whoKnows.falsy().addNoTypeTest(local, typeTestsWithVar, send->recv.variable, core::Types::nilClass()); whoKnows.sanityCheck(); - } else if (send->fun == core::Names::blank_p()) { + return; + } + + if (send->fun == core::Names::blank_p()) { if (!knowledgeFilter.isNeeded(local)) { return; } @@ -552,7 +558,10 @@ void Environment::updateKnowledge(core::Context ctx, cfg::LocalRef local, core:: whoKnows.falsy().addYesTypeTest(local, typeTestsWithVar, send->recv.variable, knowledgeTypeWithoutFalsy); whoKnows.sanityCheck(); } - } else if (send->fun == core::Names::present_p()) { + return; + } + + if (send->fun == core::Names::present_p()) { if (!knowledgeFilter.isNeeded(local)) { return; } @@ -566,11 +575,13 @@ void Environment::updateKnowledge(core::Context ctx, cfg::LocalRef local, core:: whoKnows.truthy().addYesTypeTest(local, typeTestsWithVar, send->recv.variable, knowledgeTypeWithoutFalsy); whoKnows.sanityCheck(); } + return; } if (send->args.empty()) { return; } + // TODO(jez) We should probably update this to be aware of T::NonForcingConstants.non_forcing_is_a? if (send->fun == core::Names::kindOf_p() || send->fun == core::Names::isA_p()) { if (!knowledgeFilter.isNeeded(local)) { @@ -587,8 +598,10 @@ void Environment::updateKnowledge(core::Context ctx, cfg::LocalRef local, core:: } whoKnows.sanityCheck(); } - } else if (send->fun == core::Names::eqeq() || send->fun == core::Names::equal_p() || - send->fun == core::Names::neq()) { + return; + } + + if (send->fun == core::Names::eqeq() || send->fun == core::Names::equal_p() || send->fun == core::Names::neq()) { if (!knowledgeFilter.isNeeded(local)) { return; } @@ -635,7 +648,10 @@ void Environment::updateKnowledge(core::Context ctx, cfg::LocalRef local, core:: } whoKnows.sanityCheck(); - } else if (send->fun == core::Names::tripleEq()) { + return; + } + + if (send->fun == core::Names::tripleEq()) { if (!knowledgeFilter.isNeeded(local)) { return; } @@ -663,8 +679,10 @@ void Environment::updateKnowledge(core::Context ctx, cfg::LocalRef local, core:: } } whoKnows.sanityCheck(); + return; + } - } else if (send->fun == core::Names::lessThan() || send->fun == core::Names::leq()) { + if (send->fun == core::Names::lessThan() || send->fun == core::Names::leq()) { const auto &recvKlass = send->recv.type; const auto &argType = send->args[0].type; @@ -691,6 +709,7 @@ void Environment::updateKnowledge(core::Context ctx, cfg::LocalRef local, core:: whoKnows.falsy().addNoTypeTest(local, typeTestsWithVar, send->recv.variable, argType); } whoKnows.sanityCheck(); + return; } } @@ -918,7 +937,10 @@ core::TypePtr flatmapHack(core::Context ctx, const core::TypePtr &receiver, cons return returnType; } - if (!receiver.isUntyped() && receiver.derivesFrom(ctx, core::Symbols::Enumerator_Lazy())) { + if (!receiver.isUntyped() && (receiver.derivesFrom(ctx, core::Symbols::Enumerator_Lazy()) || + receiver.derivesFrom(ctx, core::Symbols::Enumerator_Chain())) + + ) { return returnType; } @@ -994,7 +1016,6 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind auto dispatched = recvType.type.dispatchCall(ctx, dispatchArgs); auto it = &dispatched; - auto multipleComponents = it->secondary != nullptr; while (it != nullptr) { for (auto &err : it->main.errors) { if (err->what != core::errors::Infer::UnknownMethod || @@ -1060,31 +1081,6 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind } } - // Sometimes we hit a method here where the method symbol is Symbols::noSymbol(). - // - // The primary cases for that is: - // - When the receiver is untyped - // - When the receiver is a void type - // - Calling super - // - Calling initialize on an object that doesn't define initialize - // - When a method doesn't exist. - // - // In all of these cases, we bail out and skip the non-private checking. - if (it->main.method.exists() && it->main.method.data(ctx)->flags.isPrivate && !send.isPrivateOk) { - if (auto e = ctx.beginError(bind.loc, core::errors::Infer::PrivateMethod)) { - if (multipleComponents) { - e.setHeader("Non-private call to private method `{}` on `{}` component of `{}`", - it->main.method.data(ctx)->name.show(ctx), it->main.receiver.show(ctx), - recvType.type.show(ctx)); - } else { - e.setHeader("Non-private call to private method `{}` on `{}`", - it->main.method.data(ctx)->name.show(ctx), it->main.receiver.show(ctx)); - } - e.addErrorLine(it->main.method.data(ctx)->loc(), "Defined in `{}` here", - it->main.method.data(ctx)->owner.show(ctx)); - } - } - if (it->main.method.exists() && it->main.method.data(ctx)->flags.isPackagePrivate) { core::ClassOrModuleRef klass = it->main.method.data(ctx)->owner; if (klass.exists()) { @@ -1117,6 +1113,17 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind if (send.link || lspQueryMatch) { retainedResult = make_shared(std::move(dispatched)); } + if (send.link) { + // This should eventually become ENFORCEs but currently they are wrong + if (!retainedResult->main.blockReturnType) { + retainedResult->main.blockReturnType = core::Types::untyped(ctx, retainedResult->main.method); + } + if (!retainedResult->main.blockPreType) { + retainedResult->main.blockPreType = core::Types::untyped(ctx, retainedResult->main.method); + } + ENFORCE(retainedResult->main.sendTp); + } + // For `case x; when X ...`, desugar produces `X.===(x)`, but with // a zero-length funLoc. We tried producing a zero-length loc for // the entire send so there would never be a match here, but that @@ -1126,7 +1133,12 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind // by desugar and redacting the `SendResponse` so LSP features work // more like developers expect. const bool isDesugarTripleEqSend = send.fun == core::Names::tripleEq() && send.funLoc.empty(); - if (lspQueryMatch && !isDesugarTripleEqSend) { + // Something like `X = Foo` will be rewritten to `X = Magic.(Foo)` if `Foo` fails + // to resolve. We don't want to report a send response for the call here. + const bool isSuggestConstantType = send.fun == core::Names::suggestConstantType(); + + const bool ignoreSendForLSPQuery = isDesugarTripleEqSend || isSuggestConstantType; + if (lspQueryMatch && !ignoreSendForLSPQuery) { auto fun = send.fun; if (fun == core::Names::checkAndAnd() && core::isa_type(args[1]->type)) { auto lit = core::cast_type_nonnull(args[2]->type); @@ -1135,20 +1147,11 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind } } core::lsp::QueryResponse::pushQueryResponse( - ctx, core::lsp::SendResponse(ctx.locAt(bind.loc), retainedResult, fun, send.isPrivateOk, - ctx.owner.asMethodRef(), ctx.locAt(send.receiverLoc), - ctx.locAt(send.funLoc), send.args.size())); + ctx, + core::lsp::SendResponse(retainedResult, send.argLocs, fun, ctx.owner.asMethodRef(), + send.isPrivateOk, ctx.file, bind.loc, send.receiverLoc, send.funLoc)); } if (send.link) { - // This should eventually become ENFORCEs but currently they are wrong - if (!retainedResult->main.blockReturnType) { - retainedResult->main.blockReturnType = core::Types::untyped(ctx, retainedResult->main.method); - } - if (!retainedResult->main.blockPreType) { - retainedResult->main.blockPreType = core::Types::untyped(ctx, retainedResult->main.method); - } - ENFORCE(retainedResult->main.sendTp); - send.link->result = move(retainedResult); } if (send.fun == core::Names::toHashDup()) { @@ -1163,9 +1166,9 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind tp.origins = typeAndOrigin.origins; if (lspQueryMatch && !bind.value.isSynthetic()) { - core::lsp::QueryResponse::pushQueryResponse(ctx, core::lsp::IdentResponse(ctx.locAt(bind.loc), - i.what.data(inWhat), tp, - ctx.owner.asMethodRef())); + core::lsp::QueryResponse::pushQueryResponse( + ctx, core::lsp::IdentResponse(ctx.locAt(bind.loc), i.what.data(inWhat), tp, + ctx.owner.asMethodRef(), ctx.locAt(inWhat.loc))); } ENFORCE(ctx.file.data(ctx).hasParseErrors() || !tp.origins.empty(), @@ -1272,9 +1275,16 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind */ ENFORCE(ctx.owner == i.method); - auto argType = i.argument(ctx).argumentTypeAsSeenByImplementation(ctx, constr); + const auto &argInfo = i.argument(ctx); + auto argType = argInfo.argumentTypeAsSeenByImplementation(ctx, constr); tp.type = std::move(argType); tp.origins.emplace_back(ctx.locAt(bind.loc)); + + if (lspQuery.matchesLoc(argInfo.loc)) { + core::lsp::QueryResponse::pushQueryResponse( + ctx, core::lsp::IdentResponse(argInfo.loc, bind.bind.variable.data(inWhat), tp, + ctx.owner.asMethodRef(), ctx.locAt(inWhat.loc))); + } }, [&](cfg::ArgPresent &i) { // Return an unanalyzable boolean value that indicates whether or not arg was provided @@ -1359,40 +1369,51 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind // Fetch the type for the argument out of the parameters for the block // by simulating a blockParam[i] call. const core::TypeAndOrigins &recvType = getAndFillTypeAndOrigin(ctx, i.yieldParam); - core::TypePtr argType = core::make_type((int64_t)i.argId); - - core::TypeAndOrigins arg{argType, recvType.origins}; - InlinedVector args; - args.emplace_back(&arg); - InlinedVector argLocs; - argLocs.emplace_back(bind.loc); - core::CallLocs locs{ - ctx.file, bind.loc, bind.loc, bind.loc, argLocs, - }; - - const auto numPosArgs = 1; - const auto suppressErrors = true; - const auto isPrivateOk = true; - const std::shared_ptr block = nullptr; - core::DispatchArgs dispatchArgs{core::Names::squareBrackets(), - locs, - numPosArgs, - args, - recvType.type, - recvType, - recvType.type, - block, - ctx.locAt(bind.loc), - isPrivateOk, - suppressErrors}; - auto dispatched = recvType.type.dispatchCall(ctx, dispatchArgs); - tp.type = dispatched.returnType; + if (recvType.type.isUntyped()) { + // This avoids reporting an untyped usage for ->(x) { 0 }. Sorbet would + // initialize the type of the local `x` by calling .[](0), which makes it + // look like we're "using" an untyped value, but that's purely internal to + // Sorbet. By early returning here, we'll only report an untyped usage if that + // real argument ends up then getting used. + tp.type = recvType.type; + } else { + core::TypePtr argType = core::make_type((int64_t)i.argId); + + core::TypeAndOrigins arg{argType, recvType.origins}; + InlinedVector args; + args.emplace_back(&arg); + + InlinedVector argLocs; + argLocs.emplace_back(bind.loc); + core::CallLocs locs{ + ctx.file, bind.loc, bind.loc, bind.loc, argLocs, + }; + + const auto numPosArgs = 1; + const auto suppressErrors = true; + const auto isPrivateOk = true; + const std::shared_ptr block = nullptr; + core::DispatchArgs dispatchArgs{core::Names::squareBrackets(), + locs, + numPosArgs, + args, + recvType.type, + recvType, + recvType.type, + block, + ctx.locAt(bind.loc), + isPrivateOk, + suppressErrors}; + auto dispatched = recvType.type.dispatchCall(ctx, dispatchArgs); + tp.type = dispatched.returnType; + } tp.origins.emplace_back(ctx.locAt(bind.loc)); + if (lspQueryMatch) { core::lsp::QueryResponse::pushQueryResponse( ctx, core::lsp::IdentResponse(ctx.locAt(bind.loc), bind.bind.variable.data(inWhat), tp, - ctx.owner.asMethodRef())); + ctx.owner.asMethodRef(), ctx.locAt(inWhat.loc))); } }, [&](cfg::Return &i) { @@ -1401,7 +1422,7 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind const core::TypeAndOrigins &typeAndOrigin = getAndFillTypeAndOrigin(ctx, i.what); if (core::Types::isSubType(ctx, core::Types::void_(), methodReturnType)) { - methodReturnType = core::Types::untypedUntracked(); + methodReturnType = core::Types::top(); } if (!core::Types::isSubTypeUnderConstraint(ctx, constr, typeAndOrigin.type, methodReturnType, core::UntypedMode::AlwaysCompatible)) { @@ -1420,6 +1441,13 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind typeAndOrigin.type); } } + } else if (!methodReturnType.isUntyped() && !methodReturnType.isTop() && + typeAndOrigin.type.isUntyped()) { + auto what = core::errors::Infer::errorClassForUntyped(ctx, ctx.file, typeAndOrigin.type); + if (auto e = ctx.beginError(bind.loc, what)) { + e.setHeader("Value returned from method is `{}`", "T.untyped"); + core::TypeErrorDiagnostics::explainUntyped(ctx, e, what, typeAndOrigin, ownerLoc); + } } }, [&](cfg::BlockReturn &i) { @@ -1429,7 +1457,7 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind const core::TypeAndOrigins &typeAndOrigin = getAndFillTypeAndOrigin(ctx, i.what); auto expectedType = i.link->result->main.blockReturnType; if (core::Types::isSubType(ctx, core::Types::void_(), expectedType)) { - expectedType = core::Types::untypedUntracked(); + expectedType = core::Types::top(); } bool isSubtype; if (i.link->result->main.constr) { @@ -1452,6 +1480,12 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind e.addErrorSection(typeAndOrigin.explainGot(ctx, ownerLoc)); core::TypeErrorDiagnostics::explainTypeMismatch(ctx, e, expectedType, typeAndOrigin.type); } + } else if (!expectedType.isUntyped() && !expectedType.isTop() && typeAndOrigin.type.isUntyped()) { + auto what = core::errors::Infer::errorClassForUntyped(ctx, ctx.file, typeAndOrigin.type); + if (auto e = ctx.beginError(bind.loc, what)) { + e.setHeader("Value returned from block is `{}`", "T.untyped"); + core::TypeErrorDiagnostics::explainUntyped(ctx, e, what, typeAndOrigin, ownerLoc); + } } tp.type = core::Types::bottom(); @@ -1535,6 +1569,7 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind ENFORCE(c.cast != core::Names::uncheckedLet() && c.cast != core::Names::bind() && c.cast != core::Names::syntheticBind()); + // TODO(jez) Should we allow `T.let` / `T.cast` opt out of the untyped code error? if (c.cast != core::Names::cast()) { if (c.cast == core::Names::assertType() && ty.type.isUntyped()) { if (auto e = ctx.beginError(bind.loc, core::errors::Infer::CastTypeMismatch)) { @@ -1543,9 +1578,24 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind e.addErrorNote("You may need to add additional `{}` annotations", "sig"); } } else if (!core::Types::isSubType(ctx, ty.type, castType)) { - if (auto e = ctx.beginError(bind.loc, core::errors::Infer::CastTypeMismatch)) { - e.setHeader("Argument does not have asserted type `{}`", castType.show(ctx)); - e.addErrorSection(ty.explainGot(ctx, ownerLoc)); + if (c.cast == core::Names::assumeType()) { + if (auto e = ctx.beginError(bind.loc, core::errors::Infer::IncorrectlyAssumedType)) { + e.setHeader("Assumed expression had type `{}` but found `{}`", castType.show(ctx), + ty.type.show(ctx)); + e.addErrorSection(ty.explainGot(ctx, ownerLoc)); + e.addErrorNote("Please add an explicit type annotation to correct this assumption"); + if (bind.loc.exists() && c.valueLoc.exists()) { + e.replaceWith("Add explicit annotation", ctx.locAt(bind.loc), "T.let({}, {})", + ctx.locAt(c.valueLoc).source(ctx).value(), ty.type.show(ctx)); + } + } + } else { + if (auto e = ctx.beginError(bind.loc, core::errors::Infer::CastTypeMismatch)) { + e.setHeader("Argument does not have asserted type `{}`", castType.show(ctx)); + e.addErrorSection(ty.explainGot(ctx, ownerLoc)); + core::TypeErrorDiagnostics::maybeAutocorrect(ctx, e, ctx.locAt(c.valueLoc), constr, + castType, ty.type); + } } } } else if (!bind.value.isSynthetic()) { @@ -1603,6 +1653,7 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind const core::TypeAndOrigins &cur = (pin != pinnedTypes.end()) ? pin->second : getTypeAndOrigin(ctx, bind.bind.variable); + // TODO(jez) What should we do about untyped code and pinning? bool asGoodAs = core::Types::isSubType(ctx, core::Types::dropLiteral(ctx, tp.type), core::Types::dropLiteral(ctx, cur.type)); @@ -1730,7 +1781,7 @@ core::TypeAndOrigins Environment::getTypeFromRebind(core::Context ctx, const cor result.type = lambdaParam->upperBound; } else { - result.type = rebind.data(ctx)->externalType(); + result.type = rebind.data(ctx)->selfType(ctx); } result.origins.emplace_back(main.blockSpec.loc); diff --git a/infer/inference.cc b/infer/inference.cc index 4789b96715..28dd64cc67 100644 --- a/infer/inference.cc +++ b/infer/inference.cc @@ -1,7 +1,8 @@ -#include "common/Timer.h" #include "common/common.h" +#include "common/timers/Timer.h" #include "core/Loc.h" #include "core/TypeConstraint.h" +#include "core/TypeErrorDiagnostics.h" #include "core/errors/infer.h" #include "core/lsp/QueryResponse.h" #include "infer/SigSuggestion.h" @@ -56,6 +57,8 @@ unique_ptr Inference::run(core::Context ctx, unique_ptr cfg) methodReturnType = returnTypeVar.data(ctx)->resultType; constr->defineDomain(ctx, domainTemp); + } else if (cfg->symbol.data(ctx)->name.isAnyStaticInitName(ctx)) { + methodReturnType = core::Types::top(); } else { methodReturnType = core::Types::untyped(ctx, cfg->symbol); } @@ -296,13 +299,6 @@ unique_ptr Inference::run(core::Context ctx, unique_ptr cfg) totalSendCount++; if (bind.bind.type && !bind.bind.type.isUntyped()) { typedSendCount++; - } else if (bind.bind.type.hasUntyped()) { - DEBUG_ONLY(histogramInc("untyped.sources", bind.bind.type.untypedBlame().rawId());); - if (auto e = ctx.beginError(bind.loc, core::errors::Infer::UntypedValue)) { - e.setHeader("This code is untyped"); - e.addErrorNote("Support for `{}` is minimal. Consider using `{}` instead.", "typed: strong", - "typed: strict"); - } } } ENFORCE(bind.bind.type); @@ -325,7 +321,14 @@ unique_ptr Inference::run(core::Context ctx, unique_ptr cfg) } if (!current.isDead) { ENFORCE(bb->firstDeadInstructionIdx == -1); - current.getAndFillTypeAndOrigin(ctx, bb->bexit.cond); + auto bexitTpo = current.getAndFillTypeAndOrigin(ctx, bb->bexit.cond); + if (bexitTpo.type.isUntyped()) { + auto what = core::errors::Infer::errorClassForUntyped(ctx, ctx.file, bexitTpo.type); + if (auto e = ctx.beginError(bb->bexit.loc, what)) { + e.setHeader("Conditional branch on `{}`", "T.untyped"); + core::TypeErrorDiagnostics::explainUntyped(ctx, e, what, bexitTpo, methodLoc); + } + } } else { ENFORCE(bb->firstDeadInstructionIdx != -1); } @@ -355,6 +358,7 @@ unique_ptr Inference::run(core::Context ctx, unique_ptr cfg) } } + // TODO(jez) Delete these? prodCounterAdd("types.input.sends.typed", typedSendCount); prodCounterAdd("types.input.sends.total", totalSendCount); diff --git a/infer/test/infer_test.cc b/infer/test/infer_test.cc index edbbe5567c..69ca58ef20 100644 --- a/infer/test/infer_test.cc +++ b/infer/test/infer_test.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" // has to go first as it violates our requirements #include "ast/ast.h" #include "ast/desugar/Desugar.h" diff --git a/main/autogen/BUILD b/main/autogen/BUILD index 6b1ebea648..211cbb0a44 100644 --- a/main/autogen/BUILD +++ b/main/autogen/BUILD @@ -2,7 +2,6 @@ cc_library( name = "autogen", srcs = [ "autogen.cc", - "autoloader.cc", "cache.cc", "constant_hash.cc", "crc_builder.cc", @@ -10,7 +9,6 @@ cc_library( ], hdrs = [ "autogen.h", - "autoloader.h", "cache.h", "constant_hash.h", "crc_builder.h", @@ -46,7 +44,7 @@ cc_test( "//ast/desugar", "//core", "//parser", - "@doctest", - "@doctest//:doctest_main", + "@doctest//doctest", + "@doctest//doctest:main", ], ) diff --git a/main/autogen/autogen.cc b/main/autogen/autogen.cc index c817e499db..a480524c11 100644 --- a/main/autogen/autogen.cc +++ b/main/autogen/autogen.cc @@ -3,8 +3,7 @@ #include "ast/Helpers.h" #include "ast/ast.h" #include "ast/treemap/treemap.h" -#include "common/formatting.h" -#include "main/autogen/autoloader.h" +#include "common/strings/formatting.h" #include "main/autogen/crc_builder.h" using namespace std; @@ -199,6 +198,11 @@ class AutogenWalk { if (original.original == nullptr) { return; } + if (original.symbol.name(ctx) == core::Names::Constants::AttachedClass()) { + // This is a reference to a constant like that came from the `has_attached_class!` DSL + // These are not real constant references. + return; + } // Create a new `Reference` auto &ref = refs.emplace_back(); @@ -247,6 +251,12 @@ class AutogenWalk { return; } + if (lhs->symbol.name(ctx) == core::Names::Constants::AttachedClass()) { + // has_attached_class! create constant assignments that look like ` = type_member` + // which do not actually exist at runtime. + return; + } + if (ctx.file.data(ctx).isRBI()) { // We are only concerned with references in RBI files so that dependencies can be // accurately tracked. Definitions of casgns are not needed. diff --git a/main/autogen/autoloader.cc b/main/autogen/autoloader.cc deleted file mode 100644 index 3fd9293dda..0000000000 --- a/main/autogen/autoloader.cc +++ /dev/null @@ -1,604 +0,0 @@ -#include "main/autogen/autoloader.h" -#include "absl/strings/match.h" -#include "common/FileOps.h" -#include "common/Timer.h" -#include "common/concurrency/ConcurrentQueue.h" -#include "common/concurrency/WorkerPool.h" -#include "common/formatting.h" -#include "common/sort.h" -#include "core/GlobalState.h" -#include "core/Names.h" - -#include "absl/strings/str_join.h" -#include "absl/strings/str_replace.h" -#include "absl/strings/str_split.h" - -using namespace std; -namespace sorbet::autogen { - -// Like the rest of autogen, the `autoloader` pass works by walking an existing tree of data and converting it to a -// different representation before using it. In this case, it takes the `autogen::ParsedFile` representation and -// converts it to a `DefTree`, and then emits the autoloader files based on passed-in string fragments - -bool AutoloaderConfig::include(const NamedDefinition &nd) const { - return !nd.qname.nameParts.empty() && topLevelNamespaceRefs.contains(nd.qname.nameParts[0]); -} - -bool AutoloaderConfig::includePath(string_view path) const { - return absl::EndsWith(path, ".rb") && - !sorbet::FileOps::isFileIgnored("", fmt::format("/{}", path), absoluteIgnorePatterns, - relativeIgnorePatterns); -} - -bool AutoloaderConfig::includeRequire(core::NameRef req) const { - return !excludedRequireRefs.contains(req); -} - -bool AutoloaderConfig::sameFileCollapsable(const vector &module) const { - return !nonCollapsableModuleNames.contains(module); -} - -bool AutoloaderConfig::registeredForPBAL(const vector &pkgParts) const { - return pbalNamespaces.empty() || (absl::c_any_of(pbalNamespaces, [&pkgParts](auto &pbalNamespace) { - return pbalNamespace.size() <= pkgParts.size() && - std::equal(pbalNamespace.begin(), pbalNamespace.end(), pkgParts.begin()); - })); -} - -string_view AutoloaderConfig::normalizePath(const core::GlobalState &gs, core::FileRef file) const { - auto path = file.data(gs).path(); - for (const auto &prefix : stripPrefixes) { - if (absl::StartsWith(path, prefix)) { - return path.substr(prefix.size()); - } - } - return path; -} - -AutoloaderConfig AutoloaderConfig::enterConfig(core::GlobalState &gs, const realmain::options::AutoloaderConfig &cfg) { - AutoloaderConfig out; - out.rootDir = cfg.rootDir; - out.preamble = cfg.preamble; - out.registryModule = cfg.registryModule; - out.rootObject = cfg.rootObject; - for (auto &str : cfg.modules) { - out.topLevelNamespaceRefs.emplace(gs.enterNameConstant(str)); - } - for (auto &str : cfg.requireExcludes) { - out.excludedRequireRefs.emplace(gs.enterNameUTF8(str)); - } - for (auto &nameParts : cfg.sameFileModules) { - vector refs; - for (auto &name : nameParts) { - refs.emplace_back(gs.enterNameConstant(name)); - } - out.nonCollapsableModuleNames.emplace(refs); - } - for (auto &nameParts : cfg.pbalNamespaces) { - vector refs; - for (auto &name : nameParts) { - refs.emplace_back(gs.enterNameConstant(name)); - } - out.pbalNamespaces.emplace(refs); - } - out.absoluteIgnorePatterns = cfg.absoluteIgnorePatterns; - out.relativeIgnorePatterns = cfg.relativeIgnorePatterns; - out.stripPrefixes = cfg.stripPrefixes; - return out; -} - -NamedDefinition NamedDefinition::fromDef(const core::GlobalState &gs, ParsedFile &parsedFile, DefinitionRef def) { - QualifiedName parentName; - if (def.data(parsedFile).parent_ref.exists()) { - auto parentRef = def.data(parsedFile).parent_ref.data(parsedFile); - if (!parentRef.resolved.empty()) { - parentName = parentRef.resolved; - } else { - parentName = parentRef.name; - } - } - const auto &pathStr = parsedFile.tree.file.data(gs).path(); - uint32_t pathDepth = count(pathStr.begin(), pathStr.end(), '/'); // Pre-compute for comparison - - auto fullName = parsedFile.showQualifiedName(gs, def); - - return {def.data(parsedFile), move(fullName), move(parentName), - parsedFile.requireStatements, parsedFile.tree.file, pathDepth}; -} - -bool NamedDefinition::preferredTo(const core::GlobalState &gs, const NamedDefinition &lhs, const NamedDefinition &rhs) { - ENFORCE(lhs.qname == rhs.qname, "Can only compare definitions with same name"); - // Load defs with a parent name first since others will tend to depend on them. - // Secondarily, the same idea for defs with less nesting in their path. - // Finally, sort alphabetically by path to break any ties. - if (lhs.parentName.empty() != rhs.parentName.empty()) { - return rhs.parentName.empty(); - } - if (lhs.pathDepth != rhs.pathDepth) { - return lhs.pathDepth < rhs.pathDepth; - } - return lhs.fileRef.data(gs).path() < rhs.fileRef.data(gs).path(); -} - -void showHelper(const core::GlobalState &gs, fmt::memory_buffer &buf, const DefTree &node, int level) { - auto fileRefToString = [&](const NamedDefinition &nd) -> string_view { return nd.fileRef.data(gs).path(); }; - fmt::format_to(std::back_inserter(buf), "{} [{}]\n", node.root() ? "" : node.name().show(gs), - fmt::map_join(node.namedDefs, ", ", fileRefToString)); - for (const auto &[name, tree] : node.children) { - for (int i = 0; i < level; ++i) { - fmt::format_to(std::back_inserter(buf), " "); - } - showHelper(gs, buf, *tree, level + 1); - } -} - -string DefTree::show(const core::GlobalState &gs, int level) const { - fmt::memory_buffer buf; - showHelper(gs, buf, *this, 0); - return to_string(buf); -} - -string DefTree::fullName(const core::GlobalState &gs) const { - return fmt::format("{}", - fmt::map_join(qname.nameParts, "::", [&](core::NameRef nr) -> string { return nr.show(gs); })); -} - -string join(string_view path, string file) { - if (file.empty()) { - return string(path); - } - return fmt::format("{}/{}", path, file); -} - -bool visitDefTree(const DefTree &tree, std::function visit) { - bool descend = visit(tree); - if (!descend) { - return false; - } - for (const auto &[_, child] : tree.children) { - descend = visitDefTree(*child, visit); - if (!descend) { - return false; - } - } - return true; -} - -core::FileRef DefTree::file() const { - core::FileRef ref; - if (!namedDefs.empty()) { - // TODO what if there are more than one? - ref = namedDefs[0].fileRef; - } else if (nonBehaviorDef != nullptr) { - ref = nonBehaviorDef->fileRef; - } - return ref; -} - -bool DefTree::hasDifferentFile(core::FileRef file) const { - bool res = false; - auto visit = [&](const DefTree &node) -> bool { - auto f = node.file(); - if (file != f && f.exists()) { - res = true; - return false; - } - return true; - }; - visitDefTree(*this, visit); - return res; -} - -bool DefTree::root() const { - return qname.empty(); -} - -core::NameRef DefTree::name() const { - ENFORCE(!qname.empty()); - return qname.name(); -} - -void DefTree::renderAutoloadSrc(fmt::memory_buffer &buf, const core::GlobalState &gs, - const AutoloaderConfig &alCfg) const { - core::FileRef definingFileRef = definingFile(); - - fmt::format_to(std::back_inserter(buf), "{}\n", alCfg.preamble); - - if (definingFileRef.exists()) { - requireStatements(gs, alCfg, buf); - } - - string fullName = "nil"; - string casgnArg; - auto type = definitionType(gs); - - if (type == Definition::Type::Module || type == Definition::Type::Class) { - fullName = root() ? alCfg.rootObject - : fmt::format("{}", fmt::map_join(qname.nameParts, "::", [&](const auto &nr) -> string { - return nr.show(gs); - })); - if (!root()) { - predeclare(gs, fullName, buf); - } - - if (pkgName.exists()) { - ENFORCE(!gs.packageDB().empty()); - - auto &pkg = gs.packageDB().getPackageInfo(pkgName); - - // First path prefix is guaranteed to be the directory location of the package - const string_view pathPrefix = pkg.pathPrefixes()[0]; - - fmt::format_to(std::back_inserter(buf), "\n{}.pbal_register_package({}, '{}')\n", alCfg.registryModule, - fullName, pathPrefix); - } - } else if (type == Definition::Type::Casgn || type == Definition::Type::Alias || - type == Definition::Type::TypeAlias) { - ENFORCE(qname.size() > 1); - casgnArg = fmt::format(", [{}, :{}]", - fmt::map_join(qname.nameParts.begin(), --qname.nameParts.end(), - "::", [&](const auto &nr) -> string { return nr.show(gs); }), - qname.name().show(gs)); - } - - if (definingFileRef.exists()) { - fmt::format_to(std::back_inserter(buf), "\n{}.for_autoload({}, \"{}\"{})\n", alCfg.registryModule, fullName, - alCfg.normalizePath(gs, definingFileRef), casgnArg); - } -} - -void DefTree::requireStatements(const core::GlobalState &gs, const AutoloaderConfig &alCfg, - fmt::memory_buffer &buf) const { - if (root() || !hasDef()) { - return; - } - auto &ndef = definition(gs); - vector reqs; - for (auto reqRef : ndef.requireStatements) { - if (alCfg.includeRequire(reqRef)) { - string req = reqRef.show(gs); - reqs.emplace_back(req); - } - } - fast_sort(reqs); - auto last = unique(reqs.begin(), reqs.end()); - for (auto it = reqs.begin(); it != last; ++it) { - fmt::format_to(std::back_inserter(buf), "require '{}'\n", *it); - } -} - -void DefTree::predeclare(const core::GlobalState &gs, string_view fullName, fmt::memory_buffer &buf) const { - if (hasDef() && definitionType(gs) == Definition::Type::Class) { - fmt::format_to(std::back_inserter(buf), "\nclass {}", fullName); - auto &def = definition(gs); - if (!def.parentName.empty()) { - fmt::format_to( - std::back_inserter(buf), " < {}", - fmt::map_join(def.parentName.nameParts, "::", [&](const auto &nr) -> string { return nr.show(gs); })); - } - } else { - fmt::format_to(std::back_inserter(buf), "\nmodule {}", fullName); - } - // TODO aliases? casgn? - fmt::format_to(std::back_inserter(buf), "\nend\n"); -} - -string DefTree::path(const core::GlobalState &gs) const { - auto toPath = [&](const auto &fr) -> string { return fr.show(gs); }; - return fmt::format("{}.rb", fmt::map_join(qname.nameParts, "/", toPath)); -} - -Definition::Type DefTree::definitionType(const core::GlobalState &gs) const { - if (!hasDef()) { - return Definition::Type::Module; - } - return definition(gs).def.type; -} - -bool DefTree::hasDef() const { - return (nonBehaviorDef != nullptr) || !namedDefs.empty(); -} - -const NamedDefinition &DefTree::definition(const core::GlobalState &gs) const { - if (!namedDefs.empty()) { - ENFORCE(namedDefs.size() == 1, "Cannot determine definitions for '{}' (size={})", fullName(gs), - namedDefs.size()); - return namedDefs[0]; - } else { - ENFORCE(nonBehaviorDef != nullptr, "Could not find any definitions for '{}'", fullName(gs)); - return *nonBehaviorDef; - } -} - -void DefTreeBuilder::addParsedFileDefinitions(const core::GlobalState &gs, const AutoloaderConfig &alConfig, - std::unique_ptr &root, ParsedFile &pf) { - ENFORCE(root->root()); - if (!alConfig.includePath(pf.path)) { - return; - } - for (auto &def : pf.defs) { - if (def.id.id() == 0) { - continue; - } - addSingleDef(gs, alConfig, root, NamedDefinition::fromDef(gs, pf, def.id)); - } -} - -DefTree *DefTree::findNode(const vector &nameParts) { - DefTree *node = this; - for (auto nr : nameParts) { - auto it = node->children.find(nr); - if (it == node->children.end()) { - return nullptr; - } - node = it->second.get(); - } - - return node; -} - -void DefTree::markPackageNamespace(core::NameRef mangledName, const vector &nameParts) { - DefTree *node = this->findNode(nameParts); - if (node == nullptr) { - return; - } - - ENFORCE(!(node->pkgName.exists()), "Package name should not be already set"); - node->pkgName = mangledName; -} - -void DefTreeBuilder::markPackages(const core::GlobalState &gs, DefTree &root, const AutoloaderConfig &alCfg) { - auto testRoot = root.findNode({core::Names::Constants::Test()}); - - for (auto nr : gs.packageDB().packages()) { - auto &pkg = gs.packageDB().getPackageInfo(nr); - if (pkg.strictAutoloaderCompatibility()) { - // Only mark strictly path-based autoload compatible packages for now to reduce - // computation / code generation, given this is the only current use-case for registering - // packages in this context in the Stripe codebase. - - // Additionally this package must be registed for path-based autoloading. - // TODO: (aadi-stripe, 10/24/2022) Remove this functionality once we no longer require - // special registration. - auto &pkgFullName = pkg.fullName(); - if (!alCfg.registeredForPBAL(pkgFullName)) { - continue; - } - - root.markPackageNamespace(pkg.mangledName(), pkgFullName); - if (testRoot != nullptr) { - testRoot->markPackageNamespace(pkg.mangledName(), pkgFullName); - } - } - } -} - -void DefTreeBuilder::addSingleDef(const core::GlobalState &gs, const AutoloaderConfig &alCfg, - std::unique_ptr &root, NamedDefinition ndef) { - if (!alCfg.include(ndef)) { - return; - } - - DefTree *node = root.get(); - for (const auto &part : ndef.qname.nameParts) { - auto &child = node->children[part]; - if (!child) { - child = make_unique(); - child->qname.nameParts = node->qname.nameParts; - child->qname.nameParts.emplace_back(part); - } - node = child.get(); - } - if (ndef.def.defines_behavior) { - node->namedDefs.emplace_back(move(ndef)); - } else { - updateNonBehaviorDef(gs, *node, move(ndef)); - } -} - -DefTree DefTreeBuilder::merge(const core::GlobalState &gs, DefTree lhs, DefTree rhs) { - ENFORCE(lhs.qname == rhs.qname, "Name mismatch for DefTreeBuilder::merge"); - lhs.namedDefs.insert(lhs.namedDefs.end(), make_move_iterator(rhs.namedDefs.begin()), - make_move_iterator(rhs.namedDefs.end())); - if (rhs.nonBehaviorDef) { - updateNonBehaviorDef(gs, lhs, move(*rhs.nonBehaviorDef.get())); - } - for (auto &[rname, rchild] : rhs.children) { - auto lchild = lhs.children.find(rname); - if (lchild == lhs.children.end()) { - lhs.children[rname] = move(rchild); - } else { - lhs.children[rname] = make_unique(merge(gs, move(*lchild->second), move(*rchild))); - } - } - return lhs; -} - -void DefTreeBuilder::updateNonBehaviorDef(const core::GlobalState &gs, DefTree &node, NamedDefinition ndef) { - if (!node.namedDefs.empty()) { - // Non behavior-defining definitions do not matter for nodes that have behavior. There is no - // need to continue tracking it. - return; - } - if ((node.nonBehaviorDef == nullptr) || NamedDefinition::preferredTo(gs, ndef, *node.nonBehaviorDef)) { - node.nonBehaviorDef = make_unique(move(ndef)); - } -} - -void DefTreeBuilder::collapseSameFileDefs(const core::GlobalState &gs, const AutoloaderConfig &alCfg, DefTree &root) { - core::FileRef definingFile; - if (!root.namedDefs.empty()) { - definingFile = root.file(); - } - if (!alCfg.sameFileCollapsable(root.qname.nameParts)) { - return; - } - - for (auto it = root.children.begin(); it != root.children.end(); /*nothing*/) { - auto copyIt = - it++; // see - // https://github.com/abseil/abseil-cpp/blob/62f05b1f57ad660e9c09e02ce7d591dcc4d0ca08/absl/container/internal/raw_hash_set.h#L1157-L1169 - // for why - auto &child = copyIt->second; - - if (child->pkgName.exists() || child->hasDifferentFile(definingFile)) { - collapseSameFileDefs(gs, alCfg, *child); - } else { - root.children.erase(copyIt); - } - } -} - -namespace { -struct RenderAutoloadTask { - string filePath; - const DefTree &node; -}; - -struct ModificationState { - bool modified; -}; - -// This function has two duties: -// * It creates autoload rendering tasks which will occur in a later parallel phase. -// * It creates subdirectories when needed, as they are required to write the autoloader output. -void populateAutoloadTasksAndCreateDirectories(const core::GlobalState &gs, vector &tasks, - const AutoloaderConfig &alCfg, string_view path, const DefTree &node) { - string name = node.root() ? "root" : node.name().show(gs); - - if (node.mustRender(gs)) { - string filePath = join(path, fmt::format("{}.rb", name)); - tasks.emplace_back(RenderAutoloadTask{move(filePath), node}); - } - - // Generate autoloads for child nodes if they exist and pkgName is not present (since the latter indicates - // path-based autoloading for the package). - if (!node.children.empty() && !node.pkgName.exists()) { - auto subdir = join(path, node.root() ? "" : name); - if (!node.root()) { - FileOps::ensureDir(subdir); - } - for (auto &[_, child] : node.children) { - populateAutoloadTasksAndCreateDirectories(gs, tasks, alCfg, subdir, *child); - } - } -} -}; // namespace - -core::FileRef DefTree::definingFile() const { - core::FileRef definingFileRef; - - if (!namedDefs.empty() || (hasDef() && children.empty())) { - definingFileRef = file(); - } - - return definingFileRef; -} - -bool DefTree::mustRender(const core::GlobalState &gs) const { - // Either the node has a behavior-defining file, or has a package name - if (definingFile().exists() || pkgName.exists()) { - return true; - } - - // The node is a class node (as opposed to a module) - if (definitionType(gs) == Definition::Type::Class) { - return true; - } - - return false; -} - -void AutoloadWriter::writeAutoloads(const core::GlobalState &gs, WorkerPool &workers, const AutoloaderConfig &alCfg, - const std::string &path, const DefTree &root) { - vector tasks; - { - Timer timeit(gs.tracer(), "populateAutoloadTasks"); - populateAutoloadTasksAndCreateDirectories(gs, tasks, alCfg, path, root); - } - - auto modificationState = ModificationState{false}; - std::mutex modificationMutex; - - if (FileOps::exists(path)) { - Timer timeit(gs.tracer(), "removeExistingFiles"); - - // Clear out files that we do not plan to write. - vector existingFiles = FileOps::listFilesInDir(path, {".rb"}, workers, true, {}, {}); - UnorderedSet existingFilesSet(make_move_iterator(existingFiles.begin()), - make_move_iterator(existingFiles.end())); - for (auto &task : tasks) { - existingFilesSet.erase(task.filePath); - } - for (const auto &file : existingFilesSet) { - FileOps::removeFile(file); - - // TODO (aadi-stripe, 12/7/2022): Investigate whether dangling autoloads are an actual problem that needs - // to be mitigated here. - // Remove all empty directories along path. This prevents zeitwerk from setting up dangling autoloads. - /* std::string_view filePath = file; */ - /* int curDirPos = filePath.find_last_of('/'); */ - /* while (curDirPos > 0) { */ - /* const auto curDir = filePath.substr(0, curDirPos); */ - /* if (curDir == path || !FileOps::removeEmptyDir(string(curDir))) { */ - /* break; */ - /* } */ - - /* curDirPos = filePath.find_last_of('/', curDirPos - 1); */ - /* } */ - } - } - - // Parallelize writing the files. - auto inputq = make_shared>(tasks.size()); - auto outputq = make_shared>(tasks.size()); - for (int i = 0; i < tasks.size(); ++i) { - inputq->push(i, 1); - } - - workers.multiplexJob( - "runAutogenWriteAutoloads", [&gs, &tasks, &alCfg, inputq, outputq, &modificationState, &modificationMutex]() { - int n = 0; - { - Timer timeit(gs.tracer(), "autogenWriteAutoloadsWorker"); - int idx = 0; - fmt::memory_buffer buf; - - for (auto result = inputq->try_pop(idx); !result.done(); result = inputq->try_pop(idx)) { - ++n; - auto &task = tasks[idx]; - buf.clear(); - task.node.renderAutoloadSrc(buf, gs, alCfg); - bool rewritten = FileOps::writeIfDifferent(task.filePath, string_view{&buf.data()[0], buf.size()}); - - // Initial read should be cheap, read outside mutex - if (rewritten && !modificationState.modified) { - modificationMutex.lock(); - // Re-test inside mutex - if (!modificationState.modified) { - modificationState.modified = true; - } - modificationMutex.unlock(); - } - } - } - - outputq->push(getAndClearThreadCounters(), n); - }); - - CounterState out; - for (auto res = outputq->wait_pop_timed(out, WorkerPool::BLOCK_INTERVAL(), gs.tracer()); !res.done(); - res = outputq->wait_pop_timed(out, WorkerPool::BLOCK_INTERVAL(), gs.tracer())) { - if (!res.gotItem()) { - continue; - } - counterConsume(move(out)); - } - - const std::string mtimeFile = join(path, "_mtime_stamp"); - if (!FileOps::exists(mtimeFile) || modificationState.modified) { - FileOps::write(mtimeFile, to_string(std::time(0))); - } -} - -} // namespace sorbet::autogen diff --git a/main/autogen/autoloader.h b/main/autogen/autoloader.h deleted file mode 100644 index 6086626ef1..0000000000 --- a/main/autogen/autoloader.h +++ /dev/null @@ -1,149 +0,0 @@ -#ifndef AUTOGEN_AUTOLOADER_H -#define AUTOGEN_AUTOLOADER_H -#include "ast/ast.h" -#include "main/autogen/data/definitions.h" -#include "main/options/options.h" -#include - -namespace sorbet { -class WorkerPool; -} - -namespace sorbet::autogen { - -// Contains same information as `realmain::options::AutoloaderConfig` except with `core::NameRef`s -// instead of strings. -struct AutoloaderConfig { - // Convert the autoloader config passed in from `realmain` to this `AutoloaderConfig`. Much of this is about - // converting `string`s to `NameRef`s - static AutoloaderConfig enterConfig(core::GlobalState &gs, const realmain::options::AutoloaderConfig &cfg); - - // `true` if the definition should have autoloads generated for it based on the `AutoloaderConfig` - bool include(const NamedDefinition &) const; - // `true` if the file should have autoloads generated for it (i.e. it's a ruby source file that's not ignored) - bool includePath(std::string_view path) const; - // `true` if the file should be required based on the provided configuration - bool includeRequire(core::NameRef req) const; - // Should definitions in this namespace be collapsed into their - // parent if they all are from the same file? - bool sameFileCollapsable(const std::vector &module) const; - // This package is registered for path-based autoloading - bool registeredForPBAL(const std::vector &pkgParts) const; - // normalize the path relative to the provided prefixes - std::string_view normalizePath(const core::GlobalState &gs, core::FileRef file) const; - - std::string rootDir; - std::string preamble; - std::string registryModule; - std::string rootObject; - UnorderedSet topLevelNamespaceRefs; - UnorderedSet excludedRequireRefs; - UnorderedSet> nonCollapsableModuleNames; - UnorderedSet> pbalNamespaces; - std::vector absoluteIgnorePatterns; - std::vector relativeIgnorePatterns; - std::vector stripPrefixes; - - AutoloaderConfig() = default; - AutoloaderConfig(const AutoloaderConfig &) = delete; - AutoloaderConfig(AutoloaderConfig &&) = default; - AutoloaderConfig &operator=(const AutoloaderConfig &) = delete; - AutoloaderConfig &operator=(AutoloaderConfig &&) = default; -}; - -struct NamedDefinition { - // Convert an `autogen::DefinitionRef` to a `NamedDefinition`: this pulls the name, the parent definitions' name, - // the requirements, the path, and the _depth_ of the path (which can short-circuit comparison against another path) - static NamedDefinition fromDef(const core::GlobalState &, ParsedFile &, DefinitionRef); - // Used for sorting `NamedDefinition` - static bool preferredTo(const core::GlobalState &gs, const NamedDefinition &lhs, const NamedDefinition &rhs); - - Definition def; - QualifiedName qname; - QualifiedName parentName; - std::vector requireStatements; - core::FileRef fileRef; - uint32_t pathDepth; - - NamedDefinition() = default; - NamedDefinition(Definition def, QualifiedName qname, QualifiedName parentName, - std::vector requireStatements, core::FileRef fileRef, uint32_t pathDepth) - : def(def), qname(std::move(qname)), parentName(std::move(parentName)), - requireStatements(std::move(requireStatements)), fileRef(fileRef), pathDepth(pathDepth) {} - NamedDefinition(const NamedDefinition &) = delete; - NamedDefinition(NamedDefinition &&) = default; - NamedDefinition &operator=(const NamedDefinition &) = delete; - NamedDefinition &operator=(NamedDefinition &&) = default; -}; - -class DefTree { -public: - UnorderedMap> children; - - // For definitions that define behavior we enforce that it is only from a single code location. - // However some nodes may represent a name that is used in many places but where none define - // behavior (e.g. a module that is only used for namespacing). In that case, deterministically - // pick a single file to use for the definition based on NamedDefinition::preferredTo precedence - // rules. - std::vector namedDefs; - std::unique_ptr nonBehaviorDef; - QualifiedName qname; - core::NameRef pkgName; - - bool root() const; - core::NameRef name() const; - std::string path(const core::GlobalState &gs) const; - std::string show(const core::GlobalState &gs, int level = 0) const; // Render the entire tree - std::string fullName(const core::GlobalState &) const; - - void renderAutoloadSrc(fmt::memory_buffer &buf, const core::GlobalState &gs, const AutoloaderConfig &) const; - bool mustRender(const core::GlobalState &gs) const; - - DefTree() = default; - DefTree(const DefTree &) = delete; - DefTree(DefTree &&) = default; - DefTree &operator=(const DefTree &) = delete; - DefTree &operator=(DefTree &&) = default; - -private: - core::FileRef file() const; - core::FileRef definingFile() const; - void predeclare(const core::GlobalState &gs, std::string_view fullName, fmt::memory_buffer &buf) const; - void requireStatements(const core::GlobalState &gs, const AutoloaderConfig &, fmt::memory_buffer &buf) const; - bool hasDifferentFile(core::FileRef) const; - bool hasDef() const; - const NamedDefinition &definition(const core::GlobalState &) const; - Definition::Type definitionType(const core::GlobalState &) const; - void markPackageNamespace(core::NameRef mangledName, const std::vector &nameParts); - DefTree *findNode(const std::vector &nameParts); - - friend class DefTreeBuilder; -}; - -class DefTreeBuilder { -public: - // Add all definitions in a parsed file to a `DefTree` root. - static void addParsedFileDefinitions(const core::GlobalState &, const AutoloaderConfig &, - std::unique_ptr &root, ParsedFile &); - static void addSingleDef(const core::GlobalState &, const AutoloaderConfig &, std::unique_ptr &root, - NamedDefinition); - - static DefTree merge(const core::GlobalState &gs, DefTree lhs, DefTree rhs); - static void markPackages(const core::GlobalState &gs, DefTree &root, const AutoloaderConfig &autoloaderConfig); - static void collapseSameFileDefs(const core::GlobalState &gs, const AutoloaderConfig &, DefTree &root); - -private: - static void updateNonBehaviorDef(const core::GlobalState &gs, DefTree &node, NamedDefinition ndef); -}; - -class AutoloadWriter { -public: - static void writeAutoloads(const core::GlobalState &gs, WorkerPool &workers, const AutoloaderConfig &, - const std::string &path, const DefTree &root); - - static void writePackageAutoloads(const core::GlobalState &gs, const AutoloaderConfig &, const std::string &path, - const std::vector &packages); -}; - -} // namespace sorbet::autogen -#endif // AUTOGEN_AUTOLOADER_H diff --git a/main/autogen/constant_hash.cc b/main/autogen/constant_hash.cc index fbc347e339..eb99ceb223 100644 --- a/main/autogen/constant_hash.cc +++ b/main/autogen/constant_hash.cc @@ -74,6 +74,16 @@ struct ConstantHashWalk { hashConstant(ctx, arg); } hashSoFar = core::mix(hashSoFar, core::_hash(")")); + } else if (send.fun == core::Names::autoloader_compatibility()) { + hashSoFar = core::mix(hashSoFar, core::_hash("(a")); + if (send.hasPosArgs()) { + if (auto str = ast::cast_tree(send.posArgs().front())) { + if (str->isString()) { + hashSoFar = core::mix(hashSoFar, core::_hash(str->asString().shortName(ctx))); + } + } + } + hashSoFar = core::mix(hashSoFar, core::_hash(")")); } } diff --git a/main/autogen/data/definitions.cc b/main/autogen/data/definitions.cc index afb3fedce3..d792348189 100644 --- a/main/autogen/data/definitions.cc +++ b/main/autogen/data/definitions.cc @@ -1,6 +1,6 @@ #include "main/autogen/data/definitions.h" #include "ast/ast.h" -#include "common/formatting.h" +#include "common/strings/formatting.h" #include "core/Files.h" #include "core/GlobalState.h" #include "main/autogen/data/msgpack.h" diff --git a/main/autogen/data/definitions.h b/main/autogen/data/definitions.h index 7e684ccd4e..3a2dac2255 100644 --- a/main/autogen/data/definitions.h +++ b/main/autogen/data/definitions.h @@ -43,9 +43,7 @@ struct Definition; struct DefinitionRef; struct ReferenceRef; -struct AutoloaderConfig; struct NamedDefinition; -class DefTree; enum class ClassKind { Class, Module }; diff --git a/main/autogen/subclasses.cc b/main/autogen/subclasses.cc index c7fe146e52..a20d981435 100644 --- a/main/autogen/subclasses.cc +++ b/main/autogen/subclasses.cc @@ -1,7 +1,7 @@ #include "main/autogen/subclasses.h" #include "common/FileOps.h" -#include "common/formatting.h" -#include "common/sort.h" +#include "common/sort/sort.h" +#include "common/strings/formatting.h" #include "core/GlobalState.h" using namespace std; diff --git a/main/autogen/test/constant_hash_test.cc b/main/autogen/test/constant_hash_test.cc index 6571f32a4c..3d7664207c 100644 --- a/main/autogen/test/constant_hash_test.cc +++ b/main/autogen/test/constant_hash_test.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" // has to go first as it violates our requirements #include "ast/desugar/Desugar.h" #include "core/ErrorQueue.h" @@ -186,6 +186,18 @@ TEST_CASE("Require") { // NOLINT "do_the_thing!")); } +TEST_CASE("Package Autoloader Compatibility") { // NOLINT + Helper helper; + + auto req = helper.hashExample("autoloader_compatibility 'legacy'\n"); + + // removing the annotation should affect the hash + CHECK_NE(req, helper.hashExample("\n")); + + // changing the annotation should affect the hash + CHECK_NE(req, helper.hashExample("autoloader_compatibility 'strict'\n")); +} + TEST_CASE("Extend/Include") { Helper helper; diff --git a/main/cache/cache-orig.cc b/main/cache/cache-orig.cc index 0bebc2e118..599ed6fbfe 100644 --- a/main/cache/cache-orig.cc +++ b/main/cache/cache-orig.cc @@ -13,7 +13,7 @@ unique_ptr ownIfUnchanged(const core::GlobalState &gs, uniqu } void maybeCacheGlobalStateAndFiles(unique_ptr kvstore, const options::Options &opts, - core::GlobalState &gs, WorkerPool &workers, vector &indexed) { + core::GlobalState &gs, WorkerPool &workers, const vector &indexed) { return; } diff --git a/main/cache/cache.cc b/main/cache/cache.cc index 3f028a14a8..e0b97db852 100644 --- a/main/cache/cache.cc +++ b/main/cache/cache.cc @@ -13,7 +13,10 @@ unique_ptr maybeCreateKeyValueStore(shared_ptr<::spdlog::log if (opts.cacheDir.empty()) { return nullptr; } - auto flavor = opts.lspExperimentalFastPathEnabled ? "experimentalfastpath" : "normalfastpath"; + // Despite being called "experimental," this feature is actually stable. We just didn't want to + // bust all existing caches when we promoted the experimental-at-the-time incremental fast path + // to the stable version. + auto flavor = "experimentalfastpath"; return make_unique(make_unique(logger, sorbet_full_version_string, opts.cacheDir, move(flavor), opts.maxCacheSizeBytes)); } @@ -33,7 +36,7 @@ unique_ptr ownIfUnchanged(const core::GlobalState &gs, uniqu } void maybeCacheGlobalStateAndFiles(unique_ptr kvstore, const options::Options &opts, - core::GlobalState &gs, WorkerPool &workers, vector &indexed) { + core::GlobalState &gs, WorkerPool &workers, const vector &indexed) { if (kvstore == nullptr) { return; } diff --git a/main/cache/cache.h b/main/cache/cache.h index 63e6232289..4fa37db9ab 100644 --- a/main/cache/cache.h +++ b/main/cache/cache.h @@ -33,7 +33,8 @@ std::unique_ptr ownIfUnchanged(const core::GlobalState &gs, // If kvstore is not null, caches global state and the given files to disk if they have changed. Can silently fail to // cache void maybeCacheGlobalStateAndFiles(std::unique_ptr kvstore, const options::Options &opts, - core::GlobalState &gs, WorkerPool &workers, std::vector &indexed); + core::GlobalState &gs, WorkerPool &workers, + const std::vector &indexed); } // namespace sorbet::realmain::cache #endif diff --git a/main/lsp/AbstractRenamer.cc b/main/lsp/AbstractRewriter.cc similarity index 82% rename from main/lsp/AbstractRenamer.cc rename to main/lsp/AbstractRewriter.cc index 3ceaeebda1..a1801fc911 100644 --- a/main/lsp/AbstractRenamer.cc +++ b/main/lsp/AbstractRewriter.cc @@ -1,4 +1,4 @@ -#include "AbstractRenamer.h" +#include "AbstractRewriter.h" #include "main/lsp/LSPLoop.h" #include "main/lsp/LSPQuery.h" @@ -24,7 +24,7 @@ core::ClassOrModuleRef findRootClassWithMethod(const core::GlobalState &gs, core } // namespace -bool AbstractRenamer::UniqueSymbolQueue::tryEnqueue(core::SymbolRef s) { +bool AbstractRewriter::UniqueSymbolQueue::tryEnqueue(core::SymbolRef s) { auto insertResult = set.insert(s); bool isNew = insertResult.second; if (isNew) { @@ -33,7 +33,7 @@ bool AbstractRenamer::UniqueSymbolQueue::tryEnqueue(core::SymbolRef s) { return isNew; } -core::SymbolRef AbstractRenamer::UniqueSymbolQueue::pop() { +core::SymbolRef AbstractRewriter::UniqueSymbolQueue::pop() { if (!symbols.empty()) { auto s = symbols.front(); symbols.pop_front(); @@ -42,7 +42,7 @@ core::SymbolRef AbstractRenamer::UniqueSymbolQueue::pop() { return core::Symbols::noSymbol(); } -optional>> AbstractRenamer::buildTextDocumentEdits() { +optional>> AbstractRewriter::buildTextDocumentEdits() { if (invalid) { return nullopt; } @@ -67,7 +67,7 @@ optional>> AbstractRenamer::buildTextDocumen return textDocEdits; } -variant> AbstractRenamer::buildWorkspaceEdit() { +variant> AbstractRewriter::buildWorkspaceEdit() { auto edits = buildTextDocumentEdits(); if (!edits.has_value()) { return JSONNullObject(); @@ -78,21 +78,21 @@ variant> AbstractRenamer::buildWorkspa return we; } -bool AbstractRenamer::getInvalid() { +bool AbstractRewriter::getInvalid() { return invalid; } -std::string AbstractRenamer::getError() { +std::string AbstractRewriter::getError() { return error; } -std::shared_ptr AbstractRenamer::getQueue() { +std::shared_ptr AbstractRewriter::getQueue() { return symbolQueue; } // Add subclass-related methods (methods overriding and overridden by `symbol`) to the `methods` vector. -void AbstractRenamer::addSubclassRelatedMethods(const core::GlobalState &gs, core::MethodRef symbol, - shared_ptr methods) { +void AbstractRewriter::addSubclassRelatedMethods(const core::GlobalState &gs, core::MethodRef symbol, + shared_ptr methods) { auto symbolData = symbol.data(gs); // We have to check for methods as part of a class hierarchy: Follow superClass() links till we find the root; @@ -121,8 +121,9 @@ void AbstractRenamer::addSubclassRelatedMethods(const core::GlobalState &gs, cor } // Add methods that are related because of dispatching via secondary components in sends (union types). -void AbstractRenamer::addDispatchRelatedMethods(const core::GlobalState &gs, const core::DispatchResult *dispatchResult, - shared_ptr methods) { +void AbstractRewriter::addDispatchRelatedMethods(const core::GlobalState &gs, + const core::DispatchResult *dispatchResult, + shared_ptr methods) { for (const core::DispatchResult *dr = dispatchResult; dr != nullptr; dr = dr->secondary.get()) { auto method = dr->main.method; ENFORCE(method.exists()); @@ -133,7 +134,7 @@ void AbstractRenamer::addDispatchRelatedMethods(const core::GlobalState &gs, con } } -void AbstractRenamer::getRenameEdits(LSPTypecheckerDelegate &typechecker, core::SymbolRef symbol, string newName) { +void AbstractRewriter::getEdits(LSPTypecheckerDelegate &typechecker, core::SymbolRef symbol) { const core::GlobalState &gs = typechecker.state(); auto originalName = symbol.name(gs).show(gs); diff --git a/main/lsp/AbstractRenamer.h b/main/lsp/AbstractRewriter.h similarity index 77% rename from main/lsp/AbstractRenamer.h rename to main/lsp/AbstractRewriter.h index 39d73c53f9..75aae38125 100644 --- a/main/lsp/AbstractRenamer.h +++ b/main/lsp/AbstractRewriter.h @@ -8,7 +8,7 @@ namespace sorbet::realmain::lsp { -class AbstractRenamer { +class AbstractRewriter { public: class UniqueSymbolQueue { public: @@ -20,17 +20,16 @@ class AbstractRenamer { UnorderedSet set; }; - AbstractRenamer(const core::GlobalState &gs, const sorbet::realmain::lsp::LSPConfiguration &config, - const std::string oldName, const std::string newName) - : gs(gs), config(config), oldName(oldName), newName(newName), invalid(false){}; + AbstractRewriter(const core::GlobalState &gs, const sorbet::realmain::lsp::LSPConfiguration &config) + : gs(gs), config(config), invalid(false){}; - virtual ~AbstractRenamer() = default; + virtual ~AbstractRewriter() = default; virtual void rename(std::unique_ptr &response, const core::SymbolRef originalSymbol) = 0; std::optional>> buildTextDocumentEdits(); std::variant> buildWorkspaceEdit(); virtual void addSymbol(const core::SymbolRef) = 0; - void getRenameEdits(LSPTypecheckerDelegate &typechecker, core::SymbolRef symbol, std::string newName); + void getEdits(LSPTypecheckerDelegate &typechecker, core::SymbolRef symbol); bool getInvalid(); std::string getError(); @@ -39,8 +38,6 @@ class AbstractRenamer { protected: const core::GlobalState &gs; const LSPConfiguration &config; - std::string oldName; - std::string newName; UnorderedMap edits; bool invalid; std::shared_ptr symbolQueue = std::make_shared(); diff --git a/main/lsp/BUILD b/main/lsp/BUILD index c2fc6cacb8..06d9749bdd 100644 --- a/main/lsp/BUILD +++ b/main/lsp/BUILD @@ -9,7 +9,7 @@ cc_library( "watchman/*.cc", ]) + [ ":lsp_messages", - "AbstractRenamer.h", + "AbstractRewriter.h", "DefLocSaver.h", "ErrorFlusherLSP.h", "ErrorReporter.h", @@ -30,6 +30,7 @@ cc_library( "watchman/WatchmanProcess.h", ], hdrs = [ + "ConvertToSingletonClassMethod.h", "LSPConfiguration.h", "LSPInput.h", "LSPLoop.h", @@ -143,8 +144,8 @@ cc_test( "lsp", "//payload", "//test/helpers", - "@doctest", - "@doctest//:doctest_main", + "@doctest//doctest", + "@doctest//doctest:main", ], ) @@ -161,8 +162,8 @@ cc_test( "lsp", "//payload", "//test/helpers", - "@doctest", - "@doctest//:doctest_main", + "@doctest//doctest", + "@doctest//doctest:main", ], ) @@ -179,8 +180,8 @@ cc_test( "lsp", "//payload", "//test/helpers", - "@doctest", - "@doctest//:doctest_main", + "@doctest//doctest", + "@doctest//doctest:main", ], ) @@ -197,8 +198,8 @@ cc_test( "lsp", "//payload", "//test/helpers", - "@doctest", - "@doctest//:doctest_main", + "@doctest//doctest", + "@doctest//doctest:main", ], ) @@ -215,7 +216,7 @@ cc_test( "lsp", "//payload", "//test/helpers", - "@doctest", - "@doctest//:doctest_main", + "@doctest//doctest", + "@doctest//doctest:main", ], ) diff --git a/main/lsp/ConvertToSingletonClassMethod.cc b/main/lsp/ConvertToSingletonClassMethod.cc new file mode 100644 index 0000000000..786c8fbbda --- /dev/null +++ b/main/lsp/ConvertToSingletonClassMethod.cc @@ -0,0 +1,208 @@ +#include "main/lsp/ConvertToSingletonClassMethod.h" +#include "absl/strings/match.h" +#include "main/lsp/AbstractRewriter.h" +#include "main/sig_finder/sig_finder.h" + +using namespace std; + +namespace sorbet::realmain::lsp { + +namespace { + +unique_ptr createMethodDefEdit(const core::GlobalState &gs, LSPTypecheckerDelegate &typechecker, + const LSPConfiguration &config, + const core::lsp::MethodDefResponse &definition) { + const auto &maybeSource = definition.termLoc.source(gs); + if (!maybeSource.has_value()) { + return nullptr; + } + const auto &source = maybeSource.value(); + if (!absl::StartsWith(source, "def ")) { + // Maybe this is an attr_reader or a prop or something. Abort. + return nullptr; + } + auto file = definition.termLoc.file(); + + auto shortName = definition.name.shortName(gs); + auto insertAt = source.find(shortName); + if (insertAt == string::npos) { + return nullptr; + } + + auto insertSelfLoc = definition.termLoc.adjustLen(gs, insertAt, 0); + if (!insertSelfLoc.exists()) { + return nullptr; + } + + auto insertParamLoc = definition.termLoc.adjustLen(gs, insertAt + shortName.size(), 0); + if (!insertParamLoc.exists()) { + return nullptr; + } + bool needsParens = true; + if (insertParamLoc.adjustLen(gs, 0, 1).source(gs) == "(") { + insertParamLoc = insertParamLoc.adjustLen(gs, 1, 0); + needsParens = false; + } + + auto insertSelfRange = Range::fromLoc(gs, insertSelfLoc); + ENFORCE(insertSelfRange != nullptr); + auto insertParamRange = Range::fromLoc(gs, insertParamLoc); + ENFORCE(insertParamRange != nullptr); + + auto uri = config.fileRef2Uri(gs, definition.termLoc.file()); + auto tdi = make_unique(move(uri), JSONNullObject()); + vector> edits; + edits.emplace_back(make_unique(move(insertSelfRange), "self.")); + if (needsParens) { + edits.emplace_back(make_unique(move(insertParamRange), "(this)")); + } else if (definition.symbol.data(gs)->arguments.empty()) { + edits.emplace_back(make_unique(move(insertParamRange), "this")); + } else { + edits.emplace_back(make_unique(move(insertParamRange), "this, ")); + } + + if (definition.symbol.data(gs)->hasSig()) { + auto trees = typechecker.getResolved({file}); + ENFORCE(!trees.empty()); + auto &rootTree = trees[0].tree; + + auto ctx = core::Context(gs, core::Symbols::root(), file); + auto queryLoc = definition.termLoc.copyWithZeroLength(); + auto parsedSig = sig_finder::SigFinder::findSignature(ctx, rootTree, queryLoc); + if (parsedSig.has_value()) { + if (!parsedSig->argTypes.empty()) { + auto firstArgLoc = parsedSig->argTypes[0].nameLoc; + auto insertSigParamRange = Range::fromLoc(gs, firstArgLoc.adjustLen(gs, 0, 0)); + auto sigParamText = fmt::format("this: {}, ", definition.symbol.data(gs)->owner.show(gs)); + edits.emplace_back(make_unique(move(insertSigParamRange), move(sigParamText))); + } else if (parsedSig->returnsLoc.exists()) { + auto insertSigParamsRange = Range::fromLoc(gs, parsedSig->returnsLoc.adjustLen(gs, 0, 0)); + auto sigParamText = fmt::format("params(this: {}).", definition.symbol.data(gs)->owner.show(gs)); + edits.emplace_back(make_unique(move(insertSigParamsRange), move(sigParamText))); + } + } + } + + return make_unique(move(tdi), move(edits)); +} + +class MethodCallSiteRewriter : public AbstractRewriter { + core::MethodRef method; + core::ClassOrModuleRef owner; + +public: + MethodCallSiteRewriter(const core::GlobalState &gs, const LSPConfiguration &config, core::MethodRef method) + : AbstractRewriter(gs, config), method(method), owner(method.data(gs)->owner) {} + ~MethodCallSiteRewriter() {} + + void rename(unique_ptr &response, const core::SymbolRef originalSymbol) override { + if (invalid) { + return; + } + + auto sendResp = response->isSend(); + if (sendResp == nullptr || !sendResp->receiverLocOffsets.exists()) { + return; + } + + if (sendResp->callerSideName == core::Names::callWithSplat() || + sendResp->callerSideName == core::Names::callWithBlock() || + sendResp->callerSideName == core::Names::callWithSplatAndBlock()) { + // These are too hard... skipping for the time being. + return; + } + + if (sendResp->dispatchResult->secondary != nullptr) { + // If the call site is not trivial, don't attepmt to rename. + // The type check error will inform the user that this needs to be fixed manually. + return; + } + + auto replaceLoc = sendResp->termLoc(); + ENFORCE(edits.find(replaceLoc) == edits.end(), "Tried to edit the same call site twice..."); + + fmt::memory_buffer buf; + fmt::format_to(back_inserter(buf), "{}.{}", owner.show(gs), sendResp->callerSideName.show(gs)); + auto receiverSource = sendResp->isPrivateOk ? "self"sv : sendResp->receiverLoc().source(gs).value(); + if (sendResp->argLocOffsets.empty()) { + fmt::format_to(back_inserter(buf), "({})", receiverSource); + } else { + auto file = sendResp->file; + auto firstArgLoc = core::Loc(file, sendResp->argLocOffsets.front()); + auto lastArgLoc = core::Loc(file, sendResp->argLocOffsets.back()); + if (!firstArgLoc.exists() || !lastArgLoc.exists()) { + return; + } + auto argSource = firstArgLoc.join(lastArgLoc).source(gs).value(); + fmt::format_to(back_inserter(buf), "({}, {})", receiverSource, argSource); + } + + if (sendResp->dispatchResult->main.blockPreType != nullptr) { + auto blockLocStart = sendResp->argLocOffsets.empty() + ? sendResp->funLocOffsets.copyEndWithZeroLength() + : sendResp->argLocOffsets.back().copyEndWithZeroLength(); + auto blockLocEnd = sendResp->termLocOffsets.copyEndWithZeroLength(); + auto blockLoc = core::Loc(sendResp->file, blockLocStart.join(blockLocEnd)); + if (auto maybeBlockSource = blockLoc.source(gs)) { + string_view blockSource = (!maybeBlockSource->empty() && absl::StartsWith(*maybeBlockSource, "()")) + ? maybeBlockSource->substr(2) + : *maybeBlockSource; + fmt::format_to(back_inserter(buf), "{}", blockSource); + } + } + + edits[replaceLoc] = to_string(buf); + } + + void addSymbol(const core::SymbolRef symbol) override { + if (!symbol.isMethod()) { + return; + } + getQueue()->tryEnqueue(symbol); + + // This doesn't make any attempt to handle methods that are overridden. + // + // Technically speaking, this code action doesn't make a ton of sense if the method is + // overriden, because converting to a singleton method will kill dynamic dispatch. + // + // We have two options: + // 1. Assume that there are no overrides of this method. + // Pro: no additional work + // Con: might not catch some callsites on child methods + // 2. Detect when there are overrides of this method, and mark the code action `invalid` with an error + // Pro: "safer" because we can error instead of silently changing the meaning of the program + // Con: requires a full scan of the symbol table + // + // For the time being, we're taking option (1). + // Option (2) would involve a call to addSubclassRelatedMethods or something similar here. + } +}; + +} // namespace + +vector> convertToSingletonClassMethod(LSPTypecheckerDelegate &typechecker, + const LSPConfiguration &config, + const core::lsp::MethodDefResponse &definition) { + auto &gs = typechecker.state(); + + auto methodDefEdit = createMethodDefEdit(gs, typechecker, config, definition); + if (methodDefEdit == nullptr) { + config.logger->error("Failed to createMethodDefEdit for convertToSingletonClassMethod"); + return {}; + } + + auto renamer = make_shared(gs, config, definition.symbol); + renamer->getEdits(typechecker, definition.symbol); + auto callSiteEdits = renamer->buildTextDocumentEdits(); + if (!callSiteEdits.has_value()) { + config.logger->error("Failed to buildTextDocumentEdits for convertToSingletonClassMethod"); + return {}; + } + + auto res = move(callSiteEdits.value()); + res.emplace_back(move(methodDefEdit)); + + return res; +} + +} // namespace sorbet::realmain::lsp diff --git a/main/lsp/ConvertToSingletonClassMethod.h b/main/lsp/ConvertToSingletonClassMethod.h new file mode 100644 index 0000000000..b6884a53e7 --- /dev/null +++ b/main/lsp/ConvertToSingletonClassMethod.h @@ -0,0 +1,17 @@ +#ifndef SORBET_CONVERT_TO_SINGLETON_CLASS_METHOD_H +#define SORBET_CONVERT_TO_SINGLETON_CLASS_METHOD_H + +#include "main/lsp/LSPConfiguration.h" +#include "main/lsp/LSPTypechecker.h" +#include "main/lsp/json_types.h" + +namespace sorbet::realmain::lsp { + +// Returns an empty vector when there was an error. +std::vector> +convertToSingletonClassMethod(LSPTypecheckerDelegate &typechecker, const LSPConfiguration &config, + const core::lsp::MethodDefResponse &definition); + +} // namespace sorbet::realmain::lsp + +#endif diff --git a/main/lsp/DefLocSaver.cc b/main/lsp/DefLocSaver.cc index a5d22d707b..a39b85c2aa 100644 --- a/main/lsp/DefLocSaver.cc +++ b/main/lsp/DefLocSaver.cc @@ -35,8 +35,8 @@ void DefLocSaver::postTransformMethodDef(core::Context ctx, ast::ExpressionPtr & } tp.origins.emplace_back(ctx.locAt(localExp->loc)); core::lsp::QueryResponse::pushQueryResponse( - ctx, - core::lsp::IdentResponse(ctx.locAt(localExp->loc), localExp->localVariable, tp, methodDef.symbol)); + ctx, core::lsp::IdentResponse(ctx.locAt(localExp->loc), localExp->localVariable, tp, + methodDef.symbol, ctx.locAt(methodDef.loc))); return; } } diff --git a/main/lsp/ErrorReporter.cc b/main/lsp/ErrorReporter.cc index 23933304c1..c304866562 100644 --- a/main/lsp/ErrorReporter.cc +++ b/main/lsp/ErrorReporter.cc @@ -155,7 +155,11 @@ void ErrorReporter::pushDiagnostics(uint32_t epoch, core::FileRef file, const ve tags.push_back(DiagnosticTag::Unnecessary); diagnostic->tags = move(tags); } + diagnostic->severity = DiagnosticSeverity::Error; + if (error->what == sorbet::core::errors::Infer::UntypedValueInformation) { + diagnostic->severity = DiagnosticSeverity::Information; + } if (!error->autocorrects.empty()) { diagnostic->message += " (fix available)"; diff --git a/main/lsp/LSPConfiguration.cc b/main/lsp/LSPConfiguration.cc index 1fd2600cb9..0575004dc0 100644 --- a/main/lsp/LSPConfiguration.cc +++ b/main/lsp/LSPConfiguration.cc @@ -103,6 +103,7 @@ LSPClientConfiguration::LSPClientConfiguration(const InitializeParams ¶ms) { enableOperationNotifications = initOptions->supportsOperationNotifications.value_or(false); enableTypecheckInfo = initOptions->enableTypecheckInfo.value_or(false); enableSorbetURIs = initOptions->supportsSorbetURIs.value_or(false); + enableHighlightUntyped = initOptions->highlightUntyped.value_or(false); } } diff --git a/main/lsp/LSPConfiguration.h b/main/lsp/LSPConfiguration.h index 7668ee06ac..683c0e84b6 100644 --- a/main/lsp/LSPConfiguration.h +++ b/main/lsp/LSPConfiguration.h @@ -33,6 +33,10 @@ class LSPClientConfiguration final { bool enableSorbetURIs = false; /** If true, then LSP sends metadata to the client every time it typechecks files. Used in tests. */ bool enableTypecheckInfo = false; + + /** If true, then LSP outputs a warning for untyped values */ + bool enableHighlightUntyped = false; + /** * Whether or not the active client has support for snippets in CompletionItems. * Note: There is a generated ClientCapabilities class, but it is cumbersome to work with as most fields are diff --git a/main/lsp/LSPFileUpdates.cc b/main/lsp/LSPFileUpdates.cc index 4f857b7f68..9160a18cb5 100644 --- a/main/lsp/LSPFileUpdates.cc +++ b/main/lsp/LSPFileUpdates.cc @@ -50,37 +50,6 @@ LSPFileUpdates LSPFileUpdates::copy() const { return copy; } -namespace { - -// In debug builds, asserts that we have not accidentally taken the fast path after a change to the set of -// methods in a file. -bool validateIdenticalFingerprints(const std::vector &a, const std::vector &b) { - if (a.size() != b.size()) { - return false; - } - - core::SymbolHash previousHash; // Initializes to <0, 0>. - auto bIt = b.begin(); - for (const auto &methodA : a) { - const auto &methodB = *bIt; - if (methodA.nameHash != methodB.nameHash) { - return false; - } - - // Enforce that hashes are sorted in ascending order. - if (methodA < previousHash) { - return false; - } - - previousHash = methodA; - bIt++; - } - - return true; -} - -} // namespace - LSPFileUpdates::FastPathFilesToTypecheckResult LSPFileUpdates::fastPathFilesToTypecheck(const core::GlobalState &gs, const LSPConfiguration &config, const vector> &updatedFiles, @@ -124,12 +93,6 @@ LSPFileUpdates::fastPathFilesToTypecheck(const core::GlobalState &gs, const LSPC const auto &oldRetypecheckableSymbolHashes = oldLocalSymbolTableHashes.retypecheckableSymbolHashes; const auto &newRetypecheckableSymbolHashes = newLocalSymbolTableHashes.retypecheckableSymbolHashes; - if (!config.opts.lspExperimentalFastPathEnabled) { - // Both oldHash and newHash should have the same methods, since this is the fast path! - ENFORCE(validateIdenticalFingerprints(oldRetypecheckableSymbolHashes, newRetypecheckableSymbolHashes), - "definitionHash should have failed"); - } - // Find which hashes changed. Note: retypecheckableSymbolHashes are pre-sorted, so set_difference should work. // This will insert two entries into `retypecheckableSymbolHashes` for each changed method, but they // will get deduped later. diff --git a/main/lsp/LSPIndexer.cc b/main/lsp/LSPIndexer.cc index e2eff59547..2cd6654f87 100644 --- a/main/lsp/LSPIndexer.cc +++ b/main/lsp/LSPIndexer.cc @@ -132,8 +132,6 @@ LSPIndexer::getTypecheckingPathInternal(const vector> &ch // Also record some information about what might have changed. const bool classesDiffer = newHash.localSymbolTableHashes.classModuleHash != oldHash.localSymbolTableHashes.classModuleHash; - const bool typeArgumentsDiffer = - newHash.localSymbolTableHashes.typeArgumentHash != oldHash.localSymbolTableHashes.typeArgumentHash; const bool typeMembersDiffer = newHash.localSymbolTableHashes.typeMemberHash != oldHash.localSymbolTableHashes.typeMemberHash; const bool fieldsDiffer = @@ -144,15 +142,11 @@ LSPIndexer::getTypecheckingPathInternal(const vector> &ch newHash.localSymbolTableHashes.classAliasHash != oldHash.localSymbolTableHashes.classAliasHash; const bool methodsDiffer = newHash.localSymbolTableHashes.methodHash != oldHash.localSymbolTableHashes.methodHash; - const uint32_t differCount = int(classesDiffer) + int(typeArgumentsDiffer) + int(typeMembersDiffer) + - int(fieldsDiffer) + int(staticFieldsDiffer) + int(classAliasesDiffer) + - int(methodsDiffer); + const uint32_t differCount = int(classesDiffer) + int(typeMembersDiffer) + int(fieldsDiffer) + + int(staticFieldsDiffer) + int(classAliasesDiffer) + int(methodsDiffer); if (classesDiffer) { prodCategoryCounterInc("lsp.slow_path_changed_def", "classmodule"); } - if (typeArgumentsDiffer) { - prodCategoryCounterInc("lsp.slow_path_changed_def", "typeargument"); - } if (typeMembersDiffer) { prodCategoryCounterInc("lsp.slow_path_changed_def", "typemember"); } @@ -171,8 +165,6 @@ LSPIndexer::getTypecheckingPathInternal(const vector> &ch if (differCount == 1) { if (classesDiffer) { prodCategoryCounterInc("lsp.slow_path_changed_def", "onlyclassmodule"); - } else if (typeArgumentsDiffer) { - prodCategoryCounterInc("lsp.slow_path_changed_def", "onlytypeargument"); } else if (typeMembersDiffer) { prodCategoryCounterInc("lsp.slow_path_changed_def", "onlytypemembers"); } else if (fieldsDiffer) { diff --git a/main/lsp/LSPLoop.cc b/main/lsp/LSPLoop.cc index 9c30725589..fd8685ca0c 100644 --- a/main/lsp/LSPLoop.cc +++ b/main/lsp/LSPLoop.cc @@ -2,10 +2,10 @@ #include "absl/synchronization/mutex.h" #include "absl/synchronization/notification.h" #include "common/EarlyReturnWithCode.h" -#include "common/Timer.h" #include "common/concurrency/WorkerPool.h" #include "common/kvstore/KeyValueStore.h" #include "common/statsd/statsd.h" +#include "common/timers/Timer.h" #include "common/web_tracer_framework/tracing.h" #include "core/errors/internal.h" #include "core/errors/namer.h" @@ -38,7 +38,13 @@ LSPLoop::LSPLoop(std::unique_ptr initialGS, WorkerPool &worke constexpr chrono::minutes STATSD_INTERVAL = chrono::minutes(5); bool LSPLoop::shouldSendCountersToStatsd(chrono::time_point currentTime) const { - return !config->opts.statsdHost.empty() && (currentTime - lastMetricUpdateTime) > STATSD_INTERVAL; + // If --web-trace-file, always flush after every task (probably: someone is debugging). + // Otherwise, batch up connections to hitting statsd (probably: normal mode of operation). + // Note: passing --web-trace-file will override the "only send every STATSD_INTERVAL" for + // statsd reporting. So it's *likely* bad to pass all of `--lsp`, `--statsd-host`, and + // `--web-trace-file` at the same time. + return !config->opts.webTraceFile.empty() || + (!config->opts.statsdHost.empty() && (currentTime - lastMetricUpdateTime) > STATSD_INTERVAL); } void LSPLoop::sendCountersToStatsd(chrono::time_point currentTime) { diff --git a/main/lsp/LSPMessage.h b/main/lsp/LSPMessage.h index e8d612b841..26cbf4ad35 100644 --- a/main/lsp/LSPMessage.h +++ b/main/lsp/LSPMessage.h @@ -1,8 +1,8 @@ #ifndef RUBY_TYPER_LSP_LSPMESSAGE_H #define RUBY_TYPER_LSP_LSPMESSAGE_H -#include "common/Timer.h" #include "common/common.h" +#include "common/timers/Timer.h" #include "main/lsp/json_enums.h" #include "rapidjson/document.h" #include "rapidjson/stringbuffer.h" diff --git a/main/lsp/LSPQuery.cc b/main/lsp/LSPQuery.cc index bed28c8a59..86cb4296c0 100644 --- a/main/lsp/LSPQuery.cc +++ b/main/lsp/LSPQuery.cc @@ -1,5 +1,5 @@ #include "main/lsp/LSPQuery.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "core/FileHash.h" #include "main/lsp/json_types.h" diff --git a/main/lsp/LSPTask.cc b/main/lsp/LSPTask.cc index cac8ad8fa7..136b9c5606 100644 --- a/main/lsp/LSPTask.cc +++ b/main/lsp/LSPTask.cc @@ -1,7 +1,7 @@ #include "main/lsp/LSPTask.h" #include "absl/strings/match.h" #include "absl/synchronization/notification.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "core/FileHash.h" #include "core/lsp/QueryResponse.h" #include "main/lsp/LSPLoop.h" @@ -280,7 +280,7 @@ LSPTask::extractLocations(const core::GlobalState &gs, auto queryResponsesFiltered = LSPQuery::filterAndDedup(gs, queryResponses); for (auto &q : queryResponsesFiltered) { if (auto *send = q->isSend()) { - addLocIfExists(gs, locations, send->funLoc); + addLocIfExists(gs, locations, send->funLoc()); } else { addLocIfExists(gs, locations, q->getLoc()); } diff --git a/main/lsp/LSPTask.h b/main/lsp/LSPTask.h index 0fff78961e..498aec0f4e 100644 --- a/main/lsp/LSPTask.h +++ b/main/lsp/LSPTask.h @@ -1,7 +1,7 @@ #ifndef RUBY_TYPER_LSPTASK_H #define RUBY_TYPER_LSPTASK_H -#include "main/lsp/AbstractRenamer.h" +#include "main/lsp/AbstractRewriter.h" #include "main/lsp/LSPMessage.h" #include "main/lsp/LSPTypechecker.h" #include "main/lsp/json_types.h" diff --git a/main/lsp/LSPTypechecker.cc b/main/lsp/LSPTypechecker.cc index 3972d9544d..2dea209448 100644 --- a/main/lsp/LSPTypechecker.cc +++ b/main/lsp/LSPTypechecker.cc @@ -4,7 +4,7 @@ #include "absl/synchronization/notification.h" #include "ast/treemap/treemap.h" #include "common/concurrency/ConcurrentQueue.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "core/ErrorCollector.h" #include "core/ErrorQueue.h" #include "core/NullFlusher.h" @@ -27,6 +27,7 @@ #include "main/lsp/notifications/indexer_initialization.h" #include "main/lsp/notifications/sorbet_resume.h" #include "main/pipeline/pipeline.h" +#include "main/sig_finder/sig_finder.h" namespace sorbet::realmain::lsp { using namespace std; @@ -63,7 +64,8 @@ LSPTypechecker::LSPTypechecker(std::shared_ptr config, LSPTypechecker::~LSPTypechecker() {} void LSPTypechecker::initialize(TaskQueue &queue, std::unique_ptr initialGS, - std::unique_ptr kvstore, WorkerPool &workers) { + std::unique_ptr kvstore, WorkerPool &workers, + const LSPConfiguration ¤tConfig) { ENFORCE(this_thread::get_id() == typecheckerThreadId, "Typechecker can only be used from the typechecker thread."); ENFORCE(!this->initialized); @@ -71,6 +73,7 @@ void LSPTypechecker::initialize(TaskQueue &queue, std::unique_ptrtrackUntyped = currentConfig.getClientConfig().enableHighlightUntyped; // Temporarily replace error queue, as it asserts that the same thread that created it uses it and we're // going to use it on typechecker thread for this one operation. auto savedErrorQueue = initialGS->errorQueue; @@ -217,8 +220,7 @@ vector LSPTypechecker::runFastPath(LSPFileUpdates &updates, Worke config->logger->debug("Added {} files that were not part of the edit to the update set", result.extraFiles.size()); UnorderedMap oldFoundHashesForFiles; auto toTypecheck = move(result.extraFiles); - auto shouldRunIncrementalNamer = - config->opts.lspExperimentalFastPathEnabled && !result.changedSymbolNameHashes.empty(); + auto shouldRunIncrementalNamer = !result.changedSymbolNameHashes.empty(); for (auto [fref, idx] : result.changedFiles) { if (shouldRunIncrementalNamer) { // Only set oldFoundHashesForFiles if we're processing a real edit. @@ -246,28 +248,40 @@ vector LSPTypechecker::runFastPath(LSPFileUpdates &updates, Worke fref.data(*gs).strictLevel = pipeline::decideStrictLevel(*gs, fref, config->opts); toTypecheck.emplace_back(fref); + } - // Only need to re-run packager if we're going to delete constants and have to re-define - // their visibility, which only happens if we're running incrementalNamer. - if (shouldRunIncrementalNamer) { - // TODO(jez) Using `gs` to access package information here assumes that edits to - // __package.rb files don't take the fast path. We'll want (or maybe need) to revisit this - // when we start making edits to `__package.rb` take fast paths. + UnorderedSet packageFiles; + + if (shouldRunIncrementalNamer) { + for (auto fref : toTypecheck) { + // Only need to re-run packager if we're going to delete constants and have to re-define + // their visibility, which only happens if we're running incrementalNamer. + // NOTE: Using `gs` to access package information here assumes that edits to __package.rb + // files don't take the fast path. We'll want (or maybe need) to revisit this when we start + // making edits to `__package.rb` take fast paths. if (!(fref.data(*gs).isPackage())) { - auto pkgName = gs->packageDB().getPackageNameForFile(fref); - if (pkgName.exists()) { + auto &pkg = gs->packageDB().getPackageForFile(*gs, fref); + if (pkg.exists()) { // Since even no-op (e.g. whitespace-only) edits will cause constants to be deleted // and re-added, we have to add the __package.rb files to set of files to retypecheck // so that we can re-run PropagateVisibility to set export bits for any constants. - auto packageFref = gs->packageDB().getPackageInfo(pkgName).fullLoc().file(); - if (result.changedFiles.find(packageFref) == result.changedFiles.end()) { - // Skip duplicates - toTypecheck.emplace_back(packageFref); + auto packageFref = pkg.fullLoc().file(); + if (!packageFref.exists()) { + continue; } + + packageFiles.emplace(packageFref); } } } } + + for (auto packageFref : packageFiles) { + if (result.changedFiles.find(packageFref) == result.changedFiles.end()) { + toTypecheck.emplace_back(packageFref); + } + } + fast_sort(toTypecheck); config->logger->debug("Running fast path over num_files={}", toTypecheck.size()); @@ -453,10 +467,6 @@ bool LSPTypechecker::runSlowPath(LSPFileUpdates updates, WorkerPool &workers, return; } - auto &resolved = maybeResolved.result(); - for (auto &tree : resolved) { - ENFORCE(tree.file.exists()); - } if (gs->sleepInSlowPathSeconds.has_value()) { auto sleepDuration = gs->sleepInSlowPathSeconds.value(); for (int i = 0; i < sleepDuration * 10; i++) { @@ -507,7 +517,7 @@ bool LSPTypechecker::runSlowPath(LSPFileUpdates updates, WorkerPool &workers, return; } - auto sorted = sortParsedFiles(*gs, *errorReporter, move(resolved)); + auto sorted = sortParsedFiles(*gs, *errorReporter, move(maybeResolved.result())); const auto presorted = true; pipeline::typecheck(*gs, move(sorted), config->opts, workers, cancelable, preemptManager, presorted); }); @@ -582,8 +592,15 @@ void tryApplyLocalVarSaver(const core::GlobalState &gs, vector return; } for (auto &t : indexedCopies) { - LocalVarSaver localVarSaver; - core::Context ctx(gs, core::Symbols::root(), t.file); + optional signature; + auto ctx = core::Context(gs, core::Symbols::root(), t.file); + if (t.file == gs.lspQuery.loc.file()) { + // For a VAR query, gs.lspQuery.loc is the enclosing MethodDef's loc, which we can use + // to find the signature before that MethodDef. + auto queryLoc = gs.lspQuery.loc.copyWithZeroLength(); + signature = sig_finder::SigFinder::findSignature(ctx, t.tree, queryLoc); + } + LocalVarSaver localVarSaver(ctx.locAt(t.tree.loc()), move(signature)); ast::TreeWalk::apply(ctx, localVarSaver, t.tree); } } @@ -702,8 +719,8 @@ LSPTypecheckerDelegate::LSPTypecheckerDelegate(TaskQueue &queue, WorkerPool &wor : typechecker(typechecker), queue{queue}, workers(workers) {} void LSPTypecheckerDelegate::initialize(InitializedTask &task, std::unique_ptr gs, - std::unique_ptr kvstore) { - return typechecker.initialize(this->queue, std::move(gs), std::move(kvstore), this->workers); + std::unique_ptr kvstore, const LSPConfiguration ¤tConfig) { + return typechecker.initialize(this->queue, std::move(gs), std::move(kvstore), this->workers, currentConfig); } void LSPTypecheckerDelegate::resumeTaskQueue(InitializedTask &task) { diff --git a/main/lsp/LSPTypechecker.h b/main/lsp/LSPTypechecker.h index b6d3610bab..262a3d6e73 100644 --- a/main/lsp/LSPTypechecker.h +++ b/main/lsp/LSPTypechecker.h @@ -95,7 +95,7 @@ class LSPTypechecker final { * Writes all diagnostic messages to LSPOutput. */ void initialize(TaskQueue &queue, std::unique_ptr gs, std::unique_ptr kvstore, - WorkerPool &workers); + WorkerPool &workers, const LSPConfiguration ¤tConfig); /** * Typechecks the given input. Returns 'true' if the updates were committed, or 'false' if typechecking was @@ -172,7 +172,7 @@ class LSPTypecheckerDelegate final { virtual ~LSPTypecheckerDelegate() = default; void initialize(InitializedTask &task, std::unique_ptr gs, - std::unique_ptr kvstore); + std::unique_ptr kvstore, const LSPConfiguration ¤tConfig); void resumeTaskQueue(InitializedTask &task); diff --git a/main/lsp/LocalVarSaver.cc b/main/lsp/LocalVarSaver.cc index 5b59c185f0..51a1d51ef6 100644 --- a/main/lsp/LocalVarSaver.cc +++ b/main/lsp/LocalVarSaver.cc @@ -31,7 +31,8 @@ void LocalVarSaver::postTransformBlock(core::Context ctx, ast::ExpressionPtr &tr if (lspQueryMatch) { core::TypeAndOrigins tp; core::lsp::QueryResponse::pushQueryResponse( - ctx, core::lsp::IdentResponse(ctx.locAt(localExp->loc), localExp->localVariable, tp, method)); + ctx, core::lsp::IdentResponse(ctx.locAt(localExp->loc), localExp->localVariable, tp, method, + this->enclosingMethodDefLoc.back())); } } } @@ -46,12 +47,18 @@ void LocalVarSaver::postTransformLocal(core::Context ctx, ast::ExpressionPtr &tr // No need for type information; this is for a reference request. // Let the default constructor make tp.type an empty shared_ptr and tp.origins an empty vector core::TypeAndOrigins tp; - core::lsp::QueryResponse::pushQueryResponse( - ctx, core::lsp::IdentResponse(ctx.locAt(local.loc), local.localVariable, tp, method)); + core::lsp::QueryResponse::pushQueryResponse(ctx, core::lsp::IdentResponse(ctx.locAt(local.loc), + local.localVariable, tp, method, + this->enclosingMethodDefLoc.back())); } } +void LocalVarSaver::preTransformMethodDef(core::Context ctx, ast::ExpressionPtr &tree) { + this->enclosingMethodDefLoc.emplace_back(ctx.locAt(tree.loc())); +} + void LocalVarSaver::postTransformMethodDef(core::Context ctx, ast::ExpressionPtr &tree) { + this->enclosingMethodDefLoc.pop_back(); auto &methodDef = ast::cast_tree_nonnull(tree); // Check args. @@ -60,11 +67,22 @@ void LocalVarSaver::postTransformMethodDef(core::Context ctx, ast::ExpressionPtr if (auto *localExp = ast::MK::arg2Local(arg)) { bool lspQueryMatch = ctx.state.lspQuery.matchesVar(methodDef.symbol, localExp->localVariable); if (lspQueryMatch) { - // (Ditto) + auto methodDefLoc = ctx.locAt(methodDef.loc); core::TypeAndOrigins tp; core::lsp::QueryResponse::pushQueryResponse( - ctx, - core::lsp::IdentResponse(ctx.locAt(localExp->loc), localExp->localVariable, tp, methodDef.symbol)); + ctx, core::lsp::IdentResponse(ctx.locAt(localExp->loc), localExp->localVariable, tp, + methodDef.symbol, methodDefLoc)); + + if (this->signature.has_value()) { + auto it = absl::c_find_if(this->signature->argTypes, [&](const auto &argSpec) { + return argSpec.name == ctx.state.lspQuery.variable._name; + }); + if (it != this->signature->argTypes.end()) { + core::lsp::QueryResponse::pushQueryResponse( + ctx, core::lsp::IdentResponse(it->nameLoc, localExp->localVariable, tp, methodDef.symbol, + methodDefLoc)); + } + } } } } diff --git a/main/lsp/LocalVarSaver.h b/main/lsp/LocalVarSaver.h index 233270eea2..d3e981185d 100644 --- a/main/lsp/LocalVarSaver.h +++ b/main/lsp/LocalVarSaver.h @@ -4,13 +4,21 @@ #include "ast/ast.h" #include "common/common.h" #include "core/core.h" +#include "main/sig_finder/sig_finder.h" namespace sorbet::realmain::lsp { class LocalVarSaver { + std::vector enclosingMethodDefLoc; + std::optional signature; + public: + LocalVarSaver(core::Loc rootLoc, std::optional &&signature) + : enclosingMethodDefLoc({rootLoc}), signature(move(signature)) {} + void postTransformBlock(core::Context ctx, ast::ExpressionPtr &local); void postTransformLocal(core::Context ctx, ast::ExpressionPtr &local); + void preTransformMethodDef(core::Context ctx, ast::ExpressionPtr &methodDef); void postTransformMethodDef(core::Context ctx, ast::ExpressionPtr &methodDef); }; }; // namespace sorbet::realmain::lsp diff --git a/main/lsp/MoveMethod.cc b/main/lsp/MoveMethod.cc index d7c2760837..58b7074cd5 100644 --- a/main/lsp/MoveMethod.cc +++ b/main/lsp/MoveMethod.cc @@ -1,5 +1,5 @@ #include "main/lsp/MoveMethod.h" -#include "main/lsp/AbstractRenamer.h" +#include "main/lsp/AbstractRewriter.h" #include "main/sig_finder/sig_finder.h" using namespace std; @@ -102,11 +102,14 @@ optional getNewModuleName(const core::GlobalState &gs, const core::NameR return nullopt; } -class MethodCallSiteRenamer : public AbstractRenamer { +class MethodCallSiteRenamer : public AbstractRewriter { + string oldName; + string newName; + public: MethodCallSiteRenamer(const core::GlobalState &gs, const LSPConfiguration &config, const string oldName, const string newName) - : AbstractRenamer(gs, config, oldName, newName) { + : AbstractRewriter(gs, config), oldName(oldName), newName(newName) { const vector invalidNames = {"initialize", "call"}; for (auto name : invalidNames) { if (oldName == name) { @@ -135,7 +138,6 @@ class MethodCallSiteRenamer : public AbstractRenamer { if (!source.has_value()) { return; } - string newsrc; if (auto sendResp = response->isSend()) { // if the call site is not trivial, don't attempt to rename // the typecheck error will guide user how to fix it @@ -145,7 +147,7 @@ class MethodCallSiteRenamer : public AbstractRenamer { } } - edits[sendResp->receiverLoc] = newName; + edits[sendResp->receiverLoc()] = newName; } } void addSymbol(const core::SymbolRef symbol) override { @@ -199,19 +201,6 @@ vector> moveMethod(LSPTypecheckerDelegate &typechecker, con } // namespace -unique_ptr getNewModuleLocation(const core::GlobalState &gs, const core::lsp::MethodDefResponse &definition, - LSPTypecheckerDelegate &typechecker) { - auto fref = definition.termLoc.file(); - - auto trees = typechecker.getResolved({fref}); - ENFORCE(!trees.empty()); - auto &rootTree = trees[0].tree; - auto insertPosition = Range::fromLoc(gs, core::Loc(fref, rootTree.loc().copyWithZeroLength())); - auto newModuleSymbol = insertPosition->start->copy(); - newModuleSymbol->character += moduleKeyword.size() + 1; - return newModuleSymbol; -} - vector> getMoveMethodEdits(LSPTypecheckerDelegate &typechecker, const LSPConfiguration &config, const core::lsp::MethodDefResponse &definition) { @@ -225,7 +214,7 @@ vector> getMoveMethodEdits(LSPTypecheckerDelegate & auto edits = moveMethod(typechecker, config, definition, newModuleName.value()); auto renamer = make_shared(gs, config, definition.name.show(gs), newModuleName.value()); - renamer->getRenameEdits(typechecker, definition.symbol, newModuleName.value()); + renamer->getEdits(typechecker, definition.symbol); auto callSiteEdits = renamer->buildTextDocumentEdits(); if (callSiteEdits.has_value()) { diff --git a/main/lsp/MoveMethod.h b/main/lsp/MoveMethod.h index 5df94ec014..ad2c3f967b 100644 --- a/main/lsp/MoveMethod.h +++ b/main/lsp/MoveMethod.h @@ -11,10 +11,6 @@ std::vector> getMoveMethodEdits(LSPTypechecker const LSPConfiguration &config, const core::lsp::MethodDefResponse &definition); -std::unique_ptr getNewModuleLocation(const core::GlobalState &gs, - const core::lsp::MethodDefResponse &definition, - LSPTypecheckerDelegate &typechecker); - } // namespace sorbet::realmain::lsp #endif diff --git a/main/lsp/UndoState.cc b/main/lsp/UndoState.cc index cacbce5fae..2561cd59e4 100644 --- a/main/lsp/UndoState.cc +++ b/main/lsp/UndoState.cc @@ -1,5 +1,5 @@ #include "main/lsp/UndoState.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "main/lsp/LSPConfiguration.h" #include "main/lsp/LSPMessage.h" #include "main/lsp/LSPOutput.h" diff --git a/main/lsp/json_types.h b/main/lsp/json_types.h index d287c806e4..3fff6ee9c0 100644 --- a/main/lsp/json_types.h +++ b/main/lsp/json_types.h @@ -1,8 +1,8 @@ #ifndef RUBY_TYPER_LSP_JSON_TYPES_H #define RUBY_TYPER_LSP_JSON_TYPES_H -#include "common/Timer.h" #include "common/common.h" +#include "common/timers/Timer.h" #include "core/core.h" #include "main/lsp/json_enums.h" #include "rapidjson/document.h" diff --git a/main/lsp/lsp_helpers.cc b/main/lsp/lsp_helpers.cc index 8e3b767e55..3122529f6d 100644 --- a/main/lsp/lsp_helpers.cc +++ b/main/lsp/lsp_helpers.cc @@ -3,7 +3,7 @@ #include "absl/strings/str_join.h" #include "absl/strings/str_split.h" #include "common/FileOps.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "main/lsp/LSPLoop.h" #include "main/lsp/json_types.h" diff --git a/main/lsp/notifications/initialized.cc b/main/lsp/notifications/initialized.cc index cbe62f33fb..6d79812a22 100644 --- a/main/lsp/notifications/initialized.cc +++ b/main/lsp/notifications/initialized.cc @@ -24,7 +24,7 @@ void InitializedTask::index(LSPIndexer &indexer) { void InitializedTask::run(LSPTypecheckerDelegate &typechecker) { ENFORCE(this->gs != nullptr); - typechecker.initialize(*this, std::move(this->gs), std::move(this->kvstore)); + typechecker.initialize(*this, std::move(this->gs), std::move(this->kvstore), config); typechecker.resumeTaskQueue(*this); } diff --git a/main/lsp/requests/code_action.cc b/main/lsp/requests/code_action.cc index 0ed45aba22..7c6cc39a53 100644 --- a/main/lsp/requests/code_action.cc +++ b/main/lsp/requests/code_action.cc @@ -1,12 +1,13 @@ #include "main/lsp/requests/code_action.h" #include "absl/algorithm/container.h" -#include "common/sort.h" +#include "absl/strings/match.h" +#include "common/sort/sort.h" #include "core/lsp/QueryResponse.h" +#include "main/lsp/ConvertToSingletonClassMethod.h" #include "main/lsp/LSPLoop.h" #include "main/lsp/LSPQuery.h" #include "main/lsp/MoveMethod.h" #include "main/lsp/json_types.h" -#include "main/sig_finder/sig_finder.h" using namespace std; @@ -43,8 +44,8 @@ vector> getQuickfixEdits(const LSPConfiguration &co } const core::lsp::MethodDefResponse * -hasLoneClassMethodResponse(const core::GlobalState &gs, const vector> &responses) { - // We want to return the singular `MethodDefResponse` for a singleton, non-operator method. +hasLoneMethodResponse(const core::GlobalState &gs, const vector> &responses) { + // We want to return the singular `MethodDefResponse` for a non-operator method. // We do not want to return the first such response, because there might be multiple // methods "defined" at the same location (cf. the DSLBuilder rewriter). And because the // query we're examining was a location-based query, there might be several overlapping responses, @@ -60,7 +61,7 @@ hasLoneClassMethodResponse(const core::GlobalState &gs, const vectorsymbol.data(gs)->owner.data(gs)->isSingletonClass(gs) || isOperator(def->name.show(gs))) { + if (isOperator(def->name.show(gs))) { return nullptr; } @@ -70,6 +71,36 @@ hasLoneClassMethodResponse(const core::GlobalState &gs, const vector> &responses) { + if (responses.empty()) { + return nullptr; + } + + auto *resp = responses[0]->isSend(); + if (resp == nullptr) { + return nullptr; + } + + auto method = resp->dispatchResult->main.method; + if (!method.exists()) { + return nullptr; + } + + auto data = method.data(gs); + if (data->owner != core::Symbols::TSingleton() || data->name != core::Names::unsafe()) { + return nullptr; + } + + if (!resp->termLocOffsets.exists() || resp->termLocOffsets.empty() || resp->argLocOffsets.size() != 1 || + !resp->argLocOffsets[0].exists() || resp->argLocOffsets[0].empty()) { + return nullptr; + } + + return resp; +} + } // namespace CodeActionTask::CodeActionTask(const LSPConfiguration &config, MessageId id, unique_ptr params) @@ -161,30 +192,70 @@ unique_ptr CodeActionTask::runRequest(LSPTypecheckerDelegate &t // Generate "Move method" code actions only for class method definitions if (queryResult.error == nullptr) { - if (auto *def = hasLoneClassMethodResponse(gs, queryResult.responses)) { - auto action = make_unique("Move method to a new module"); - action->kind = CodeActionKind::RefactorExtract; - + if (auto *def = hasLoneMethodResponse(gs, queryResult.responses)) { + unique_ptr action; bool canResolveLazily = config.getClientConfig().clientCodeActionResolveEditSupport && config.getClientConfig().clientCodeActionDataSupport; - auto newModuleLoc = getNewModuleLocation(gs, *def, typechecker); - auto renameCommand = make_unique("Rename Symbol", "sorbet.rename"); - auto arg = make_unique( - make_unique(params->textDocument->uri), move(newModuleLoc)); - auto args = vector>(); - args.emplace_back(move(arg)); - - renameCommand->arguments = move(args); - action->command = move(renameCommand); - if (canResolveLazily) { - action->data = move(params); + + if (def->symbol.data(gs)->owner.data(gs)->isSingletonClass(gs)) { + auto action = make_unique("Move method to a new module"); + action->kind = CodeActionKind::RefactorExtract; + + if (canResolveLazily) { + action->data = move(params); + } else { + auto workspaceEdit = make_unique(); + auto edits = getMoveMethodEdits(typechecker, config, *def); + workspaceEdit->documentChanges = move(edits); + action->edit = move(workspaceEdit); + } + + result.emplace_back(move(action)); } else { - auto workspaceEdit = make_unique(); - auto edits = getMoveMethodEdits(typechecker, config, *def); - workspaceEdit->documentChanges = move(edits); - action->edit = move(workspaceEdit); + auto action = make_unique("Convert to singleton class method (best effort)"); + action->kind = CodeActionKind::RefactorRewrite; + + if (canResolveLazily) { + const auto &maybeSource = def->termLoc.source(gs); + if (maybeSource.has_value() && absl::StartsWith(maybeSource.value(), "def ")) { + action->data = move(params); + result.emplace_back(move(action)); + } else { + // Maybe this is an attr_reader or a prop or something. Abort. + // (Only have to do this logic in the lazy case, because the eager case does + // it already.) + } + } else { + auto workspaceEdit = make_unique(); + auto edits = convertToSingletonClassMethod(typechecker, config, *def); + if (!edits.empty()) { + // "empty" means an error in convertToSingletonClassMethod. + // Don't prevent other code actions from being reported due to this one error. + // Instead, merely skip this code action. + workspaceEdit->documentChanges = move(edits); + action->edit = move(workspaceEdit); + result.emplace_back(move(action)); + } + } } + } else if (auto *resp = isTUnsafeResponse(gs, queryResult.responses)) { + auto tdi = make_unique(move(params->textDocument->uri), JSONNullObject()); + auto replaceRange = Range::fromLoc(gs, resp->termLoc()); + auto arg0Loc = core::Loc(file, resp->argLocOffsets[0]); + auto newContents = arg0Loc.source(gs).value(); + + vector> edits; + edits.emplace_back(make_unique(move(replaceRange), string(newContents))); + + vector> documentEdits; + documentEdits.emplace_back(make_unique(move(tdi), move(edits))); + auto workspaceEdit = make_unique(); + workspaceEdit->documentChanges = move(documentEdits); + + auto action = make_unique("Delete T.unsafe"); + action->kind = CodeActionKind::RefactorRewrite; + action->edit = move(workspaceEdit); result.emplace_back(move(action)); } } diff --git a/main/lsp/requests/code_action_resolve.cc b/main/lsp/requests/code_action_resolve.cc index 8ca68990f0..c83d5a4423 100644 --- a/main/lsp/requests/code_action_resolve.cc +++ b/main/lsp/requests/code_action_resolve.cc @@ -1,17 +1,37 @@ #include "main/lsp/requests/code_action_resolve.h" +#include "main/lsp/ConvertToSingletonClassMethod.h" #include "main/lsp/LSPQuery.h" #include "main/lsp/MoveMethod.h" #include "main/lsp/ShowOperation.h" using namespace std; namespace sorbet::realmain::lsp { + +namespace { + +bool allowedCodeActionKind(optional codeActionKind) { + if (!codeActionKind.has_value()) { + return false; + } + + switch (*codeActionKind) { + case CodeActionKind::RefactorExtract: + case CodeActionKind::RefactorRewrite: + return true; + default: + return false; + } +} + +} // namespace + CodeActionResolveTask::CodeActionResolveTask(const LSPConfiguration &config, MessageId id, unique_ptr params) : LSPRequestTask(config, move(id), LSPMethod::CodeActionResolve), params(move(params)) {} unique_ptr CodeActionResolveTask::runRequest(LSPTypecheckerDelegate &typechecker) { auto response = make_unique("2.0", id, LSPMethod::CodeActionResolve); - if (params->kind != CodeActionKind::RefactorExtract || !params->data.has_value()) { + if (!allowedCodeActionKind(params->kind) || !params->data.has_value()) { response->error = make_unique((int)LSPErrorCodes::InvalidRequest, "Invalid `codeAction/resolve` request"); return response; @@ -30,14 +50,35 @@ unique_ptr CodeActionResolveTask::runRequest(LSPTypecheckerDele ShowOperation op(config, ShowOperation::Kind::MoveMethod); for (const auto &resp : queryResult.responses) { - if (const auto def = resp->isMethodDef()) { - auto action = make_unique("Move method to a new module"); + const auto *def = resp->isMethodDef(); + if (def == nullptr) { + continue; + } + + auto &gs = typechecker.state(); + + unique_ptr action; + if (def->symbol.data(gs)->owner.data(gs)->isSingletonClass(gs)) { + action = make_unique("Move method to a new module"); action->kind = CodeActionKind::RefactorExtract; auto workspaceEdit = make_unique(); workspaceEdit->documentChanges = getMoveMethodEdits(typechecker, config, *def); action->edit = move(workspaceEdit); - response->result = move(action); + } else { + action = make_unique("Convert to singleton class method (best effort)"); + action->kind = CodeActionKind::RefactorRewrite; + auto workspaceEdit = make_unique(); + auto edits = convertToSingletonClassMethod(typechecker, config, *def); + workspaceEdit->documentChanges = move(edits); + action->edit = move(workspaceEdit); } + response->result = move(action); + } + + if (response->result == nullopt) { + response->error = + make_unique((int)LSPErrorCodes::InvalidRequest, "Invalid `codeAction/resolve` request"); + return response; } return response; diff --git a/main/lsp/requests/completion.cc b/main/lsp/requests/completion.cc index a429a7bddd..49ab3b2ed2 100644 --- a/main/lsp/requests/completion.cc +++ b/main/lsp/requests/completion.cc @@ -4,8 +4,8 @@ #include "absl/strings/match.h" #include "absl/strings/str_cat.h" #include "ast/treemap/treemap.h" -#include "common/formatting.h" -#include "common/sort.h" +#include "common/sort/sort.h" +#include "common/strings/formatting.h" #include "common/typecase.h" #include "core/lsp/QueryResponse.h" #include "main/lsp/FieldFinder.h" @@ -1272,7 +1272,7 @@ unique_ptr CompletionTask::runRequest(LSPTypecheckerDelegate &t if (auto sendResp = resp->isSend()) { auto callerSideName = sendResp->callerSideName; - auto prefix = (callerSideName == core::Names::methodNameMissing() || !sendResp->funLoc.contains(queryLoc)) + auto prefix = (callerSideName == core::Names::methodNameMissing() || !sendResp->funLoc().contains(queryLoc)) ? "" : callerSideName.shortName(gs); if (prefix == "" && queryLoc.adjust(gs, -2, 0).source(gs) == "::") { @@ -1307,7 +1307,7 @@ unique_ptr CompletionTask::runRequest(LSPTypecheckerDelegate &t // and the user's intent might have been to complete a local or a keyword. In the former case, we // know that the user doesn't want such completion results, since they have already written something // prefixed with `self.`. - auto explicitSelfReceiver = sendResp->receiverLoc.source(gs) == "self"; + auto explicitSelfReceiver = sendResp->receiverLoc().source(gs) == "self"; auto wantLocalsAndKeywords = sendResp->isPrivateOk && !explicitSelfReceiver; auto suggestKeywords = wantLocalsAndKeywords; // `enclosingMethod` existing indicates whether we want local variable completion results. @@ -1317,7 +1317,7 @@ unique_ptr CompletionTask::runRequest(LSPTypecheckerDelegate &t prefix, MethodSearchParams{ sendResp->dispatchResult, - sendResp->totalArgs, + sendResp->argLocOffsets.size(), sendResp->isPrivateOk, }, suggestKeywords, diff --git a/main/lsp/requests/definition.cc b/main/lsp/requests/definition.cc index 6baea6f542..5e5c807701 100644 --- a/main/lsp/requests/definition.cc +++ b/main/lsp/requests/definition.cc @@ -35,16 +35,21 @@ unique_ptr DefinitionTask::runRequest(LSPTypecheckerDelegate &t for (auto loc : sym.locs(gs)) { locMapping.emplace_back(loc, config.loc2Location(gs, loc)); } - // Move all non-existent Locations to the front of the vector. - auto validLocations = absl::c_partition(locMapping, [](const auto &p) { return p.second == nullptr; }); - // If we have multiple locations, eliminate "definitions" in RBI files - // for classes and modules, since the one(s) in Ruby files are more - // likely to be what the user is looking for. - if (sym.isClassOrModule() && std::distance(validLocations, locMapping.end()) > 1) { - validLocations = std::partition(validLocations, locMapping.end(), - [&gs](const auto &p) { return p.first.file().data(gs).isRBI(); }); + { + // Erase all invalid locations + auto validLocations = absl::c_partition(locMapping, [](const auto &p) { return p.second != nullptr; }); + locMapping.erase(validLocations, locMapping.end()); } - std::transform(validLocations, locMapping.end(), std::back_inserter(locations), + // If we have any non-RBI location, eliminate definitions in RBI files for classes and + // modules (not methods), since class definitions in RBI files are usually only there to + // specify missing methods on the class (and the user doesn't care about those + // missing_method.rbi definitions). + auto notIsRBI = [&gs](const auto &p) { return !p.first.file().data(gs).isRBI(); }; + if (sym.isClassOrModule() && !locMapping.empty() && absl::c_any_of(locMapping, notIsRBI)) { + auto startOfRBIDefs = absl::c_partition(locMapping, notIsRBI); + locMapping.erase(startOfRBIDefs, locMapping.end()); + } + std::transform(locMapping.begin(), locMapping.end(), std::back_inserter(locations), [](auto &p) { return std::move(p.second); }); } else if (resp->isField() || (fileIsTyped && (resp->isIdent() || resp->isLiteral()))) { const auto &retType = resp->getTypeAndOrigins(); diff --git a/main/lsp/requests/document_formatting.cc b/main/lsp/requests/document_formatting.cc index 29ec688d9e..b0cad00612 100644 --- a/main/lsp/requests/document_formatting.cc +++ b/main/lsp/requests/document_formatting.cc @@ -75,7 +75,11 @@ void DocumentFormattingTask::preprocess(LSPPreprocessor &preprocessor) { string_view sourceView; if (maybeFileContents.has_value()) { sourceView = maybeFileContents.value(); - } else { + } else if (sorbet::FileOps::exists(path)) { + // If the requested file path isn't in the workspace, + // we won't be able to load it, in which case + // we leave sourceView as empty and this becomes a no-op + // In this case, the request is for a file that's // not open in the IDE, so we read it from disk instead sourceView = sorbet::FileOps::read(path); diff --git a/main/lsp/requests/document_highlight.cc b/main/lsp/requests/document_highlight.cc index cda039456a..071caefd7c 100644 --- a/main/lsp/requests/document_highlight.cc +++ b/main/lsp/requests/document_highlight.cc @@ -83,8 +83,10 @@ unique_ptr DocumentHighlightTask::runRequest(LSPTypecheckerDele auto identResp = resp->isIdent(); auto loc = identResp->termLoc; if (loc.exists()) { - auto run2 = typechecker.query( - core::lsp::Query::createVarQuery(identResp->enclosingMethod, identResp->variable), {loc.file()}); + auto run2 = typechecker.query(core::lsp::Query::createVarQuery(identResp->enclosingMethod, + identResp->enclosingMethodLoc, + identResp->variable), + {loc.file()}); auto locations = extractLocations(gs, run2.responses); response->result = locationsToDocumentHighlights(uri, move(locations)); } diff --git a/main/lsp/requests/hover.cc b/main/lsp/requests/hover.cc index 3fd59cc901..1f898ccbe4 100644 --- a/main/lsp/requests/hover.cc +++ b/main/lsp/requests/hover.cc @@ -1,7 +1,7 @@ #include "main/lsp/requests/hover.h" #include "absl/strings/ascii.h" #include "absl/strings/str_join.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "core/lsp/QueryResponse.h" #include "main/lsp/LSPLoop.h" #include "main/lsp/LSPQuery.h" diff --git a/main/lsp/requests/initialize.cc b/main/lsp/requests/initialize.cc index c130e15f74..2c85bb1f06 100644 --- a/main/lsp/requests/initialize.cc +++ b/main/lsp/requests/initialize.cc @@ -36,7 +36,8 @@ unique_ptr InitializeTask::runRequest(LSPTypecheckerDelegate &t auto codeActionProvider = make_unique(); codeActionProvider->codeActionKinds = {CodeActionKind::Quickfix, CodeActionKind::SourceFixAllSorbet, - CodeActionKind::RefactorExtract}; + CodeActionKind::RefactorExtract, CodeActionKind::RefactorRewrite}; + codeActionProvider->resolveProvider = true; serverCap->codeActionProvider = move(codeActionProvider); if (opts.lspSignatureHelpEnabled) { diff --git a/main/lsp/requests/references.cc b/main/lsp/requests/references.cc index 0da63b2c90..64893cf1e1 100644 --- a/main/lsp/requests/references.cc +++ b/main/lsp/requests/references.cc @@ -162,9 +162,10 @@ unique_ptr ReferencesTask::runRequest(LSPTypecheckerDelegate &t if (fileIsTyped) { auto loc = identResp->termLoc; if (loc.exists()) { - auto run2 = typechecker.query( - core::lsp::Query::createVarQuery(identResp->enclosingMethod, identResp->variable), - {loc.file()}); + auto run2 = typechecker.query(core::lsp::Query::createVarQuery(identResp->enclosingMethod, + identResp->enclosingMethodLoc, + identResp->variable), + {loc.file()}); response->result = extractLocations(gs, run2.responses); } } else { diff --git a/main/lsp/requests/rename.cc b/main/lsp/requests/rename.cc index 5d0dd4ca18..c183fcf8f0 100644 --- a/main/lsp/requests/rename.cc +++ b/main/lsp/requests/rename.cc @@ -4,7 +4,7 @@ #include "absl/strings/str_replace.h" #include "absl/strings/str_split.h" #include "core/lsp/QueryResponse.h" -#include "main/lsp/AbstractRenamer.h" +#include "main/lsp/AbstractRewriter.h" #include "main/lsp/LSPLoop.h" #include "main/lsp/LSPQuery.h" #include "main/lsp/ShowOperation.h" @@ -38,11 +38,11 @@ bool isValidRenameLocation(const core::SymbolRef &symbol, const core::GlobalStat return true; } -class LocalRenamer : public AbstractRenamer { +class LocalRenamer : public AbstractRewriter { public: - LocalRenamer(const core::GlobalState &gs, const LSPConfiguration &config, const string oldName, - const string newName, std::vector localUsages) - : AbstractRenamer(gs, config, oldName, newName), localUsages(localUsages) { + LocalRenamer(const core::GlobalState &gs, const LSPConfiguration &config, const string newName, + std::vector localUsages) + : AbstractRewriter(gs, config), newName(newName), localUsages(localUsages) { // If the name is the same as before or empty, return an error. VS Code already prevents this on its own, but // other IDEs might not if (newName.empty()) { @@ -69,14 +69,18 @@ class LocalRenamer : public AbstractRenamer { void addSymbol(const core::SymbolRef symbol) override {} private: + string newName; std::vector localUsages; }; -class MethodRenamer : public AbstractRenamer { +class MethodRenamer : public AbstractRewriter { + string oldName; + string newName; + public: MethodRenamer(const core::GlobalState &gs, const LSPConfiguration &config, const string oldName, const string newName) - : AbstractRenamer(gs, config, oldName, newName) { + : AbstractRewriter(gs, config), oldName(oldName), newName(newName) { const vector invalidNames = {"initialize", "call"}; for (auto name : invalidNames) { if (oldName == name) { @@ -147,7 +151,7 @@ class MethodRenamer : public AbstractRenamer { return ""; } // TODO(jez) Use Loc::adjust here? - string::size_type methodNameOffset = methodNameLoc->beginPos() - sendResp->termLoc.beginPos(); + string::size_type methodNameOffset = methodNameLoc->beginPos() - sendResp->termLocOffsets.beginPos(); auto newsrc = replaceAt(source, methodNameOffset); return newsrc; } @@ -190,11 +194,12 @@ class MethodRenamer : public AbstractRenamer { } }; // MethodRenamer -class ConstRenamer : public AbstractRenamer { +class ConstRenamer : public AbstractRewriter { + string newName; + public: - ConstRenamer(const core::GlobalState &gs, const LSPConfiguration &config, const string oldName, - const string newName) - : AbstractRenamer(gs, config, oldName, newName) {} + ConstRenamer(const core::GlobalState &gs, const LSPConfiguration &config, const string newName) + : AbstractRewriter(gs, config), newName(newName) {} ~ConstRenamer() {} void rename(unique_ptr &response, const core::SymbolRef originalSymbol) override { auto loc = response->getLoc(); @@ -214,11 +219,12 @@ class ConstRenamer : public AbstractRenamer { } }; -class FieldRenamer : public AbstractRenamer { +class FieldRenamer : public AbstractRewriter { + string newName; + public: - FieldRenamer(const core::GlobalState &gs, const LSPConfiguration &config, const string oldName, - const string newName) - : AbstractRenamer(gs, config, oldName, newName) {} + FieldRenamer(const core::GlobalState &gs, const LSPConfiguration &config, const string newName) + : AbstractRewriter(gs, config), newName(newName) {} ~FieldRenamer() {} void rename(unique_ptr &response, const core::SymbolRef originalSymbol) override { @@ -251,23 +257,23 @@ class FieldRenamer : public AbstractRenamer { } }; -void enrichResponse(unique_ptr &responseMsg, shared_ptr renamer) { +void enrichResponse(unique_ptr &responseMsg, shared_ptr renamer) { responseMsg->result = renamer->buildWorkspaceEdit(); if (renamer->getInvalid()) { responseMsg->error = make_unique((int)LSPErrorCodes::InvalidRequest, renamer->getError()); } } -shared_ptr makeRenamer(const core::GlobalState &gs, - const sorbet::realmain::lsp::LSPConfiguration &config, core::SymbolRef symbol, - const std::string newName) { - auto originalName = symbol.name(gs).show(gs); +shared_ptr makeRenamer(const core::GlobalState &gs, + const sorbet::realmain::lsp::LSPConfiguration &config, core::SymbolRef symbol, + const std::string newName) { if (symbol.isMethod()) { + auto originalName = symbol.name(gs).show(gs); return make_shared(gs, config, originalName, newName); } else if (symbol.isField(gs)) { - return make_shared(gs, config, originalName, newName); + return make_shared(gs, config, newName); } else { - return make_shared(gs, config, originalName, newName); + return make_shared(gs, config, newName); } } @@ -307,7 +313,7 @@ unique_ptr RenameTask::runRequest(LSPTypecheckerDelegate &typec } auto resp = move(queryResponses[0]); - shared_ptr renamer; + shared_ptr renamer; if (auto constResp = resp->isConstant()) { // Sanity check the text. if (islower(params->newName[0])) { @@ -317,26 +323,26 @@ unique_ptr RenameTask::runRequest(LSPTypecheckerDelegate &typec } if (isValidRenameLocation(constResp->symbolBeforeDealias, gs, response)) { renamer = makeRenamer(gs, config, constResp->symbolBeforeDealias, params->newName); - renamer->getRenameEdits(typechecker, constResp->symbolBeforeDealias, params->newName); + renamer->getEdits(typechecker, constResp->symbolBeforeDealias); enrichResponse(response, renamer); } } else if (auto defResp = resp->isMethodDef()) { if (isValidRenameLocation(defResp->symbol, gs, response)) { renamer = makeRenamer(gs, config, defResp->symbol, params->newName); - renamer->getRenameEdits(typechecker, defResp->symbol, params->newName); + renamer->getEdits(typechecker, defResp->symbol); enrichResponse(response, renamer); } } else if (auto sendResp = resp->isSend()) { - // We don't need to handle dispatchResult->secondary here, because it will be checked in getRenameEdits. + // We don't need to handle dispatchResult->secondary here, because it will be checked in getEdits. auto method = sendResp->dispatchResult->main.method; renamer = makeRenamer(gs, config, method, params->newName); - renamer->getRenameEdits(typechecker, method, params->newName); + renamer->getEdits(typechecker, method); enrichResponse(response, renamer); } else if (auto identResp = resp->isIdent()) { if (identResp->enclosingMethod.exists()) { - core::NameRef localName = identResp->variable._name; auto references = - typechecker.query(core::lsp::Query::createVarQuery(identResp->enclosingMethod, identResp->variable), + typechecker.query(core::lsp::Query::createVarQuery(identResp->enclosingMethod, + identResp->enclosingMethodLoc, identResp->variable), {identResp->termLoc.file()}); std::vector locations; @@ -344,14 +350,13 @@ unique_ptr RenameTask::runRequest(LSPTypecheckerDelegate &typec locations.emplace_back(reference->getLoc()); } - shared_ptr renamer = - make_shared(gs, config, localName.show(gs), params->newName, locations); + shared_ptr renamer = make_shared(gs, config, params->newName, locations); renamer->rename(resp, core::SymbolRef{}); enrichResponse(response, renamer); } } else if (auto fieldResp = resp->isField()) { renamer = makeRenamer(gs, config, fieldResp->symbol, params->newName); - renamer->getRenameEdits(typechecker, fieldResp->symbol, params->newName); + renamer->getEdits(typechecker, fieldResp->symbol); enrichResponse(response, renamer); } diff --git a/main/lsp/requests/signature_help.cc b/main/lsp/requests/signature_help.cc index b3fcbcd070..288d3e9b6f 100644 --- a/main/lsp/requests/signature_help.cc +++ b/main/lsp/requests/signature_help.cc @@ -80,7 +80,7 @@ unique_ptr SignatureHelpTask::runRequest(LSPTypecheckerDelegate auto resp = move(queryResponses[0]); // only triggers on sends. Some SignatureHelps are triggered when the variable is being typed. if (auto sendResp = resp->isSend()) { - auto sendLocIndex = sendResp->termLoc.beginPos(); + auto sendLocIndex = sendResp->termLoc().beginPos(); auto fref = config.uri2FileRef(gs, params->textDocument->uri); if (!fref.exists()) { diff --git a/main/lsp/requests/workspace_symbols.cc b/main/lsp/requests/workspace_symbols.cc index 9948c474b9..1f1a30759c 100644 --- a/main/lsp/requests/workspace_symbols.cc +++ b/main/lsp/requests/workspace_symbols.cc @@ -1,5 +1,5 @@ #include "main/lsp/requests/workspace_symbols.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "core/lsp/QueryResponse.h" #include "main/lsp/LSPLoop.h" #include "main/lsp/ShowOperation.h" diff --git a/main/lsp/test/error_reporter_test.cc b/main/lsp/test/error_reporter_test.cc index 7a2bb6e941..251d599995 100644 --- a/main/lsp/test/error_reporter_test.cc +++ b/main/lsp/test/error_reporter_test.cc @@ -1,8 +1,8 @@ -#include "doctest.h" +#include "doctest/doctest.h" // has to go first as it violates our requirements -#include "common/Counters_impl.h" -#include "common/Timer.h" +#include "common/counters/Counters_impl.h" #include "common/kvstore/KeyValueStore.h" +#include "common/timers/Timer.h" #include "core/Error.h" #include "core/ErrorQueue.h" #include "core/Loc.h" diff --git a/main/lsp/test/generate_lsp_messages_test.cc b/main/lsp/test/generate_lsp_messages_test.cc index 5154f10817..31370b286a 100644 --- a/main/lsp/test/generate_lsp_messages_test.cc +++ b/main/lsp/test/generate_lsp_messages_test.cc @@ -1,7 +1,7 @@ -#include "doctest.h" +#include "doctest/doctest.h" #include "common/common.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "main/lsp/LSPMessage.h" #include "main/lsp/json_types.h" diff --git a/main/lsp/test/lsp_file_updates_test.cc b/main/lsp/test/lsp_file_updates_test.cc index 319cea3f6e..01b5b15d49 100644 --- a/main/lsp/test/lsp_file_updates_test.cc +++ b/main/lsp/test/lsp_file_updates_test.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" // has to go first as it violates our requirements #include "ast/ast.h" diff --git a/main/lsp/test/lsp_preprocessor_test.cc b/main/lsp/test/lsp_preprocessor_test.cc index facafe7d5e..c4402cfc57 100644 --- a/main/lsp/test/lsp_preprocessor_test.cc +++ b/main/lsp/test/lsp_preprocessor_test.cc @@ -1,8 +1,9 @@ -#include "doctest.h" +#include "doctest/doctest.h" // has to go first as it violates our requirements +#include "common/common.h" #include "common/concurrency/WorkerPool.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "core/Error.h" #include "core/ErrorCollector.h" #include "core/ErrorQueue.h" diff --git a/main/lsp/test/lsp_test.cc b/main/lsp/test/lsp_test.cc index d477c570bc..719bc5258d 100644 --- a/main/lsp/test/lsp_test.cc +++ b/main/lsp/test/lsp_test.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" // has to go first as it violates our requirements #include "main/lsp/LSPLoop.h" diff --git a/main/lsp/tools/generate_lsp_messages.h b/main/lsp/tools/generate_lsp_messages.h index 5be916ed89..4a3dc9545b 100644 --- a/main/lsp/tools/generate_lsp_messages.h +++ b/main/lsp/tools/generate_lsp_messages.h @@ -5,7 +5,7 @@ #include "common/FileOps.h" #include "common/JSON.h" #include "common/common.h" -#include "common/formatting.h" +#include "common/strings/formatting.h" #include #include diff --git a/main/lsp/tools/make_lsp_types.cc b/main/lsp/tools/make_lsp_types.cc index f8fac64311..f0fb0abec4 100644 --- a/main/lsp/tools/make_lsp_types.cc +++ b/main/lsp/tools/make_lsp_types.cc @@ -1264,6 +1264,7 @@ void makeLSPTypes(vector> &enumTypes, vector MultiThreadedLSPWrapper::read(int timeoutMs) { } void LSPWrapper::enableAllExperimentalFeatures() { - enableExperimentalFeature(LSPExperimentalFeature::DocumentHighlight); - enableExperimentalFeature(LSPExperimentalFeature::DocumentSymbol); - enableExperimentalFeature(LSPExperimentalFeature::SignatureHelp); - enableExperimentalFeature(LSPExperimentalFeature::DocumentFormat); - enableExperimentalFeature(LSPExperimentalFeature::ExperimentalFastPath); -} - -void LSPWrapper::enableExperimentalFeature(LSPExperimentalFeature feature) { - switch (feature) { - case LSPExperimentalFeature::DocumentHighlight: - opts->lspDocumentHighlightEnabled = true; - break; - case LSPExperimentalFeature::DocumentSymbol: - opts->lspDocumentSymbolEnabled = true; - break; - case LSPExperimentalFeature::SignatureHelp: - opts->lspSignatureHelpEnabled = true; - break; - case LSPExperimentalFeature::DocumentFormat: - opts->lspDocumentFormatRubyfmtEnabled = true; - break; - case LSPExperimentalFeature::ExperimentalFastPath: - opts->lspExperimentalFastPathEnabled = true; - break; - } + opts->lspDocumentHighlightEnabled = true; + opts->lspDocumentSymbolEnabled = true; + opts->lspSignatureHelpEnabled = true; + opts->lspDocumentFormatRubyfmtEnabled = true; } int LSPWrapper::getTypecheckCount() { diff --git a/main/lsp/wrapper.h b/main/lsp/wrapper.h index 3b94cf62b1..c8780783fe 100644 --- a/main/lsp/wrapper.h +++ b/main/lsp/wrapper.h @@ -45,14 +45,6 @@ class LSPWrapper { bool disableFastPath); public: - enum class LSPExperimentalFeature { - DocumentSymbol = 6, - SignatureHelp = 7, - DocumentHighlight = 9, - DocumentFormat = 10, - ExperimentalFastPath = 11, - }; - // N.B.: Sorbet assumes we 'own' this object; keep it alive to avoid memory errors. const std::shared_ptr opts; @@ -60,12 +52,6 @@ class LSPWrapper { const LSPConfiguration &config() const; - /** - * Enable an experimental LSP feature. - * Note: Use this method *before* the client performs initialization with the server. - */ - void enableExperimentalFeature(LSPExperimentalFeature feature); - /** * Enable all experimental LSP features. * Note: Use this method *before* the client performs initialization with the server. diff --git a/main/options/BUILD b/main/options/BUILD index 6a2e2b8ff3..5d91b39775 100644 --- a/main/options/BUILD +++ b/main/options/BUILD @@ -35,7 +35,7 @@ cc_test( deps = [ "//core", "//main/options", - "@doctest", - "@doctest//:doctest_main", + "@doctest//doctest", + "@doctest//doctest:main", ], ) diff --git a/main/options/options.cc b/main/options/options.cc index 853c071a91..bfd169f1f4 100644 --- a/main/options/options.cc +++ b/main/options/options.cc @@ -5,10 +5,10 @@ #include "absl/strings/str_split.h" #include "common/FileOps.h" -#include "common/Timer.h" #include "common/concurrency/WorkerPool.h" -#include "common/formatting.h" -#include "common/sort.h" +#include "common/sort/sort.h" +#include "common/strings/formatting.h" +#include "common/timers/Timer.h" #include "core/Error.h" #include "core/errors/infer.h" #include "main/options/ConfigParser.h" @@ -76,11 +76,11 @@ const vector print_options({ {"missing-constants", &Printers::MissingConstants}, {"autogen", &Printers::Autogen}, {"autogen-msgpack", &Printers::AutogenMsgPack}, - {"autogen-autoloader", &Printers::AutogenAutoloader, true, false}, {"autogen-subclasses", &Printers::AutogenSubclasses}, {"package-tree", &Printers::Packager, false}, {"minimized-rbi", &Printers::MinimizeRBI}, {"payload-sources", &Printers::PayloadSources}, + {"untyped-blame", &Printers::UntypedBlame}, }); PrinterConfig::PrinterConfig() : state(make_shared()){}; @@ -148,16 +148,16 @@ vector> Printers::printers() { MissingConstants, Autogen, AutogenMsgPack, - AutogenAutoloader, AutogenSubclasses, Packager, MinimizeRBI, PayloadSources, + UntypedBlame, }); } bool Printers::isAutogen() const { - return Autogen.enabled || AutogenMsgPack.enabled || AutogenSubclasses.enabled || AutogenAutoloader.enabled; + return Autogen.enabled || AutogenMsgPack.enabled || AutogenSubclasses.enabled; } struct StopAfterOptions { @@ -349,9 +349,6 @@ buildOptions(const vector()->default_value("true")); options.add_options("advanced")("enable-experimental-requires-ancestor", "Enable experimental `requires_ancestor` annotation"); @@ -400,36 +397,11 @@ buildOptions(const vector>(), "string"); - - options.add_options("advanced")( - "autogen-autoloader-exclude-require", - "Names that should be excluded from top-level require statements in autoloader output. (e.g. 'pry')", - cxxopts::value>()); - options.add_options("advanced")("autogen-autoloader-ignore", - "Input files to exclude from autoloader output. (See --ignore for formatting.)", - cxxopts::value>()); - options.add_options("advanced")("autogen-autoloader-modules", "Top-level modules to include in autoloader output", - cxxopts::value>()); - options.add_options("advanced")("autogen-autoloader-preamble", "Preamble to add to each autoloader file", - cxxopts::value()->default_value("")); - options.add_options("advanced")("autogen-autoloader-root", "Root directory for autoloader output", - cxxopts::value()->default_value("autoloader")); - options.add_options("advanced")("autogen-registry-module", "Name of Ruby module used for autoloader registry", - cxxopts::value()->default_value("Opus::Require")); - options.add_options("advanced")("autogen-root-object", - "Name of Ruby object on which root autoloads should be installed", - cxxopts::value()->default_value("Object")); - options.add_options("advanced")("autogen-autoloader-samefile", - "Modules that should never be collapsed into their parent. This helps break cycles " - "in certain cases. (e.g. Foo::Bar::Baz)", - cxxopts::value>()); - options.add_options("advanced")("autogen-autoloader-pbal-namespaces", - "Namespaces for which path-based autoloading is enabled.", - cxxopts::value>()); - options.add_options("advanced")("autogen-autoloader-strip-prefix", - "Prefixes to strip from file output paths. " - "If path does not start with prefix, nothing is stripped", - cxxopts::value>()); + options.add_options("dev")("skip-package-import-visibility-check-for", + "Packages for which the visible_to check does not apply. They can import any package " + "regardless of visible_to annotations." + "This option must be used in conjunction with --stripe-packages", + cxxopts::value>(), "string"); buildAutogenCacheOptions(options); options.add_options("advanced")("error-url-base", @@ -438,6 +410,9 @@ buildOptions(const vector()->default_value(empty.errorUrlBase), "url-base"); options.add_options("advanced")("experimental-ruby3-keyword-args", "Enforce use of new (Ruby 3.0-style) keyword arguments", cxxopts::value()); + options.add_options("advanced")("check-out-of-order-constant-references", + "Enable out-of-order constant reference checks (error 5027)"); + options.add_options("advanced")("track-untyped", "Track untyped usage statistics in the file-table output"); // Developer options options.add_options("dev")("p,print", to_string(all_prints), cxxopts::value>(), "type"); @@ -528,6 +503,7 @@ buildOptions(const vectorerror("--print={} requires an output path to be specified", opt); - throw EarlyReturnWithCode(1); - } found = true; break; } @@ -660,39 +632,6 @@ void parseIgnorePatterns(const vector &rawIgnorePatterns, vector } } -bool extractAutoloaderConfig(cxxopts::ParseResult &raw, Options &opts, shared_ptr logger) { - AutoloaderConfig &cfg = opts.autoloaderConfig; - if (raw.count("autogen-autoloader-exclude-require") > 0) { - cfg.requireExcludes = raw["autogen-autoloader-exclude-require"].as>(); - } - if (raw.count("autogen-autoloader-ignore") > 0) { - auto rawIgnorePatterns = raw["autogen-autoloader-ignore"].as>(); - parseIgnorePatterns(rawIgnorePatterns, cfg.absoluteIgnorePatterns, cfg.relativeIgnorePatterns); - } - if (raw.count("autogen-autoloader-modules") > 0) { - cfg.modules = raw["autogen-autoloader-modules"].as>(); - } - if (raw.count("autogen-autoloader-strip-prefix") > 0) { - cfg.stripPrefixes = raw["autogen-autoloader-strip-prefix"].as>(); - } - if (raw.count("autogen-autoloader-samefile") > 0) { - for (auto &fullName : raw["autogen-autoloader-samefile"].as>()) { - cfg.sameFileModules.emplace_back(absl::StrSplit(fullName, "::")); - } - } - if (raw.count("autogen-autoloader-pbal-namespaces") > 0) { - for (auto &fullName : raw["autogen-autoloader-pbal-namespaces"].as>()) { - cfg.pbalNamespaces.emplace_back(absl::StrSplit(fullName, "::")); - } - } - cfg.preamble = raw["autogen-autoloader-preamble"].as(); - cfg.registryModule = raw["autogen-registry-module"].as(); - cfg.rootDir = stripTrailingSlashes(raw["autogen-autoloader-root"].as()); - - cfg.rootObject = raw["autogen-root-object"].as(); - return true; -} - void addFilesFromDir(Options &opts, string_view dir, WorkerPool &workerPool, shared_ptr logger) { auto fileNormalized = stripTrailingSlashes(dir); opts.rawInputDirNames.emplace_back(fileNormalized); @@ -701,8 +640,8 @@ void addFilesFromDir(Options &opts, string_view dir, WorkerPool &workerPool, sha try { containedFiles = opts.fs->listFilesInDir(fileNormalized, opts.allowedExtensions, workerPool, true, opts.absoluteIgnorePatterns, opts.relativeIgnorePatterns); - } catch (sorbet::FileNotFoundException e) { - logger->error(e.what()); + } catch (sorbet::FileNotFoundException &e) { + logger->error("{}", e.what()); throw EarlyReturnWithCode(1); } catch (sorbet::FileNotDirException) { logger->error("Path `{}` is not a directory", dir); @@ -796,8 +735,8 @@ void readOptions(Options &opts, opts.lspDocumentFormatRubyfmtEnabled = FileOps::exists(opts.rubyfmtPath) && (enableAllLSPFeatures || raw["enable-experimental-lsp-document-formatting-rubyfmt"].as()); - - opts.lspExperimentalFastPathEnabled = raw["enable-experimental-lsp-fast-path"].as(); + opts.outOfOrderReferenceChecksEnabled = raw["check-out-of-order-constant-references"].as(); + opts.trackUntyped = raw["track-untyped"].as(); if (raw.count("lsp-directories-missing-from-client") > 0) { auto lspDirsMissingFromClient = raw["lsp-directories-missing-from-client"].as>(); @@ -849,8 +788,7 @@ void readOptions(Options &opts, // Certain features only need certain passes if (opts.print.isAutogen() && (opts.stopAfterPhase != Phase::NAMER)) { - logger->error( - "-p autogen{-msgpack,-classlist,-subclasses,-autoloader} must also include --stop-after=namer"); + logger->error("-p autogen{-msgpack,-classlist,-subclasses} must also include --stop-after=namer"); throw EarlyReturnWithCode(1); } @@ -877,7 +815,7 @@ void readOptions(Options &opts, } if (raw.count("autogen-behavior-allowed-in-rbi-files-paths") > 0) { - if (!opts.print.isAutogen() || opts.print.AutogenAutoloader.enabled) { + if (!opts.print.isAutogen()) { logger->error("autogen-behavior-allowed-in-rbi-files-paths can only be used with -p autogen or -p " "autogen-msgpack"); throw EarlyReturnWithCode(1); @@ -886,6 +824,11 @@ void readOptions(Options &opts, raw["autogen-behavior-allowed-in-rbi-files-paths"].as>(); } + if (opts.print.UntypedBlame.enabled && !opts.trackUntyped) { + logger->error("-p untyped-blame: must also include --track-untyped"); + throw EarlyReturnWithCode(1); + } + extractAutogenConstCacheConfig(raw, opts.autogenConstantCacheConfig); opts.noErrorCount = raw["no-error-count"].as(); @@ -1017,6 +960,25 @@ void readOptions(Options &opts, opts.secondaryTestPackageNamespaces.emplace_back(ns); } } + + if (raw.count("skip-package-import-visibility-check-for")) { + if (!opts.stripePackages) { + logger->error( + "--skip-package-import-visibility-check-for can only be specified in --stripe-packages mode"); + throw EarlyReturnWithCode(1); + } + std::regex nsValid("[A-Z][a-zA-Z0-9:]+"); + for (const string &ns : raw["skip-package-import-visibility-check-for"].as>()) { + if (!std::regex_match(ns, nsValid)) { + logger->error( + "--skip-package-import-visibility-check-for must contain items that start with a capital " + "letter and are alphanumeric."); + throw EarlyReturnWithCode(1); + } + opts.skipPackageImportVisibilityCheckFor.emplace_back(ns); + } + } + opts.stripePackagesHint = raw["stripe-packages-hint-message"].as(); if (!opts.stripePackagesHint.empty() && !opts.stripePackages) { if (!opts.stripePackages) { @@ -1071,7 +1033,6 @@ void readOptions(Options &opts, } } - extractAutoloaderConfig(raw, opts, logger); opts.errorUrlBase = raw["error-url-base"].as(); opts.noErrorSections = raw["no-error-sections"].as(); opts.ruby3KeywordArgs = raw["experimental-ruby3-keyword-args"].as(); diff --git a/main/options/options.h b/main/options/options.h index aa0a508d03..5b8479f0fe 100644 --- a/main/options/options.h +++ b/main/options/options.h @@ -1,9 +1,9 @@ #ifndef RUBY_TYPER_OPTIONS_H #define RUBY_TYPER_OPTIONS_H -#include "common/ConstExprStr.h" #include "common/EarlyReturnWithCode.h" #include "common/FileSystem.h" #include "common/common.h" +#include "common/strings/ConstExprStr.h" #include "core/StrictLevel.h" #include "main/pipeline/semantic_extension/SemanticExtension.h" #include "spdlog/spdlog.h" @@ -82,11 +82,11 @@ struct Printers { PrinterConfig MissingConstants; PrinterConfig Autogen; PrinterConfig AutogenMsgPack; - PrinterConfig AutogenAutoloader; PrinterConfig AutogenSubclasses; PrinterConfig Packager; PrinterConfig MinimizeRBI; PrinterConfig PayloadSources; + PrinterConfig UntypedBlame; // Ensure everything here is in PrinterConfig::printers(). std::vector> printers(); @@ -106,22 +106,6 @@ enum class Phase { INFERENCER, }; -struct AutoloaderConfig { - // Top-level modules to include in autoloader output - std::vector modules; - std::string rootDir; - std::string preamble; - std::string registryModule; - std::string rootObject; - std::vector requireExcludes; - std::vector> sameFileModules; - std::vector> pbalNamespaces; - std::vector stripPrefixes; - - std::vector absoluteIgnorePatterns; - std::vector relativeIgnorePatterns; -}; - struct AutogenConstCacheConfig { // A file which contains a cache that can be used to potentially skip autogen std::string cacheFile; @@ -142,7 +126,6 @@ constexpr size_t MAX_CACHE_SIZE_BYTES = 1L * 1024 * 1024 * 1024; // 1 GiB struct Options { Printers print; - AutoloaderConfig autoloaderConfig; Phase stopAfterPhase = Phase::INFERENCER; bool noStdlib = false; @@ -184,6 +167,7 @@ struct Options { std::vector extraPackageFilesDirectoryUnderscorePrefixes; std::vector extraPackageFilesDirectorySlashPrefixes; std::vector secondaryTestPackageNamespaces; + std::vector skipPackageImportVisibilityCheckFor; std::string typedSource = ""; std::string cacheDir = ""; // This configured both maximum filesystem db size and max virtual memory usage @@ -267,7 +251,9 @@ struct Options { bool lspDocumentSymbolEnabled = false; bool lspDocumentFormatRubyfmtEnabled = false; bool lspSignatureHelpEnabled = false; - bool lspExperimentalFastPathEnabled = false; + // Enables out-of-order reference checking + bool outOfOrderReferenceChecksEnabled = false; + bool trackUntyped = false; // Experimental feature `requires_ancestor` bool requiresAncestorEnabled = false; diff --git a/main/options/test/options_test.cc b/main/options/test/options_test.cc index a861eba07d..4b72b0ab8a 100644 --- a/main/options/test/options_test.cc +++ b/main/options/test/options_test.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" // has to go first as it violates our requirements #include "spdlog/spdlog.h" // has to go above null_sink.h; this comment prevents reordering. diff --git a/main/pipeline/pipeline.cc b/main/pipeline/pipeline.cc index df2fcc7ddb..73bc29cb1b 100644 --- a/main/pipeline/pipeline.cc +++ b/main/pipeline/pipeline.cc @@ -23,11 +23,11 @@ #include "cfg/builder/builder.h" #include "class_flatten/class_flatten.h" #include "common/FileOps.h" -#include "common/Timer.h" #include "common/concurrency/ConcurrentQueue.h" #include "common/crypto_hashing/crypto_hashing.h" -#include "common/formatting.h" -#include "common/sort.h" +#include "common/sort/sort.h" +#include "common/strings/formatting.h" +#include "common/timers/Timer.h" #include "core/ErrorQueue.h" #include "core/NameSubstitution.h" #include "core/Unfreeze.h" @@ -237,13 +237,14 @@ incrementalResolve(core::GlobalState &gs, vector what, what = packager::Packager::runIncremental(gs, move(what)); } #endif + auto runIncrementalNamer = foundHashesForFiles.has_value() && !foundHashesForFiles->empty(); { Timer timeit(gs.tracer(), "incremental_naming"); core::UnfreezeSymbolTable symbolTable(gs); core::UnfreezeNameTable nameTable(gs); auto emptyWorkers = WorkerPool::create(0, gs.tracer()); - auto result = foundHashesForFiles.has_value() + auto result = runIncrementalNamer ? sorbet::namer::Namer::runIncremental( gs, move(what), std::move(foundHashesForFiles.value()), *emptyWorkers) : sorbet::namer::Namer::run(gs, move(what), *emptyWorkers, nullptr); @@ -264,7 +265,7 @@ incrementalResolve(core::GlobalState &gs, vector what, core::UnfreezeSymbolTable symbolTable(gs); core::UnfreezeNameTable nameTable(gs); - auto result = sorbet::resolver::Resolver::runIncremental(gs, move(what)); + auto result = sorbet::resolver::Resolver::runIncremental(gs, move(what), runIncrementalNamer); // incrementalResolve is not cancelable. ENFORCE(result.hasResult()); what = move(result.result()); @@ -405,6 +406,10 @@ ast::ExpressionPtr readFileWithStrictnessOverrides(core::GlobalState &gs, core:: } prodCounterAdd("types.input.bytes", src.size()); prodCounterInc("types.input.files"); + if (core::File::isRBIPath(fileName)) { + counterAdd("types.input.rbi.bytes", src.size()); + counterInc("types.input.rbi.files"); + } { core::UnfreezeFileTable unfreezeFiles(gs); @@ -472,7 +477,6 @@ vector mergeIndexResults(core::GlobalState &cgs, const options: shared_ptr> input, WorkerPool &workers, const unique_ptr &kvstore) { ProgressIndicator progress(opts.showProgress, "Indexing", input->bound); - Timer timeit(cgs.tracer(), "mergeIndexResults"); auto batchq = make_shared>(input->bound); vector ret; @@ -533,7 +537,6 @@ vector mergeIndexResults(core::GlobalState &cgs, const options: vector indexSuppliedFiles(core::GlobalState &baseGs, vector &files, const options::Options &opts, WorkerPool &workers, const unique_ptr &kvstore) { - Timer timeit(baseGs.tracer(), "indexSuppliedFiles"); auto resultq = make_shared>(files.size()); auto fileq = make_shared>(files.size()); for (auto &file : files) { @@ -680,14 +683,13 @@ vector package(core::GlobalState &gs, vector w WorkerPool &workers) { #ifndef SORBET_REALMAIN_MIN if (opts.stripePackages) { - Timer timeit(gs.tracer(), "package"); { core::UnfreezeNameTable unfreezeToEnterPackagerOptionsGS(gs); core::packages::UnfreezePackages unfreezeToEnterPackagerOptionsPackageDB = gs.unfreezePackages(); - gs.setPackagerOptions(opts.secondaryTestPackageNamespaces, - opts.extraPackageFilesDirectoryUnderscorePrefixes, - opts.extraPackageFilesDirectorySlashPrefixes, - opts.packageSkipRBIExportEnforcementDirs, opts.stripePackagesHint); + gs.setPackagerOptions( + opts.secondaryTestPackageNamespaces, opts.extraPackageFilesDirectoryUnderscorePrefixes, + opts.extraPackageFilesDirectorySlashPrefixes, opts.packageSkipRBIExportEnforcementDirs, + opts.skipPackageImportVisibilityCheckFor, opts.stripePackagesHint); } what = packager::Packager::run(gs, workers, move(what)); if (opts.print.Packager.enabled) { @@ -963,56 +965,6 @@ ast::ParsedFilesOrCancelled resolve(unique_ptr &gs, vectorshowRawFull()); } -#ifndef SORBET_REALMAIN_MIN - if (opts.print.FileTableProto.enabled || opts.print.FileTableFullProto.enabled) { - if (opts.print.FileTableProto.enabled && opts.print.FileTableFullProto.enabled) { - Exception::raise("file-table-proto and file-table-full-proto are mutually exclusive print options"); - } - auto files = core::Proto::filesToProto(*gs, opts.print.FileTableFullProto.enabled); - if (opts.print.FileTableProto.outputPath.empty()) { - files.SerializeToOstream(&cout); - } else { - string buf; - files.SerializeToString(&buf); - opts.print.FileTableProto.print(buf); - } - } - if (opts.print.FileTableJson.enabled || opts.print.FileTableFullJson.enabled) { - if (opts.print.FileTableJson.enabled && opts.print.FileTableFullJson.enabled) { - Exception::raise("file-table-json and file-table-full-json are mutually exclusive print options"); - } - auto files = core::Proto::filesToProto(*gs, opts.print.FileTableFullJson.enabled); - if (opts.print.FileTableJson.outputPath.empty()) { - core::Proto::toJSON(files, cout); - } else { - stringstream buf; - core::Proto::toJSON(files, buf); - opts.print.FileTableJson.print(buf.str()); - } - } - if (opts.print.FileTableMessagePack.enabled || opts.print.FileTableFullMessagePack.enabled) { - if (opts.print.FileTableMessagePack.enabled && opts.print.FileTableFullMessagePack.enabled) { - Exception::raise("file-table-msgpack and file-table-full-msgpack are mutually exclusive print options"); - } - auto files = core::Proto::filesToProto(*gs, opts.print.FileTableFullMessagePack.enabled); - stringstream buf; - core::Proto::toJSON(files, buf); - auto str = buf.str(); - rapidjson::Document document; - document.Parse(str); - mpack_writer_t writer; - if (opts.print.FileTableMessagePack.outputPath.empty()) { - mpack_writer_init_stdfile(&writer, stdout, /* close when done */ false); - } else { - mpack_writer_init_filename(&writer, opts.print.FileTableMessagePack.outputPath.c_str()); - } - json2msgpack::json2msgpack(document, &writer); - if (mpack_writer_destroy(&writer)) { - Exception::raise("failed to write msgpack"); - } - } -#endif - if (opts.print.MissingConstants.enabled) { what = printMissingConstants(*gs, opts, move(what)); } @@ -1159,7 +1111,60 @@ void typecheck(const core::GlobalState &gs, vector what, const } } -bool cacheTreesAndFiles(const core::GlobalState &gs, WorkerPool &workers, vector &parsedFiles, +void printFileTable(unique_ptr &gs, const options::Options &opts, + const UnorderedMap &untypedUsages) { +#ifndef SORBET_REALMAIN_MIN + if (opts.print.FileTableProto.enabled || opts.print.FileTableFullProto.enabled) { + if (opts.print.FileTableProto.enabled && opts.print.FileTableFullProto.enabled) { + Exception::raise("file-table-proto and file-table-full-proto are mutually exclusive print options"); + } + auto files = core::Proto::filesToProto(*gs, untypedUsages, opts.print.FileTableFullProto.enabled); + if (opts.print.FileTableProto.outputPath.empty()) { + files.SerializeToOstream(&cout); + } else { + string buf; + files.SerializeToString(&buf); + opts.print.FileTableProto.print(buf); + } + } + if (opts.print.FileTableJson.enabled || opts.print.FileTableFullJson.enabled) { + if (opts.print.FileTableJson.enabled && opts.print.FileTableFullJson.enabled) { + Exception::raise("file-table-json and file-table-full-json are mutually exclusive print options"); + } + auto files = core::Proto::filesToProto(*gs, untypedUsages, opts.print.FileTableFullJson.enabled); + if (opts.print.FileTableJson.outputPath.empty()) { + core::Proto::toJSON(files, cout); + } else { + stringstream buf; + core::Proto::toJSON(files, buf); + opts.print.FileTableJson.print(buf.str()); + } + } + if (opts.print.FileTableMessagePack.enabled || opts.print.FileTableFullMessagePack.enabled) { + if (opts.print.FileTableMessagePack.enabled && opts.print.FileTableFullMessagePack.enabled) { + Exception::raise("file-table-msgpack and file-table-full-msgpack are mutually exclusive print options"); + } + auto files = core::Proto::filesToProto(*gs, untypedUsages, opts.print.FileTableFullMessagePack.enabled); + stringstream buf; + core::Proto::toJSON(files, buf); + auto str = buf.str(); + rapidjson::Document document; + document.Parse(str); + mpack_writer_t writer; + if (opts.print.FileTableMessagePack.outputPath.empty()) { + mpack_writer_init_stdfile(&writer, stdout, /* close when done */ false); + } else { + mpack_writer_init_filename(&writer, opts.print.FileTableMessagePack.outputPath.c_str()); + } + json2msgpack::json2msgpack(document, &writer); + if (mpack_writer_destroy(&writer)) { + Exception::raise("failed to write msgpack"); + } + } +#endif +} + +bool cacheTreesAndFiles(const core::GlobalState &gs, WorkerPool &workers, const vector &parsedFiles, const unique_ptr &kvstore) { if (kvstore == nullptr) { return false; @@ -1168,7 +1173,7 @@ bool cacheTreesAndFiles(const core::GlobalState &gs, WorkerPool &workers, vector Timer timeit(gs.tracer(), "pipeline::cacheTreesAndFiles"); // Compress files in parallel. - auto fileq = make_shared>(parsedFiles.size()); + auto fileq = make_shared>(parsedFiles.size()); for (auto &parsedFile : parsedFiles) { fileq->push(&parsedFile, 1); } @@ -1177,7 +1182,7 @@ bool cacheTreesAndFiles(const core::GlobalState &gs, WorkerPool &workers, vector workers.multiplexJob("compressTreesAndFiles", [fileq, resultq, &gs]() { vector>> threadResult; int processedByThread = 0; - ast::ParsedFile *job = nullptr; + const ast::ParsedFile *job = nullptr; unique_ptr timeit; { for (auto result = fileq->try_pop(job); !result.done(); result = fileq->try_pop(job)) { @@ -1267,4 +1272,63 @@ vector autogenWriteCacheFile(const core::GlobalState &gs, const #endif } +void printUntypedBlames(const core::GlobalState &gs, const UnorderedMap &untypedBlames, + const options::Options &opts) { +#ifndef SORBET_REALMAIN_MIN + + if (!opts.print.UntypedBlame.enabled) { + return; + } + + rapidjson::StringBuffer result; + rapidjson::Writer writer(result); + + writer.StartArray(); + + for (auto &[symId, count] : untypedBlames) { + auto sym = core::SymbolRef::fromRaw(symId); + + writer.StartObject(); + + writer.String("path"); + if (sym.exists() && sym.loc(gs).exists()) { + writer.String(std::string(sym.loc(gs).file().data(gs).path())); + + } else { + writer.String(""); + } + + writer.String("package"); + if (sym.exists() && sym.loc(gs).exists()) { + const auto file = sym.loc(gs).file(); + const auto pkg = gs.packageDB().getPackageNameForFile(file); + if (pkg == core::NameRef::noName()) { + writer.String(""); + } else { + writer.String(pkg.show(gs)); + } + + } else { + writer.String(""); + } + + writer.String("owner"); + auto owner = sym.owner(gs).show(gs); + writer.String(owner); + + writer.String("name"); + writer.String(sym.name(gs).show(gs)); + + writer.String("count"); + writer.Int64(count); + + writer.EndObject(); + } + + writer.EndArray(); + + opts.print.UntypedBlame.print(result.GetString()); +#endif +} + } // namespace sorbet::realmain::pipeline diff --git a/main/pipeline/pipeline.h b/main/pipeline/pipeline.h index 3f0c982f9f..098d40f339 100644 --- a/main/pipeline/pipeline.h +++ b/main/pipeline/pipeline.h @@ -54,15 +54,22 @@ void typecheck(const core::GlobalState &gs, std::vector what, c std::optional> preemptionManager = std::nullopt, bool presorted = false, bool intentionallyLeakASTs = false); +void printFileTable(std::unique_ptr &gs, const options::Options &opts, + const UnorderedMap &untypedUsages); + core::StrictLevel decideStrictLevel(const core::GlobalState &gs, const core::FileRef file, const options::Options &opts); // Caches any uncached trees and files. Returns true if it modifies kvstore. -bool cacheTreesAndFiles(const core::GlobalState &gs, WorkerPool &workers, std::vector &parsedFiles, +bool cacheTreesAndFiles(const core::GlobalState &gs, WorkerPool &workers, + const std::vector &parsedFiles, const std::unique_ptr &kvstore); // Exported for tests only. std::string fileKey(const core::File &file); +void printUntypedBlames(const core::GlobalState &gs, const UnorderedMap &untypedBlames, + const options::Options &opts); + } // namespace sorbet::realmain::pipeline #endif // RUBY_TYPER_PIPELINE_H diff --git a/main/realmain.cc b/main/realmain.cc index 5ea48aaaad..3be8b8d712 100644 --- a/main/realmain.cc +++ b/main/realmain.cc @@ -7,7 +7,6 @@ #include "common/statsd/statsd.h" #include "common/web_tracer_framework/tracing.h" #include "main/autogen/autogen.h" -#include "main/autogen/autoloader.h" #include "main/autogen/cache.h" #include "main/autogen/crc_builder.h" #include "main/autogen/data/version.h" @@ -20,12 +19,14 @@ #include "packager/rbi_gen.h" #endif +#include "absl/algorithm/container.h" #include "absl/strings/match.h" #include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" #include "absl/strings/str_split.h" #include "common/FileOps.h" -#include "common/Timer.h" -#include "common/sort.h" +#include "common/sort/sort.h" +#include "common/timers/Timer.h" #include "core/Error.h" #include "core/ErrorQueue.h" #include "core/Files.h" @@ -197,11 +198,10 @@ struct AutogenResult { }; CounterState counters; vector> prints; - unique_ptr defTree = make_unique(); }; -void runAutogen(const core::GlobalState &gs, options::Options &opts, const autogen::AutoloaderConfig &autoloaderCfg, - const autogen::AutogenConfig &autogenCfg, WorkerPool &workers, vector &indexed) { +void runAutogen(const core::GlobalState &gs, options::Options &opts, const autogen::AutogenConfig &autogenCfg, + WorkerPool &workers, vector &indexed, const vector &changedFiles) { Timer timeit(logger, "autogen"); auto resultq = make_shared>(indexed.size()); @@ -212,64 +212,58 @@ void runAutogen(const core::GlobalState &gs, options::Options &opts, const autog } auto crcBuilder = autogen::CRCBuilder::create(); - workers.multiplexJob( - "runAutogen", [&gs, &opts, &indexed, &autoloaderCfg, &autogenCfg, crcBuilder, fileq, resultq]() { - AutogenResult out; - int n = 0; - int autogenVersion = opts.autogenVersion == 0 ? autogen::AutogenVersion::MAX_VERSION : opts.autogenVersion; - { - Timer timeit(logger, "autogenWorker"); - int idx = 0; - - for (auto result = fileq->try_pop(idx); !result.done(); result = fileq->try_pop(idx)) { - ++n; - auto &tree = indexed[idx]; - if (tree.file.data(gs).isPackage()) { - continue; - } - if (autogenVersion < autogen::AutogenVersion::VERSION_INCLUDE_RBI && tree.file.data(gs).isRBI()) { - continue; - } + workers.multiplexJob("runAutogen", [&gs, &opts, &indexed, &autogenCfg, crcBuilder, fileq, resultq]() { + AutogenResult out; + int n = 0; + int autogenVersion = opts.autogenVersion == 0 ? autogen::AutogenVersion::MAX_VERSION : opts.autogenVersion; + { + Timer timeit(logger, "autogenWorker"); + int idx = 0; - core::Context ctx(gs, core::Symbols::root(), tree.file); - auto pf = autogen::Autogen::generate(ctx, move(tree), autogenCfg, *crcBuilder); - tree = move(pf.tree); + for (auto result = fileq->try_pop(idx); !result.done(); result = fileq->try_pop(idx)) { + ++n; + auto &tree = indexed[idx]; + if (tree.file.data(gs).isPackage()) { + continue; + } + if (autogenVersion < autogen::AutogenVersion::VERSION_INCLUDE_RBI && tree.file.data(gs).isRBI()) { + continue; + } - AutogenResult::Serialized serialized; + core::Context ctx(gs, core::Symbols::root(), tree.file); + auto pf = autogen::Autogen::generate(ctx, move(tree), autogenCfg, *crcBuilder); + tree = move(pf.tree); - if (opts.print.Autogen.enabled) { - Timer timeit(logger, "autogenToString"); - serialized.strval = pf.toString(ctx, autogenVersion); - } - if (opts.print.AutogenMsgPack.enabled) { - Timer timeit(logger, "autogenToMsgpack"); - serialized.msgpack = pf.toMsgpack(ctx, autogenVersion, autogenCfg); - } + AutogenResult::Serialized serialized; - if (!tree.file.data(gs).isRBI()) { - // Exclude RBI files because they are not loadable and should not appear in - // auto-loader related output. - if (opts.print.AutogenSubclasses.enabled) { - Timer timeit(logger, "autogenSubclasses"); - serialized.subclasses = autogen::Subclasses::listAllSubclasses( - ctx, pf, opts.autogenSubclassesAbsoluteIgnorePatterns, - opts.autogenSubclassesRelativeIgnorePatterns); - } - if (opts.print.AutogenAutoloader.enabled) { - Timer timeit(logger, "autogenNamedDefs"); - autogen::DefTreeBuilder::addParsedFileDefinitions(ctx, autoloaderCfg, out.defTree, pf); - } - } + if (opts.print.Autogen.enabled) { + Timer timeit(logger, "autogenToString"); + serialized.strval = pf.toString(ctx, autogenVersion); + } + if (opts.print.AutogenMsgPack.enabled) { + Timer timeit(logger, "autogenToMsgpack"); + serialized.msgpack = pf.toMsgpack(ctx, autogenVersion, autogenCfg); + } - out.prints.emplace_back(idx, move(serialized)); + if (!tree.file.data(gs).isRBI()) { + // Exclude RBI files because they are not loadable and should not appear in + // auto-loader related output. + if (opts.print.AutogenSubclasses.enabled) { + Timer timeit(logger, "autogenSubclasses"); + serialized.subclasses = autogen::Subclasses::listAllSubclasses( + ctx, pf, opts.autogenSubclassesAbsoluteIgnorePatterns, + opts.autogenSubclassesRelativeIgnorePatterns); + } } + + out.prints.emplace_back(idx, move(serialized)); } + } - out.counters = getAndClearThreadCounters(); - resultq->push(move(out), n); - }); + out.counters = getAndClearThreadCounters(); + resultq->push(move(out), n); + }); - autogen::DefTree root; AutogenResult out; for (auto res = resultq->wait_pop_timed(out, WorkerPool::BLOCK_INTERVAL(), *logger); !res.done(); res = resultq->wait_pop_timed(out, WorkerPool::BLOCK_INTERVAL(), *logger)) { @@ -280,36 +274,19 @@ void runAutogen(const core::GlobalState &gs, options::Options &opts, const autog for (auto &print : out.prints) { merged[print.first] = move(print.second); } - if (opts.print.AutogenAutoloader.enabled) { - Timer timeit(logger, "autogenAutoloaderDefTreeMerge"); - root = autogen::DefTreeBuilder::merge(gs, move(root), move(*out.defTree)); - } } - { - Timer timeit(logger, "autogenDependencyDBPrint"); - for (auto &elem : merged) { - if (opts.print.Autogen.enabled) { - opts.print.Autogen.print(elem.strval); - } - if (opts.print.AutogenMsgPack.enabled) { - opts.print.AutogenMsgPack.print(elem.msgpack); - } - } - } - if (opts.print.AutogenAutoloader.enabled) { - { - Timer timeit(logger, "autogenMarkPackages"); - autogen::DefTreeBuilder::markPackages(gs, root, autoloaderCfg); - } + if (opts.print.Autogen.enabled || opts.print.AutogenMsgPack.enabled) { { - Timer timeit(logger, "autogenAutoloaderPrune"); - autogen::DefTreeBuilder::collapseSameFileDefs(gs, autoloaderCfg, root); - } - { - Timer timeit(logger, "autogenAutoloaderWrite"); - autogen::AutoloadWriter::writeAutoloads(gs, workers, autoloaderCfg, opts.print.AutogenAutoloader.outputPath, - root); + Timer timeit(logger, "autogenDependencyDBPrint"); + for (auto &elem : merged) { + if (opts.print.Autogen.enabled) { + opts.print.Autogen.print(elem.strval); + } + if (opts.print.AutogenMsgPack.enabled) { + opts.print.AutogenMsgPack.print(elem.msgpack); + } + } } } @@ -517,13 +494,24 @@ int realmain(int argc, char *argv[]) { gs->includeErrorSections = false; } gs->ruby3KeywordArgs = opts.ruby3KeywordArgs; - gs->lspExperimentalFastPathEnabled = opts.lspExperimentalFastPathEnabled; if (!opts.stripeMode) { // Definitions in multiple locations interact poorly with autoloader this error is enforced in Stripe code. if (opts.isolateErrorCode.empty()) { gs->suppressErrorClass(core::errors::Namer::MultipleBehaviorDefs.code); } } + + if (!opts.outOfOrderReferenceChecksEnabled) { + if (opts.isolateErrorCode.empty()) { + gs->suppressErrorClass(core::errors::Resolver::OutOfOrderConstantAccess.code); + } + } + + gs->trackUntyped = opts.trackUntyped; + gs->printingFileTable = opts.print.FileTableJson.enabled || opts.print.FileTableFullJson.enabled || + opts.print.FileTableProto.enabled || opts.print.FileTableFullProto.enabled || + opts.print.FileTableMessagePack.enabled || opts.print.FileTableFullMessagePack.enabled; + if (opts.suggestTyped) { gs->ignoreErrorClassForSuggestTyped(core::errors::Infer::SuggestTyped.code); gs->ignoreErrorClassForSuggestTyped(core::errors::Resolver::SigInFileWithoutSigil.code); @@ -660,10 +648,10 @@ int realmain(int argc, char *argv[]) { { core::UnfreezeNameTable unfreezeToEnterPackagerOptionsGS(*gs); core::packages::UnfreezePackages unfreezeToEnterPackagerOptionsPackageDB = gs->unfreezePackages(); - gs->setPackagerOptions(opts.secondaryTestPackageNamespaces, - opts.extraPackageFilesDirectoryUnderscorePrefixes, - opts.extraPackageFilesDirectorySlashPrefixes, - opts.packageSkipRBIExportEnforcementDirs, opts.stripePackagesHint); + gs->setPackagerOptions( + opts.secondaryTestPackageNamespaces, opts.extraPackageFilesDirectoryUnderscorePrefixes, + opts.extraPackageFilesDirectorySlashPrefixes, opts.packageSkipRBIExportEnforcementDirs, + opts.skipPackageImportVisibilityCheckFor, opts.stripePackagesHint); } packages = packager::Packager::findPackages(*gs, *workers, move(packages)); @@ -744,24 +732,21 @@ int realmain(int argc, char *argv[]) { gs->suppressErrorClass(core::errors::Resolver::StubConstant.code); gs->suppressErrorClass(core::errors::Resolver::RecursiveTypeAlias.code); - indexed = pipeline::package(*gs, move(indexed), opts, *workers); // Only need to compute FoundMethodHashes when running to compute a FileHash auto foundMethodHashes = nullptr; indexed = move(pipeline::name(*gs, move(indexed), opts, *workers, foundMethodHashes).result()); - autogen::AutoloaderConfig autoloaderCfg; { core::UnfreezeNameTable nameTableAccess(*gs); core::UnfreezeSymbolTable symbolAccess(*gs); indexed = resolver::Resolver::runConstantResolution(*gs, move(indexed), *workers); - autoloaderCfg = autogen::AutoloaderConfig::enterConfig(*gs, opts.autoloaderConfig); } autogen::AutogenConfig autogenCfg = {.behaviorAllowedInRBIsPaths = std::move(opts.autogenBehaviorAllowedInRBIFilesPaths)}; - runAutogen(*gs, opts, autoloaderCfg, autogenCfg, *workers, indexed); + runAutogen(*gs, opts, autogenCfg, *workers, indexed, opts.autogenConstantCacheConfig.changedFiles); #endif } else { // Only need to compute hashes when running to compute a FileHash @@ -781,6 +766,10 @@ int realmain(int argc, char *argv[]) { } } + // getAndClearHistogram ensures that we don't accidentally submit a high-cardinality histogram to statsd + auto untypedUsages = getAndClearHistogram("untyped.usages"); + pipeline::printFileTable(gs, opts, untypedUsages); + if (!opts.minimizeRBI.empty()) { #ifdef SORBET_REALMAIN_MIN logger->warn("--minimize-rbi is disabled in sorbet-orig for faster builds"); @@ -885,20 +874,9 @@ int realmain(int argc, char *argv[]) { FileOps::write(opts.storeState.c_str(), core::serialize::Serializer::store(*gs)); } - auto untypedSources = getAndClearHistogram("untyped.sources"); - if (opts.suggestSig) { - ENFORCE(sorbet::debug_mode); - vector> withNames; - long sum = 0; - for (auto e : untypedSources) { - withNames.emplace_back(core::SymbolRef::fromRaw(e.first).showFullName(*gs), e.second); - sum += e.second; - } - fast_sort(withNames, [](const auto &lhs, const auto &rhs) -> bool { return lhs.second > rhs.second; }); - for (auto &p : withNames) { - logger->error("Typing `{}` would impact {}% callsites({} out of {}).", p.first, p.second * 100.0 / sum, - p.second, sum); - } + auto untypedBlames = getAndClearHistogram("untyped.blames"); + if constexpr (sorbet::track_untyped_blame_mode) { + pipeline::printUntypedBlames(*gs, untypedBlames, opts); } } diff --git a/namer/BUILD b/namer/BUILD index e6fca09691..70daa3dac8 100644 --- a/namer/BUILD +++ b/namer/BUILD @@ -41,7 +41,7 @@ cc_test( "//local_vars", "//parser", "//rewriter", - "@doctest", - "@doctest//:doctest_main", + "@doctest//doctest", + "@doctest//doctest:main", ], ) diff --git a/namer/namer.cc b/namer/namer.cc index 0de50d5cfe..3e9293de71 100644 --- a/namer/namer.cc +++ b/namer/namer.cc @@ -7,10 +7,10 @@ #include "ast/desugar/Desugar.h" #include "ast/treemap/treemap.h" #include "class_flatten/class_flatten.h" -#include "common/Timer.h" #include "common/concurrency/ConcurrentQueue.h" #include "common/concurrency/WorkerPool.h" -#include "common/sort.h" +#include "common/sort/sort.h" +#include "common/timers/Timer.h" #include "core/Context.h" #include "core/FileHash.h" #include "core/FoundDefinitions.h" @@ -209,9 +209,94 @@ class SymbolFinder { for (auto &exp : klass.rhs) { findClassModifiers(ctx, klassName, exp); + addAncestor(ctx, klass, exp); + } + + if (!klass.ancestors.empty()) { + /* Superclass is typeAlias in parent scope, mixins are typeAlias in inner scope */ + for (auto &anc : klass.ancestors) { + if (!isValidAncestor(anc)) { + if (auto e = ctx.beginError(anc.loc(), core::errors::Namer::AncestorNotConstant)) { + e.setHeader("Superclasses must only contain constant literals"); + } + anc = ast::MK::EmptyTree(); + } + } + } + + auto &foundClass = klassName.klass(*foundDefs); + if (foundClass.name != core::Names::Constants::Root() && !ctx.file.data(ctx).isRBI() && + ast::BehaviorHelpers::checkClassDefinesBehavior(klass)) { + // TODO(dmitry) This won't find errors in fast-incremental mode. + foundClass.definesBehavior = true; + } + } + + void addAncestor(core::Context ctx, ast::ClassDef &klass, ast::ExpressionPtr &node) { + auto send = ast::cast_tree(node); + if (send == nullptr) { + ENFORCE(node.get() != nullptr); + return; + } + + ast::ClassDef::ANCESTORS_store *dest; + if (send->fun == core::Names::include()) { + dest = &klass.ancestors; + } else if (send->fun == core::Names::extend()) { + dest = &klass.singletonAncestors; + } else { + return; + } + if (!send->recv.isSelfReference()) { + // ignore `something.include` + return; + } + + const auto numPosArgs = send->numPosArgs(); + if (numPosArgs == 0) { + if (auto e = ctx.beginError(send->loc, core::errors::Namer::IncludeMutipleParam)) { + e.setHeader("`{}` requires at least one argument", send->fun.show(ctx)); + } + return; + } + + if (send->hasBlock()) { + if (auto e = ctx.beginError(send->loc, core::errors::Namer::IncludePassedBlock)) { + e.setHeader("`{}` can not be passed a block", send->fun.show(ctx)); + } + return; + } + + for (auto i = numPosArgs - 1; i >= 0; --i) { + // Reverse order is intentional: that's how Ruby does it. + auto &arg = send->getPosArg(i); + if (ast::isa_tree(arg)) { + continue; + } + if (arg.isSelfReference()) { + dest->emplace_back(arg.deepCopy()); + continue; + } + if (isValidAncestor(arg)) { + dest->emplace_back(arg.deepCopy()); + } else { + if (auto e = ctx.beginError(arg.loc(), core::errors::Namer::AncestorNotConstant)) { + e.setHeader("`{}` must only contain constant literals", send->fun.show(ctx)); + } + } } } + bool isValidAncestor(ast::ExpressionPtr &exp) { + if (ast::isa_tree(exp) || exp.isSelfReference() || ast::isa_tree(exp)) { + return true; + } + if (auto lit = ast::cast_tree(exp)) { + return isValidAncestor(lit->scope); + } + return false; + } + void preTransformBlock(core::Context ctx, ast::ExpressionPtr &block) { methodVisiStack.emplace_back(nullopt); } @@ -549,7 +634,12 @@ class SymbolFinder { return false; } - auto *cast = ast::cast_tree(asgn.rhs); + auto *recur = &asgn.rhs; + while (auto *outer = ast::cast_tree(*recur)) { + recur = &outer->expr; + } + + auto *cast = ast::cast_tree(*recur); if (cast == nullptr) { return false; } @@ -605,6 +695,15 @@ class SymbolFinder { } }; +using BehaviorLocs = InlinedVector; +using ClassBehaviorLocsMap = UnorderedMap; + +bool isBadHasAttachedClass(core::Context ctx, core::NameRef name) { + auto owner = ctx.owner.asClassOrModuleRef(); + return name == core::Names::Constants::AttachedClass() && owner != core::Symbols::Class() && + owner.data(ctx)->isClass() && !owner.data(ctx)->isSingletonClass(ctx); +} + /** * Defines symbols for all of the definitions found via SymbolFinder. Single threaded. */ @@ -678,8 +777,7 @@ class SymbolDefiner { "ClassOrModule symbols should always be entered first, so they should never need to mangle something else"); if (auto e = ctx.beginError(errorLoc, core::errors::Namer::ConstantKindRedefinition)) { auto prevSymbolKind = prettySymbolKind(ctx, prevSymbol.kind()); - if (prevSymbol.kind() == Kind::ClassOrModule && - !prevSymbol.asClassOrModuleRef().data(ctx)->isUndeclared()) { + if (prevSymbol.kind() == Kind::ClassOrModule && prevSymbol.asClassOrModuleRef().data(ctx)->isDeclared()) { prevSymbolKind = prevSymbol.asClassOrModuleRef().showKind(ctx); } @@ -961,9 +1059,12 @@ class SymbolDefiner { return symbol; } - auto implicitlyPrivate = ctx.owner.enclosingClass(ctx) == core::Symbols::root(); + // Methods defined at the top level default to private (on Object) + // Also, the `initialize` method defaults to private + auto implicitlyPrivate = + (ctx.owner.enclosingClass(ctx) == core::Symbols::root()) || + (!symbol.data(ctx)->owner.data(ctx)->attachedClass(ctx).exists() && name == core::Names::initialize()); if (implicitlyPrivate) { - // Methods defined at the top level default to private (on Object) symbol.data(ctx)->flags.isPrivate = true; } else { // All other methods default to public (their visibility might be changed later) @@ -1059,13 +1160,19 @@ class SymbolDefiner { } } } + + // Even though the declaration is erroneous, the class/module is technically "declared". + symbol.data(ctx)->setDeclared(); } else if (!isUnknown) { symbol.data(ctx)->setIsModule(isModule); + symbol.data(ctx)->setDeclared(); } + return symbol; } - core::ClassOrModuleRef insertClass(core::MutableContext ctx, const State &state, const core::FoundClass &klass) { + core::ClassOrModuleRef insertClass(core::MutableContext ctx, const State &state, const core::FoundClass &klass, + bool willDeleteOldDefs, ClassBehaviorLocsMap &classBehaviorLocs) { auto symbol = getClassSymbol(ctx, state, klass); if (klass.classKind == core::FoundClass::Kind::Class && !symbol.data(ctx)->superClass().exists() && @@ -1080,34 +1187,59 @@ class SymbolDefiner { symbol.data(ctx)->setSuperClass(core::Symbols::Net_Protocol()); } - // Skip adding locs when kind is Unknown, becaue that means it was a only a FoundClass for a - // class or static field scope (not the class def itself), and we want to treat those as - // usage locs, not definition locs. - // TODO(jez) This causes known problems on the fast path, where these locs can fail to be - // updated and crash. We've chosen the current approach because (1) it matches old behavior - // (2) adding O(files) locs for something like Opus is far too slow. const bool isUnknown = klass.classKind == core::FoundClass::Kind::Unknown; - // Don't add locs for ; 1) they aren't useful and 2) they'll end up with O(files in - // project) locs! - if (symbol != core::Symbols::root() && !isUnknown) { - symbol.data(ctx)->addLoc(ctx, ctx.locAt(klass.declLoc)); - } - auto singletonClass = symbol.data(ctx)->singletonClass(ctx); // force singleton class into existence - if (symbol != core::Symbols::root() && !isUnknown) { - singletonClass.data(ctx)->addLoc(ctx, ctx.locAt(klass.declLoc)); - } + const bool isModule = klass.classKind == core::FoundClass::Kind::Module; - // Reset resultType to nullptr for idempotency on the fast path--it will always be - // re-entered in resolver. - symbol.data(ctx)->resultType = nullptr; - singletonClass.data(ctx)->resultType = nullptr; - // TODO(jez) This is a gross hack. We are basically re-implementing the logic in - // singletonClass to reset the type template to what it used to be. - // Is there a better way to accomplish this? (This is largely the same as the bad locs problem above; - // we can probably be more principled about what state calling `singletonClass` sets up/resets.) - auto todo = core::make_type(core::Symbols::todo()); - auto tp = singletonClass.data(ctx)->members()[core::Names::Constants::AttachedClass()].asTypeMemberRef(); - tp.data(ctx)->resultType = core::make_type(tp, todo, todo); + // Don't add locs for ; 1) they aren't useful and 2) they'll end up with O(files in project) locs! + if (symbol != core::Symbols::root()) { + // If the kind is unknown, it means it was only a FoundClass for a class or static field scope, not + // the class def itself. We want to generally treat these as usage locs, not definition locs. + // + // At best, we only want to keep one loc in the codebase per unknown class for perf reasons. We don't + // want to store O(files) locs for something like Opus. So if the existing loc (which would have been + // brought into existence by getClassSymbol()) is not from this file, we don't add a new loc. + // + // If the unknown class loc is from the same file, it's still possible that it is from a real + // definition in that file. In which case, we check the declared bit on the class. + // We only set the loc if the class is not declared. + bool updateLoc = + !isUnknown || (!symbol.data(ctx)->isDeclared() && symbol.data(ctx)->loc().file() == ctx.file); + if (updateLoc) { + symbol.data(ctx)->addLoc(ctx, ctx.locAt(klass.declLoc)); + } + + if (!isUnknown) { + if (klass.definesBehavior) { + auto &behaviorLocs = classBehaviorLocs[symbol]; + behaviorLocs.emplace_back(ctx.locAt(klass.declLoc)); + symbol.data(ctx)->flags.isBehaviorDefining = true; + } + + auto singletonClass = symbol.data(ctx)->singletonClass(ctx); // force singleton class into existence + singletonClass.data(ctx)->addLoc(ctx, ctx.locAt(klass.declLoc)); + + // This willDeleteOldDefs condition is a hack to improve performance when editing within a method body. + // Ideally, we would be able to make finalizeSymbols fast/incremental enough to run on all edits. + if (willDeleteOldDefs) { + // Reset resultType to nullptr for idempotency on the fast path--it will always be + // re-entered in resolver. + symbol.data(ctx)->resultType = nullptr; + singletonClass.data(ctx)->resultType = nullptr; + // TODO(jez) This is a gross hack. We are basically re-implementing the logic in + // singletonClass to reset the type template to what it used to be. + // Is there a better way to accomplish this? (This is largely the same as the bad locs problem + // above; we can probably be more principled about what state calling `singletonClass` sets + // up/resets.) + if (!isModule) { + auto todo = core::make_type(core::Symbols::todo()); + auto tp = singletonClass.data(ctx) + ->members()[core::Names::Constants::AttachedClass()] + .asTypeMemberRef(); + tp.data(ctx)->resultType = core::make_type(tp, todo, todo); + } + } + } + } // make sure we've added a static init symbol so we have it ready for the flatten pass later if (symbol == core::Symbols::root()) { @@ -1139,9 +1271,7 @@ class SymbolDefiner { // T.noreturn here represents the zero-length list of subclasses of this sealed class. // We will use T.any to record subclasses when they're resolved. - vector targs{core::Types::bottom()}; - sealedSubclasses.data(ctx)->resultType = - core::make_type(core::Symbols::Set(), move(targs)); + sealedSubclasses.data(ctx)->resultType = core::Types::setOf(core::Types::bottom()); } if (fun == core::Names::declareInterface() || fun == core::Names::declareAbstract()) { symbolData->flags.isAbstract = true; @@ -1509,25 +1639,26 @@ class SymbolDefiner { SymbolDefiner(const core::FoundDefinitions &foundDefs, optional oldFoundHashes) : foundDefs(foundDefs), oldFoundHashes(move(oldFoundHashes)) {} - SymbolDefiner::State enterClassDefinitions(core::MutableContext ctx) { + SymbolDefiner::State enterClassDefinitions(core::MutableContext ctx, bool willDeleteOldDefs, + ClassBehaviorLocsMap &classBehaviorLocs) { SymbolDefiner::State state; state.definedClasses.reserve(foundDefs.klasses().size()); for (const auto &klass : foundDefs.klasses()) { - state.definedClasses.emplace_back( - insertClass(ctx.withOwner(getOwnerSymbol(state, klass.owner)), state, klass)); + state.definedClasses.emplace_back(insertClass(ctx.withOwner(getOwnerSymbol(state, klass.owner)), state, + klass, willDeleteOldDefs, classBehaviorLocs)); } return state; } void enterNewDefinitions(core::MutableContext ctx, SymbolDefiner::State &&state) { - // We have to defer defining "deletable" symbols until this (second) phase of incremental + // We have to defer defining non-class constant symbols until this (second) phase of incremental // namer so that we don't delete and immediately re-enter a symbol (possibly keeping it // alive, if it had multiple locs at the time of deletion) before SymbolDefiner has had a // chance to process _all_ files. - for (auto ref : foundDefs.deletableDefinitions()) { + for (auto ref : foundDefs.nonClassConstants()) { switch (ref.kind()) { case core::FoundDefinitionRef::Kind::StaticField: { const auto &staticField = ref.staticField(foundDefs); @@ -1580,9 +1711,6 @@ class SymbolDefiner { } }; -using BehaviorLocs = InlinedVector; -using ClassBehaviorLocsMap = UnorderedMap; - /** * Inserts newly created symbols (from SymbolDefiner) into a tree. */ @@ -1649,71 +1777,6 @@ class TreeSymbolizer { return localExpr; } - void addAncestor(core::Context ctx, ast::ClassDef &klass, ast::ExpressionPtr &node) { - auto send = ast::cast_tree(node); - if (send == nullptr) { - ENFORCE(node.get() != nullptr); - return; - } - - ast::ClassDef::ANCESTORS_store *dest; - if (send->fun == core::Names::include()) { - dest = &klass.ancestors; - } else if (send->fun == core::Names::extend()) { - dest = &klass.singletonAncestors; - } else { - return; - } - if (!send->recv.isSelfReference()) { - // ignore `something.include` - return; - } - - const auto numPosArgs = send->numPosArgs(); - if (numPosArgs == 0) { - if (auto e = ctx.beginError(send->loc, core::errors::Namer::IncludeMutipleParam)) { - e.setHeader("`{}` requires at least one argument", send->fun.show(ctx)); - } - return; - } - - if (send->hasBlock()) { - if (auto e = ctx.beginError(send->loc, core::errors::Namer::IncludePassedBlock)) { - e.setHeader("`{}` can not be passed a block", send->fun.show(ctx)); - } - return; - } - - for (auto i = numPosArgs - 1; i >= 0; --i) { - // Reverse order is intentional: that's how Ruby does it. - auto &arg = send->getPosArg(i); - if (ast::isa_tree(arg)) { - continue; - } - if (arg.isSelfReference()) { - dest->emplace_back(arg.deepCopy()); - continue; - } - if (isValidAncestor(arg)) { - dest->emplace_back(arg.deepCopy()); - } else { - if (auto e = ctx.beginError(arg.loc(), core::errors::Namer::AncestorNotConstant)) { - e.setHeader("`{}` must only contain constant literals", send->fun.show(ctx)); - } - } - } - } - - bool isValidAncestor(ast::ExpressionPtr &exp) { - if (ast::isa_tree(exp) || exp.isSelfReference() || ast::isa_tree(exp)) { - return true; - } - if (auto lit = ast::cast_tree(exp)) { - return isValidAncestor(lit->scope); - } - return false; - } - public: TreeSymbolizer() {} @@ -1766,21 +1829,6 @@ class TreeSymbolizer { auto allowMissing = true; ENFORCE(ctx.state.lookupStaticInitForClass(klass.symbol, allowMissing).exists()); - for (auto &exp : klass.rhs) { - addAncestor(ctx, klass, exp); - } - - if (!klass.ancestors.empty()) { - /* Superclass is typeAlias in parent scope, mixins are typeAlias in inner scope */ - for (auto &anc : klass.ancestors) { - if (!isValidAncestor(anc)) { - if (auto e = ctx.beginError(anc.loc(), core::errors::Namer::AncestorNotConstant)) { - e.setHeader("Superclasses must only contain constant literals"); - } - anc = ast::MK::EmptyTree(); - } - } - } auto loc = klass.declLoc; ast::InsSeq::STATS_store retSeqs; retSeqs.emplace_back(std::move(tree)); @@ -1792,13 +1840,6 @@ class TreeSymbolizer { retSeqs.emplace_back(ast::MK::KeepForIDE(loc.copyWithZeroLength(), klass.ancestors.front().deepCopy())); } - if (klass.symbol != core::Symbols::root() && !ctx.file.data(ctx).isRBI() && - ast::BehaviorHelpers::checkClassDefinesBehavior(klass)) { - // TODO(dmitry) This won't find errors in fast-incremental mode. - auto &locs = classBehaviorLocs[klass.symbol]; - locs.emplace_back(ctx.locAt(klass.declLoc)); - } - tree = ast::MK::InsSeq(loc, std::move(retSeqs), ast::MK::EmptyTree()); } @@ -1945,6 +1986,30 @@ class TreeSymbolizer { ast::make_expression(asgn.loc, std::move(asgn.lhs), std::move(send))); } + if (isBadHasAttachedClass(ctx, typeName->cnst)) { + // We could go out of our way to try to not even define the type member, + // in an attempt to maintain an invariant that is only ever defined on + // - modules + // - class singleton classes + // - ::Class itself + // But then in GlobalPass, resolveTypeMember would simply mangle rename things so that + // the symbol pointed to a type member symbol anyways (instead of a + // static field or type alias symbol). So let's just report an error and then continue + // to let the type member be defined, shedding a single tear that we can't enforce the + // invariant we might have otherwise wanted. + if (auto e = ctx.beginError(asgn.loc, core::errors::Namer::HasAttachedClassInClass)) { + // This is the simple way to explain the error to users, even though the + // condition above is more complicated. The one exception to the way this error + // is phrased: `::Class` itself, which is a `class`, is allowed to use `has_attached_class!`. + // + // But since `::Class` is final (even according to the VM), and since we've + // already marked `::Class` with `has_attached_class!`, it's not worth leaking + // that special case to the user. + e.setHeader("`{}` can only be used inside a `{}`, not a `{}`", + core::Names::declareHasAttachedClass().show(ctx), "module", "class"); + } + } + bool isTypeTemplate = send->fun == core::Names::typeTemplate(); auto onSymbol = isTypeTemplate ? ctx.owner.asClassOrModuleRef().data(ctx)->lookupSingletonClass(ctx) : ctx.owner; @@ -2086,8 +2151,6 @@ class TreeSymbolizer { } } } - - ClassBehaviorLocsMap classBehaviorLocs; }; vector findSymbols(const core::GlobalState &gs, vector trees, @@ -2191,6 +2254,37 @@ void populateFoundDefHashes(core::Context ctx, core::FoundDefinitions &foundDefs } } +void findConflictingClassDefs(const core::GlobalState &gs, ClassBehaviorLocsMap &classBehaviorLocs) { + vector> conflicts; + for (auto &[ref, locs] : classBehaviorLocs) { + if (locs.size() < 2) { + continue; + } + fast_sort(locs, [](const auto &lhs, const auto &rhs) -> bool { return lhs.file() < rhs.file(); }); + // In rare cases we see multiple defs in same file. Ignore them. + auto last = unique(locs.begin(), locs.end(), + [](const auto &lhs, const auto &rhs) -> bool { return lhs.file() == rhs.file(); }); + locs.erase(last, locs.end()); + if (locs.size() < 2) { + continue; + } + conflicts.emplace_back(make_pair(ref, std::move(locs))); + } + classBehaviorLocs.clear(); + + fast_sort(conflicts, [](const auto &lhs, const auto &rhs) -> bool { return lhs.first.id() < rhs.first.id(); }); + for (const auto &[ref, locs] : conflicts) { + core::Loc mainLoc = locs[0]; + core::Context ctx(gs, core::Symbols::root(), mainLoc.file()); + if (auto e = ctx.beginError(mainLoc.offsets(), core::errors::Namer::MultipleBehaviorDefs)) { + e.setHeader("`{}` has behavior defined in multiple files", ref.show(ctx)); + for (auto it = locs.begin() + 1; it != locs.end(); ++it) { + e.addErrorLine(*it, "Previous definition"); + } + } + } +} + ast::ParsedFilesOrCancelled defineSymbols(core::GlobalState &gs, vector allFoundDefinitions, WorkerPool &workers, UnorderedMap &&oldFoundHashesForFiles, @@ -2201,7 +2295,9 @@ ast::ParsedFilesOrCancelled defineSymbols(core::GlobalState &gs, vector incrementalDefinitions; + auto willDeleteOldDefs = !oldFoundHashesForFiles.empty(); for (auto &fileFoundDefinitions : allFoundDefinitions) { foundMethods += fileFoundDefinitions.names->methods().size(); count++; @@ -2219,8 +2315,8 @@ ast::ParsedFilesOrCancelled defineSymbols(core::GlobalState &gs, vector() : std::move(frefIt->second); SymbolDefiner symbolDefiner(*fileFoundDefinitions.names, move(oldFoundHashes)); - auto state = symbolDefiner.enterClassDefinitions(ctx); - if (!oldFoundHashesForFiles.empty()) { + auto state = symbolDefiner.enterClassDefinitions(ctx, willDeleteOldDefs, classBehaviorLocs); + if (willDeleteOldDefs) { symbolDefiner.deleteOldDefinitions(ctx, state); } incrementalDefinitions[fref] = move(state); @@ -2229,6 +2325,8 @@ ast::ParsedFilesOrCancelled defineSymbols(core::GlobalState &gs, vector trees; - ClassBehaviorLocsMap classBehaviorLocs; }; -void mergeClassBehaviorLocs(const core::GlobalState &gs, ClassBehaviorLocsMap &merged, - ClassBehaviorLocsMap &threadRes) { - Timer timeit(gs.tracer(), "naming.symbolizeTreesMergeClass"); - if (merged.empty()) { - swap(merged, threadRes); - return; - } - for (auto [ref, threadLocs] : threadRes) { - auto &mergedLocs = merged[ref]; - mergedLocs.insert(mergedLocs.end(), make_move_iterator(threadLocs.begin()), - make_move_iterator(threadLocs.end())); - } - threadRes.clear(); -} - -void findConflictingClassDefs(const core::GlobalState &gs, ClassBehaviorLocsMap &map) { - vector> conflicts; - for (auto &[ref, locs] : map) { - if (locs.size() < 2) { - continue; - } - fast_sort(locs, [](const auto &lhs, const auto &rhs) -> bool { return lhs.file() < rhs.file(); }); - // In rare cases we see multiple defs in same file. Ignore them. - auto last = unique(locs.begin(), locs.end(), - [](const auto &lhs, const auto &rhs) -> bool { return lhs.file() == rhs.file(); }); - locs.erase(last, locs.end()); - if (locs.size() < 2) { - continue; - } - conflicts.emplace_back(make_pair(ref, std::move(locs))); - } - map.clear(); - - fast_sort(conflicts, [](const auto &lhs, const auto &rhs) -> bool { return lhs.first.id() < rhs.first.id(); }); - for (const auto &[ref, locs] : conflicts) { - core::Loc mainLoc = locs[0]; - core::Context ctx(gs, core::Symbols::root(), mainLoc.file()); - if (auto e = ctx.beginError(mainLoc.offsets(), core::errors::Namer::MultipleBehaviorDefs)) { - e.setHeader("`{}` has behavior defined in multiple files", ref.show(ctx)); - for (auto it = locs.begin() + 1; it != locs.end(); ++it) { - e.addErrorLine(*it, "Previous definition"); - } - } - } -} - vector symbolizeTrees(const core::GlobalState &gs, vector trees, WorkerPool &workers) { Timer timeit(gs.tracer(), "naming.symbolizeTrees"); @@ -2326,14 +2377,12 @@ vector symbolizeTrees(const core::GlobalState &gs, vectorpush(move(output), output.trees.size()); } }); trees.clear(); { - ClassBehaviorLocsMap classBehaviorLocs; SymbolizeTreesResult threadResult; for (auto result = resultq->wait_pop_timed(threadResult, WorkerPool::BLOCK_INTERVAL(), gs.tracer()); !result.done(); @@ -2341,10 +2390,8 @@ vector symbolizeTrees(const core::GlobalState &gs, vector bool { return lhs.file < rhs.file; }); return trees; diff --git a/namer/test/namer_test.cc b/namer/test/namer_test.cc index 0382f9ea36..d00b8c612b 100644 --- a/namer/test/namer_test.cc +++ b/namer/test/namer_test.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" // has to go first as it violates our requirements #include "ast/ast.h" diff --git a/packager/VisibilityChecker.cc b/packager/VisibilityChecker.cc index b58b890077..4c2bab9ace 100644 --- a/packager/VisibilityChecker.cc +++ b/packager/VisibilityChecker.cc @@ -4,8 +4,8 @@ #include "absl/synchronization/blocking_counter.h" #include "ast/treemap/treemap.h" #include "common/concurrency/ConcurrentQueue.h" -#include "common/formatting.h" -#include "common/sort.h" +#include "common/sort/sort.h" +#include "common/strings/formatting.h" #include "core/Context.h" #include "core/errors/packager.h" #include "core/errors/resolver.h" @@ -18,6 +18,18 @@ namespace sorbet::packager { namespace { +static core::SymbolRef getEnumClassForEnumValue(const core::GlobalState &gs, core::SymbolRef sym) { + if (sym.isStaticField(gs) && sym.owner(gs).isClassOrModule()) { + auto owner = sym.owner(gs); + // There's a hidden class like `MyEnum::X$1` between `MyEnum::X` and `T::Enum` in the ancestor chain. + if (owner.asClassOrModuleRef().data(gs)->superClass() == core::Symbols::T_Enum()) { + return owner; + } + } + + return core::Symbols::noSymbol(); +} + // For each __package.rb file, traverse the resolved tree and apply the visibility annotations to the symbols. class PropagateVisibility final { const core::packages::PackageInfo &package; @@ -28,8 +40,14 @@ class PropagateVisibility final { } void recursiveExportSymbol(core::GlobalState &gs, bool firstSymbol, core::ClassOrModuleRef klass) { - // We only mark symbols from this package. - if (!this->definedByThisPackage(gs, klass)) { + // We only mark symbols from this package. However, there's a + // tough case where non-behavior-defining "namespace-like" + // constants might get attributed to other packages (since we + // don't have a canonical location, so we use the first place + // we see them... which might be in a subpackage) and + // therefore this might stop too soon. That's why we only stop + // recursing if the thing is actually behavior-defining. + if (!this->definedByThisPackage(gs, klass) && klass.data(gs)->flags.isBehaviorDefining) { return; } @@ -49,7 +67,11 @@ class PropagateVisibility final { } void exportParentNamespace(core::GlobalState &gs, core::ClassOrModuleRef owner) { - while (owner.exists() && !owner.data(gs)->flags.isExported && this->definedByThisPackage(gs, owner)) { + // Implicitly export parent namespace (symbol owner) until we hit the root of the package. + // NOTE that we make an exception for namespaces that define behavior: these CANNOT get exported implicitly, + // as that violates the private-by-default paradigm. + while (owner.exists() && !owner.data(gs)->flags.isExported && !owner.data(gs)->flags.isBehaviorDefining && + this->definedByThisPackage(gs, owner)) { owner.data(gs)->flags.isExported = true; owner = owner.data(gs)->owner; } @@ -192,6 +214,22 @@ class PropagateVisibility final { e.addErrorLine(sym.loc(ctx), "Defined here"); } } + + // If sym is an enum value, it can't be exported directly. Instead, its wrapping enum class must be exported. + auto enumClass = getEnumClassForEnumValue(ctx.state, sym); + if (enumClass.exists()) { + if (auto e = ctx.beginError(loc, core::errors::Packager::InvalidExport)) { + std::string enumClassName = enumClass.show(ctx); + e.setHeader("Cannot export enum value `{}`. Instead, export the entire enum `{}`", sym.show(ctx), + enumClassName); + e.addErrorLine(sym.loc(ctx), "Defined here"); + + e.addAutocorrect(core::AutocorrectSuggestion{ + fmt::format("Export `{}`", enumClassName), + {core::AutocorrectSuggestion::Edit{core::Loc{package.fullLoc().file(), loc}, + fmt::format("export {}", enumClassName)}}}); + } + } } PropagateVisibility(const core::packages::PackageInfo &package) : package{package} {} @@ -294,6 +332,24 @@ class PropagateVisibility final { core::MutableContext ctx{gs, core::Symbols::root(), f.file}; ast::TreeWalk::apply(ctx, pass, f.tree); + // if we used `export_all`, then there were no `export` + // directives in the previous pass; we should instead export + // the package root + if (package.exportAll()) { + // we check if these exist because if no constants were + // defined in the package then we might not have actually + // ever created the relevant namespaces + auto pkgRoot = package.getPackageScope(gs); + if (pkgRoot.exists()) { + pass.recursiveExportSymbol(gs, true, pkgRoot); + } + + auto pkgTestRoot = package.getPackageTestScope(gs); + if (pkgTestRoot.exists()) { + pass.recursiveExportSymbol(gs, true, pkgTestRoot); + } + } + return f; } }; @@ -377,7 +433,13 @@ class VisibilityCheckerPass final { } else { e.addErrorLine(definedHereLoc, "Defined here"); } - if (auto exp = pkg.addExport(ctx, lit.symbol)) { + + auto symToExport = lit.symbol; + auto enumClass = getEnumClassForEnumValue(ctx.state, symToExport); + if (enumClass.exists()) { + symToExport = enumClass; + } + if (auto exp = pkg.addExport(ctx, symToExport)) { e.addAutocorrect(std::move(exp.value())); } if (!db.errorHint().empty()) { diff --git a/packager/packager.cc b/packager/packager.cc index 0683a9a70c..9fadac4f3a 100644 --- a/packager/packager.cc +++ b/packager/packager.cc @@ -8,11 +8,12 @@ #include "common/FileOps.h" #include "common/concurrency/ConcurrentQueue.h" #include "common/concurrency/WorkerPool.h" -#include "common/formatting.h" -#include "common/sort.h" +#include "common/sort/sort.h" +#include "common/strings/formatting.h" #include "core/AutocorrectSuggestion.h" #include "core/Unfreeze.h" #include "core/errors/packager.h" +#include "core/packages/MangledName.h" #include "core/packages/PackageInfo.h" #include #include @@ -67,17 +68,6 @@ struct FullyQualifiedName { } }; -class NameFormatter final { - const core::GlobalState &gs; - -public: - NameFormatter(const core::GlobalState &gs) : gs(gs) {} - - void operator()(std::string *out, core::NameRef name) const { - out->append(name.shortName(gs)); - } -}; - struct PackageName { core::LocOffsets loc; core::NameRef mangledName = core::NameRef::noName(); @@ -86,7 +76,7 @@ struct PackageName { // Pretty print the package's (user-observable) name (e.g. Foo::Bar) string toString(const core::GlobalState &gs) const { - return absl::StrJoin(fullName.parts, "::", NameFormatter(gs)); + return absl::StrJoin(fullName.parts, "::", core::packages::NameFormatter(gs)); } bool operator==(const PackageName &rhs) const { @@ -184,8 +174,12 @@ class PackageInfoImpl final : public core::packages::PackageInfo { return declLoc_; } - bool strictAutoloaderCompatibility() const { - return strictAutoloaderCompatibility_; + bool legacyAutoloaderCompatibility() const { + return legacyAutoloaderCompatibility_; + } + + bool exportAll() const { + return exportAll_; } // The possible path prefixes associated with files in the package, including path separator at end. @@ -202,8 +196,16 @@ class PackageInfoImpl final : public core::packages::PackageInfo { // These are copied into every package that imports this package. vector exports_; - // Whether the code in this package is compatible for path-based autoloading. - bool strictAutoloaderCompatibility_; + // Code in this package is _completely incompatible_ for path-based autoloading, and only works with the 'legacy' + // Sorbet-generated autoloader. + bool legacyAutoloaderCompatibility_; + + // Whether this package should just export everything + bool exportAll_; + + // The other packages to which this package is visible. If this vector is empty, then it means + // the package is fully public and can be imported by anything. + vector visibleTo_; // PackageInfoImpl is the only implementation of PackageInfoImpl const static PackageInfoImpl &from(const core::packages::PackageInfo &pkg) { @@ -351,6 +353,13 @@ class PackageInfoImpl final : public core::packages::PackageInfo { } return rv; } + vector> visibleTo() const { + vector> rv; + for (auto &v : visibleTo_) { + rv.emplace_back(v.fullName.parts); + } + return rv; + } std::optional importsPackage(core::NameRef mangledName) const { if (!mangledName.exists()) { @@ -418,11 +427,7 @@ PackageName getPackageName(core::MutableContext ctx, ast::UnresolvedConstantLit pName.fullTestPkgName = pName.fullName.withPrefix(TEST_NAME); // Foo::Bar => Foo_Bar_Package - auto mangledName = absl::StrCat(absl::StrJoin(pName.fullName.parts, "_", NameFormatter(ctx)), core::PACKAGE_SUFFIX); - - auto utf8Name = ctx.state.enterNameUTF8(mangledName); - auto packagerName = ctx.state.freshNameUnique(core::UniqueNameKind::Packager, utf8Name, 1); - pName.mangledName = ctx.state.enterNameConstant(packagerName); + pName.mangledName = core::packages::MangledName::mangledNameFromParts(ctx.state, pName.fullName.parts); return pName; } @@ -504,8 +509,10 @@ class PackageNamespaces final { vector bounds; vector nameParts; + vector namePartsLocs; vector> curPkg; core::NameRef foundTestNS = core::NameRef::noName(); + core::LocOffsets foundTestNSLoc; static constexpr uint16_t SKIP_BOUND_VAL = 0; @@ -517,18 +524,20 @@ class PackageNamespaces final { } int depth() const { + ENFORCE(nameParts.size() == namePartsLocs.size()); return nameParts.size(); } - const vector currentConstantName() const { - if (!foundTestNS.exists()) { - return nameParts; + const vector> currentConstantName() const { + vector> res; + + if (foundTestNS.exists()) { + res.emplace_back(foundTestNS, foundTestNSLoc); } - auto res = vector{}; - res.emplace_back(foundTestNS); - for (const auto nm : nameParts) { - res.emplace_back(nm); + ENFORCE(nameParts.size() == namePartsLocs.size()); + for (size_t i = 0; i < nameParts.size(); ++i) { + res.emplace_back(nameParts[i], namePartsLocs[i]); } return res; } @@ -550,7 +559,7 @@ class PackageNamespaces final { return false; } - void pushName(core::Context ctx, core::NameRef name) { + void pushName(core::Context ctx, core::NameRef name, core::LocOffsets loc) { if (skips > 0) { skips++; return; @@ -560,12 +569,14 @@ class PackageNamespaces final { if (isTestFile && boundsEmpty && !foundTestNS.exists()) { if (isPrimaryTestNamespace(name)) { foundTestNS = name; + foundTestNSLoc = loc; return; } else if (!isTestNamespace(ctx, name)) { // Inside a test file, but not inside a test namespace. Set bounds such that // begin == end, stopping any subsequent search. bounds.emplace_back(begin, end); nameParts.emplace_back(name); + namePartsLocs.emplace_back(loc); begin = end = 0; return; } @@ -582,6 +593,7 @@ class PackageNamespaces final { bounds.emplace_back(begin, end); nameParts.emplace_back(name); + namePartsLocs.emplace_back(loc); auto lb = std::lower_bound(packages.begin() + begin, packages.begin() + end, nameParts, [ctx](auto pkgNr, auto &nameParts) -> bool { @@ -617,6 +629,7 @@ class PackageNamespaces final { if (isTestFile && bounds.size() == 0 && foundTestNS.exists()) { ENFORCE(nameParts.empty()); foundTestNS = core::NameRef::noName(); + foundTestNSLoc = core::LocOffsets::none(); return; } @@ -638,12 +651,14 @@ class PackageNamespaces final { end = bounds.back().second; bounds.pop_back(); nameParts.pop_back(); + namePartsLocs.pop_back(); } ~PackageNamespaces() { // Book-keeping sanity checks ENFORCE(bounds.empty()); ENFORCE(nameParts.empty()); + ENFORCE(namePartsLocs.empty()); ENFORCE(begin == 0); ENFORCE(end = packages.size()); ENFORCE(curPkg.empty()); @@ -665,7 +680,7 @@ class EnforcePackagePrefix final { int errorDepth = 0; int rootConsts = 0; bool useTestNamespace = false; - vector tmpNameParts; + vector> tmpNameParts; public: EnforcePackagePrefix(core::Context ctx, const PackageInfoImpl &pkg, bool isTestFile) @@ -800,13 +815,17 @@ class EnforcePackagePrefix final { ENFORCE(errorDepth == 0); errorDepth++; if (auto e = ctx.beginError(loc, core::errors::Packager::DefinitionPackageMismatch)) { - e.setHeader( - "Class or method behavior may not be defined outside of the enclosing package namespace `{}`", - fmt::map_join(pkgName, "::", [&](const auto &nr) { return nr.show(ctx); })); + e.setHeader("This file must only define behavior in enclosing package `{}`", + fmt::map_join(pkgName, "::", [&](const auto &nr) { return nr.show(ctx); })); + const auto &constantName = namespaces.currentConstantName(); + e.addErrorLine(ctx.locAt(constantName.back().second), "Defining behavior in `{}` instead:", + fmt::map_join(constantName, "::", [&](const auto &nr) { return nr.first.show(ctx); })); + e.addErrorLine(pkg.declLoc(), "Enclosing package `{}` declared here", + fmt::map_join(pkgName, "::", [&](const auto &nr) { return nr.show(ctx); })); if (packageForNamespace.exists()) { - auto &namespaceParts = ctx.state.packageDB().getPackageInfo(packageForNamespace).fullName(); - e.addErrorNote("Attempting to define class or method behavior in package namespace `{}`", - fmt::map_join(namespaceParts, "::", [&](const auto &nr) { return nr.show(ctx); })); + auto &packageInfo = ctx.state.packageDB().getPackageInfo(packageForNamespace); + e.addErrorLine(packageInfo.declLoc(), "Package `{}` declared here", + constantName.back().first.show(ctx)); } } } @@ -817,7 +836,7 @@ class EnforcePackagePrefix final { ENFORCE(tmpNameParts.empty()); auto prevDepth = namespaces.depth(); while (lit != nullptr) { - tmpNameParts.emplace_back(lit->cnst); + tmpNameParts.emplace_back(lit->cnst, lit->loc); auto *scope = ast::cast_tree(lit->scope); lit = ast::cast_tree(lit->scope); if (scope != nullptr) { @@ -828,12 +847,12 @@ class EnforcePackagePrefix final { } if (rootConsts == 0) { for (auto it = tmpNameParts.rbegin(); it != tmpNameParts.rend(); ++it) { - namespaces.pushName(ctx, *it); + namespaces.pushName(ctx, it->first, it->second); } } if (prevDepth == 0 && isTestFile && namespaces.depth() > 0) { - useTestNamespace = isPrimaryTestNamespace(tmpNameParts.back()) || + useTestNamespace = isPrimaryTestNamespace(tmpNameParts.back().first) || !isSecondaryTestNamespace(ctx, pkg.name.fullName.parts[0]); } @@ -883,7 +902,8 @@ class EnforcePackagePrefix final { auto reqMangledName = namespaces.packageForNamespace(); if (reqMangledName.exists()) { auto &reqPkg = gs.packageDB().getPackageInfo(reqMangledName); - auto givenNamespace = absl::StrJoin(namespaces.currentConstantName(), "::", NameFormatter(gs)); + auto givenNamespace = + absl::StrJoin(namespaces.currentConstantName(), "::", core::packages::NameFormatter(gs)); e.addErrorLine(reqPkg.declLoc(), "Must belong to this package, given constant name `{}`", givenNamespace); } } @@ -975,6 +995,10 @@ struct PackageInfoFinder { send.addPosArg(prependName(move(importArg), core::Names::Constants::PackageSpecRegistry())); } + if (send.fun == core::Names::exportAll() && send.numPosArgs() == 0) { + info->exportAll_ = true; + } + if (send.fun == core::Names::autoloader_compatibility() && send.numPosArgs() == 1) { // Parse autoloader_compatibility DSL and set strict bit on PackageInfoImpl if configured auto *compatibilityAnnotationLit = ast::cast_tree(send.getPosArg(0)); @@ -995,16 +1019,45 @@ struct PackageInfoFinder { } auto compatibilityAnnotation = compatibilityAnnotationLit->asString(); - if (compatibilityAnnotation != core::Names::strict() && compatibilityAnnotation != core::Names::legacy()) { + if (compatibilityAnnotation != core::Names::legacy()) { if (auto e = ctx.beginError(send.loc, core::errors::Packager::InvalidConfiguration)) { - e.setHeader("Argument to `{}` must be either 'strict' or 'legacy'", send.fun.show(ctx)); + if (compatibilityAnnotation == core::Names::strict()) { + e.setHeader("The 'strict' argument has been deprecated as an argument to `{}`", + send.fun.show(ctx)); + e.addErrorNote("If you wish to mark your " + "package as strictly path-based-autoloading compatible, do not provide an " + "autoloader_compatibility annotation"); + } else { + e.setHeader("Argument to `{}` can only be 'legacy'", send.fun.show(ctx)); + } } return; } - if (compatibilityAnnotation == core::Names::strict()) { - info->strictAutoloaderCompatibility_ = true; + if (compatibilityAnnotation == core::Names::legacy()) { + info->legacyAutoloaderCompatibility_ = true; + } + } + + if (send.fun == core::Names::visible_to() && send.numPosArgs() == 1) { + if (auto target = verifyConstant(ctx, send.fun, send.getPosArg(0))) { + auto name = getPackageName(ctx, target); + ENFORCE(name.mangledName.exists()); + + if (name.mangledName == info->name.mangledName) { + if (auto e = ctx.beginError(target->loc, core::errors::Packager::NoSelfImport)) { + e.setHeader("Useless `{}`, because {} cannot import itself", "visible_to", + info->name.toString(ctx)); + } + } + + auto importArg = move(send.getPosArg(0)); + send.removePosArg(0); + ENFORCE(send.numPosArgs() == 0); + send.addPosArg(prependName(move(importArg), core::Names::Constants::PackageSpecRegistry())); + + info->visibleTo_.emplace_back(move(name)); } } } @@ -1072,6 +1125,18 @@ struct PackageInfoFinder { return; } + if (info->exportAll()) { + // we're only here because exports exist, which means if + // `exportAll` is set then we've got conflicting + // information about export; flag the exports as wrong + for (auto it = exported.begin(); it != exported.end(); ++it) { + if (auto e = ctx.beginError(it->fqn.loc.offsets(), core::errors::Packager::ExportConflict)) { + e.setHeader("Package `{}` declares `{}` and therefore should not use explicit exports", + info->name.toString(ctx), "export_all!"); + } + } + } + fast_sort(exported, Export::lexCmp); vector dupInds; for (auto it = exported.begin(); it != exported.end(); ++it) { @@ -1113,6 +1178,8 @@ struct PackageInfoFinder { case core::Names::export_().rawId(): case core::Names::restrict_to_service().rawId(): case core::Names::autoloader_compatibility().rawId(): + case core::Names::visible_to().rawId(): + case core::Names::exportAll().rawId(): return true; default: return false; @@ -1268,6 +1335,38 @@ ast::ParsedFile validatePackage(core::Context ctx, ast::ParsedFile file) { return file; } + auto &pkgInfo = PackageInfoImpl::from(absPkg); + bool skipImportVisibilityCheck = packageDB.skipImportVisibilityCheckFor(pkgInfo.mangledName()); + + if (!skipImportVisibilityCheck) { + for (auto &i : pkgInfo.importedPackageNames) { + auto &otherPkg = packageDB.getPackageInfo(i.name.mangledName); + + // this might mean the other package doesn't exist, but that + // should have been caught already + if (!otherPkg.exists()) { + continue; + } + + const auto &visibleTo = otherPkg.visibleTo(); + if (visibleTo.empty()) { + continue; + } + + bool allowed = absl::c_any_of(otherPkg.visibleTo(), + [&absPkg](const auto &other) { return other == absPkg.fullName(); }); + + if (!allowed) { + if (auto e = ctx.beginError(i.name.loc, core::errors::Packager::ImportNotVisible)) { + e.setHeader("Package `{}` includes explicit visibility modifiers and cannot be imported from `{}`", + otherPkg.show(ctx), absPkg.show(ctx)); + e.addErrorNote("Please consult with the owning team before adding a `{}` line to the package `{}`", + "visible_to", otherPkg.show(ctx)); + } + } + } + } + // Sanity check: __package.rb files _must_ be typed: strict if (file.file.data(ctx).originalSigil < core::StrictLevel::Strict) { if (auto e = ctx.beginError(core::LocOffsets{0, 0}, core::errors::Packager::PackageFileMustBeStrict)) { @@ -1445,17 +1544,12 @@ void Packager::setPackageNameOnFiles(core::GlobalState &gs, const vector Packager::run(core::GlobalState &gs, WorkerPool &workers, vector files) { + ENFORCE(!gs.runningUnderAutogen, "Packager pass does not run in autogen"); + Timer timeit(gs.tracer(), "packager"); files = findPackages(gs, workers, std::move(files)); setPackageNameOnFiles(gs, files); - if (gs.runningUnderAutogen) { - // Autogen only requires package metadata. Remove the package files. - auto it = std::remove_if(files.begin(), files.end(), - [&gs](auto &file) -> bool { return file.file.data(gs).isPackage(); }); - files.erase(it, files.end()); - return files; - } // Step 2: // * Find package files and rewrite them into virtual AST mappings. @@ -1517,7 +1611,7 @@ class ImportFormatter final { ImportFormatter(const core::GlobalState &gs) : gs(gs) {} void operator()(std::string *out, const vector &name) const { - fmt::format_to(back_inserter(*out), "\"{}\"", absl::StrJoin(name, "::", NameFormatter(gs))); + fmt::format_to(back_inserter(*out), "\"{}\"", absl::StrJoin(name, "::", core::packages::NameFormatter(gs))); } }; @@ -1551,7 +1645,8 @@ class PackageInfoFormatter final { const auto &pkg = gs.packageDB().getPackageInfo(mangledName); out->append("{{"); out->append("\"name\":"); - fmt::format_to(back_inserter(*out), "\"{}\",", absl::StrJoin(pkg.fullName(), "::", NameFormatter(gs))); + fmt::format_to(back_inserter(*out), "\"{}\",", + absl::StrJoin(pkg.fullName(), "::", core::packages::NameFormatter(gs))); out->append("\"imports\":["); fmt::format_to(back_inserter(*out), absl::StrJoin(pkg.imports(), ",", ImportFormatter(gs))); out->append("],\"testImports\":["); diff --git a/packager/packager.h b/packager/packager.h index c1fd1e00c9..2e99eccfe8 100644 --- a/packager/packager.h +++ b/packager/packager.h @@ -59,6 +59,8 @@ class Packager final { // For each file, set its package name. static void setPackageNameOnFiles(core::GlobalState &gs, const std::vector &files); + static core::SymbolRef getEnumClassForEnumValue(const core::GlobalState &gs, core::SymbolRef sym); + Packager() = delete; }; } // namespace sorbet::packager diff --git a/packager/rbi_gen.cc b/packager/rbi_gen.cc index deef8eddcc..03a90c9a7a 100644 --- a/packager/rbi_gen.cc +++ b/packager/rbi_gen.cc @@ -7,7 +7,7 @@ #include "common/FileOps.h" #include "common/concurrency/ConcurrentQueue.h" #include "common/concurrency/WorkerPool.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "core/GlobalState.h" #include "packager/packager.h" @@ -327,7 +327,7 @@ class RBIExporter final { if (methodData->flags.isPrivate) { if (methodData->owner.data(gs)->isSingletonClass(gs)) { visibility = "private_class_method "; - } else { + } else if (methodData->name != core::Names::initialize()) { visibility = "private "; } } else if (methodData->flags.isProtected) { diff --git a/parser/BUILD b/parser/BUILD index 4a7d24d2ac..ee771e2b0e 100644 --- a/parser/BUILD +++ b/parser/BUILD @@ -83,7 +83,7 @@ cc_test( visibility = ["//tools:__pkg__"], deps = [ ":parser", - "@doctest", - "@doctest//:doctest_main", + "@doctest//doctest", + "@doctest//doctest:main", ], ) diff --git a/parser/Builder.cc b/parser/Builder.cc index 399da3b648..0b71baeff9 100644 --- a/parser/Builder.cc +++ b/parser/Builder.cc @@ -377,6 +377,10 @@ class Builder::Impl { return true; } + if (parser::isa_node(nd)) { + return true; + } + if (auto *pair = parser::cast_node(nd)) { return parser::isa_node(pair->key.get()); } @@ -992,6 +996,14 @@ class Builder::Impl { return make_unique(tokLoc(dots)); } + unique_ptr forwarded_restarg(const token *star) { + return make_unique(tokLoc(star)); + } + + unique_ptr forwarded_kwrestarg(const token *dstar) { + return make_unique(tokLoc(dstar)); + } + unique_ptr gvar(const token *tok) { return make_unique(tokLoc(tok), gs_.enterNameUTF8(tok->view())); } @@ -1548,7 +1560,7 @@ class Builder::Impl { // only 1 child: String auto firstPart = parts.front().get(); if (auto *s = parser::cast_node(firstPart)) { - return make_unique(s->loc, s->val); + return make_unique(tokLoc(begin, end), s->val); } else { return nullptr; } @@ -2206,6 +2218,16 @@ ForeignPtr forwarded_args(SelfPtr builder, const token *dots) { return build->toForeign(build->forwarded_args(dots)); } +ForeignPtr forwarded_restarg(SelfPtr builder, const token *star) { + auto build = cast_builder(builder); + return build->toForeign(build->forwarded_restarg(star)); +} + +ForeignPtr forwarded_kwrestarg(SelfPtr builder, const token *dstar) { + auto build = cast_builder(builder); + return build->toForeign(build->forwarded_kwrestarg(dstar)); +} + ForeignPtr gvar(SelfPtr builder, const token *tok) { auto build = cast_builder(builder); return build->toForeign(build->gvar(tok)); @@ -2740,6 +2762,8 @@ struct ruby_parser::builder Builder::interface = { for_, forward_arg, forwarded_args, + forwarded_restarg, + forwarded_kwrestarg, gvar, hash_pattern, ident, diff --git a/parser/parser/cc/driver.cc b/parser/parser/cc/driver.cc index 3862e630a4..8fc24a9aad 100644 --- a/parser/parser/cc/driver.cc +++ b/parser/parser/cc/driver.cc @@ -104,8 +104,8 @@ bool base_driver::rewind_if_different_line(token_t token1, token_t token2) { return true; } -// TODO(jez) This can quite easily get out of hand performance-wise. The major selling point of -// LR parsers is that they admit linear-time implementations. +// This can quite easily get out of hand performance-wise. The major selling point of LR parsers is +// that they admit linear-time implementations. // // For the time being (read: until we start seeing performance problems in practice), introducing // arbitrary-size backtracking like this method does is probably fine, because diff --git a/parser/parser/cc/grammars/typedruby.ypp b/parser/parser/cc/grammars/typedruby.ypp index 7bbc818ae0..5db7bdafdb 100644 --- a/parser/parser/cc/grammars/typedruby.ypp +++ b/parser/parser/cc/grammars/typedruby.ypp @@ -36,7 +36,6 @@ using namespace std::string_literals; %define api.namespace {ruby_parser::bison::TYPEDRUBY} %define api.prefix {TYPEDRUBY} %define api.value.type { union parser_value } -// TODO(jez) Does this degrade parser performance? %define api.location.type { location } %define api.token.constructor false %define parse.error verbose @@ -871,6 +870,7 @@ int yylex(parser::semantic_type *lval, ruby_parser::location *lloc, ruby_parser: driver.lex.set_state_expr_beg(); driver.lex.unset_command_start(); driver.pattern_variables.push(); + driver.pattern_hash_keys.push(); $$ = driver.lex.context.inKwarg; driver.lex.context.inKwarg = true; @@ -885,6 +885,7 @@ int yylex(parser::semantic_type *lval, ruby_parser::location *lloc, ruby_parser: driver.lex.set_state_expr_beg(); driver.lex.unset_command_start(); driver.pattern_variables.push(); + driver.pattern_hash_keys.push(); $$ = driver.lex.context.inKwarg; driver.lex.context.inKwarg = true; @@ -1932,6 +1933,14 @@ lbrace_cmd_block_start: { $$ = driver.alloc.node_list(driver.build.splat(self, $1, $2)); } + | tSTAR + { + if (!driver.lex.is_declared_anonymous_restarg()) { + driver.diagnostics.emplace_back(dlevel::ERROR, dclass::NoAnonymousRestArg, $1); + } + + $$ = driver.alloc.node_list(driver.build.forwarded_restarg(self, $1)); + } | args tCOMMA arg_value { auto &list = $1; @@ -1951,6 +1960,16 @@ lbrace_cmd_block_start: list->emplace_back(driver.build.splat(self, $3, $4)); $$ = list; } + | args tCOMMA tSTAR + { + if (!driver.lex.is_declared_anonymous_restarg()) { + driver.diagnostics.emplace_back(dlevel::ERROR, dclass::NoAnonymousRestArg, $3); + } + + auto &list = $1; + list->emplace_back(driver.build.forwarded_restarg(self, $3)); + $$ = list; + } mrhs_arg: mrhs { @@ -3216,26 +3235,15 @@ lcurly_block_start: $$ = $1; $$->emplace_back($2); } - | p_args_head tSTAR tIDENTIFIER - { - $$ = $1; - $$->emplace_back(driver.build.match_rest(self, $2, $3)); - } - | p_args_head tSTAR tIDENTIFIER tCOMMA p_args_post + | p_args_head p_rest { $$ = $1; - $$->emplace_back(driver.build.match_rest(self, $2, $3)); - $$->concat($5); - } - | p_args_head tSTAR - { - $$ = $1; - $$->emplace_back(driver.build.match_rest(self, $2, nullptr)); + $$->emplace_back($2); } - | p_args_head tSTAR tCOMMA p_args_post + | p_args_head p_rest tCOMMA p_args_post { $$ = $1; - $$->emplace_back(driver.build.match_rest(self, $2, nullptr)); + $$->emplace_back($2); $$->concat($4); } | p_args_tail @@ -3414,7 +3422,7 @@ lcurly_block_start: auto non_lvar = driver.build.accessible(self, $2); $$ = driver.build.pin(self, $1, non_lvar); } - p_expr_ref: tCARET tLPAREN expr_value tRPAREN + p_expr_ref: tCARET tLPAREN expr_value rparen { auto expr = driver.build.begin(self, $2, $3, $4); $$ = driver.build.pin(self, $1, expr); @@ -3741,22 +3749,11 @@ regexp_contents: // nothing { $$ = driver.build.ident(self, $1); } - | tIVAR - { - $$ = driver.build.ivar(self, $1); - } - | tGVAR - { - $$ = driver.build.gvar(self, $1); - } | tCONSTANT { $$ = driver.build.const_(self, $1); } - | tCVAR - { - $$ = driver.build.cvar(self, $1); - } + | nonlocal_var keyword_variable: kNIL { @@ -4186,6 +4183,8 @@ f_opt_paren_args: f_paren_args } | kwrest_mark { + driver.lex.declare_anonymous_kwrestarg(); + auto kwrestarg = driver.build.kwrestarg(self, $1, nullptr); $$ = driver.alloc.node_list(kwrestarg); @@ -4241,6 +4240,8 @@ f_opt_paren_args: f_paren_args } | restarg_mark { + driver.lex.declare_anonymous_restarg(); + auto restarg = driver.build.restarg(self, $1, nullptr); $$ = driver.alloc.node_list(restarg); @@ -4379,9 +4380,17 @@ f_opt_paren_args: f_paren_args { $$ = driver.build.kwsplat(self, $1, $2); } + | tDSTAR + { + if (!driver.lex.is_declared_anonymous_kwrestarg()) { + driver.diagnostics.emplace_back(dlevel::ERROR, dclass::NoAnonymousKwrestArg, $1); + } + + $$ = driver.build.forwarded_kwrestarg(self, $1); + } operation: tIDENTIFIER | tCONSTANT | tFID - operation2: tIDENTIFIER | tCONSTANT | tFID | op + operation2: operation | op operation3: tIDENTIFIER | tFID | op dot_or_colon: call_op | tCOLON2 call_op: tDOT @@ -4415,7 +4424,7 @@ f_opt_paren_args: f_paren_args $$ = $2; } - trailer: | tNL | tCOMMA + trailer: opt_nl | tCOMMA term: tSEMI { diff --git a/parser/parser/cc/lexer.rl b/parser/parser/cc/lexer.rl index fdb1a538af..225fcc16d0 100644 --- a/parser/parser/cc/lexer.rl +++ b/parser/parser/cc/lexer.rl @@ -214,7 +214,6 @@ int lexer::compare_indent_level(token_t left, token_t right) { if (leftChar != rightChar) { // mismatched indent. give up and say equal - // TODO(jez) Might want to handle this case better return 0; } @@ -3102,6 +3101,22 @@ bool lexer::is_declared_anonymous_args() { return is_declared(ANONYMOUS_BLOCKARG); } +void lexer::declare_anonymous_restarg() { + declare(ANONYMOUS_RESTARG); +} + +bool lexer::is_declared_anonymous_restarg() { + return is_declared(ANONYMOUS_RESTARG); +} + +void lexer::declare_anonymous_kwrestarg() { + declare(ANONYMOUS_KWRESTARG); +} + +bool lexer::is_declared_anonymous_kwrestarg() { + return is_declared(ANONYMOUS_KWRESTARG); +} + optional_size lexer::dedentLevel() { // We erase @dedentLevel as a precaution to avoid accidentally // using a stale value. diff --git a/parser/parser/codegen/generate_diagnostics.cc b/parser/parser/codegen/generate_diagnostics.cc index 3126624e9d..16cab96598 100644 --- a/parser/parser/codegen/generate_diagnostics.cc +++ b/parser/parser/codegen/generate_diagnostics.cc @@ -76,6 +76,8 @@ tuple MESSAGES[] = { {"ForwardArgAfterRestArg", "... after rest argument"}, {"InvalidIdToGet", "identifier {} is not valid to get"}, {"NoAnonymousBlockArg", "no anonymous block parameter"}, + {"NoAnonymousRestArg", "no anonymous rest parameter"}, + {"NoAnonymousKwrestArg", "no anonymous keyword rest parameter"}, // Error recovery hints {"DedentedEnd", "Hint: this {} token might not be properly closed"}, diff --git a/parser/parser/include/ruby_parser/builder.hh b/parser/parser/include/ruby_parser/builder.hh index abd818e004..f9ccf41def 100644 --- a/parser/parser/include/ruby_parser/builder.hh +++ b/parser/parser/include/ruby_parser/builder.hh @@ -86,6 +86,8 @@ struct builder { const token *do_, ForeignPtr body, const token *end); ForeignPtr (*forward_arg)(SelfPtr builder, const token *begin, const token *dots, const token *end); ForeignPtr (*forwarded_args)(SelfPtr builder, const token *dots); + ForeignPtr (*forwarded_restarg)(SelfPtr builder, const token *star); + ForeignPtr (*forwarded_kwrestarg)(SelfPtr builder, const token *dstar); ForeignPtr (*gvar)(SelfPtr builder, const token *tok); ForeignPtr (*hash_pattern)(SelfPtr builder, const token *begin, const node_list *kwargs, const token *end); ForeignPtr (*ident)(SelfPtr builder, const token *tok); diff --git a/parser/parser/include/ruby_parser/lexer.hh b/parser/parser/include/ruby_parser/lexer.hh index 4ae4ae5b34..00f90dddf7 100644 --- a/parser/parser/include/ruby_parser/lexer.hh +++ b/parser/parser/include/ruby_parser/lexer.hh @@ -97,6 +97,8 @@ private: const std::string FORWARD_ARGS = "FORWARD_ARGS"; const std::string ANONYMOUS_BLOCKARG = "ANONYMOUS_BLOCKARG"; + const std::string ANONYMOUS_RESTARG = "ANONYMOUS_RESTARG"; + const std::string ANONYMOUS_KWRESTARG = "ANONYMOUS_KWRESTARG"; // State before =begin / =end block comment int cs_before_block_comment; @@ -234,6 +236,10 @@ public: bool is_declared_forward_args(); void declare_anonymous_args(); bool is_declared_anonymous_args(); + void declare_anonymous_restarg(); + bool is_declared_anonymous_restarg(); + void declare_anonymous_kwrestarg(); + bool is_declared_anonymous_kwrestarg(); optional_size dedentLevel(); }; diff --git a/parser/test/parser_test.cc b/parser/test/parser_test.cc index c5ae4c28c2..62966519e1 100644 --- a/parser/test/parser_test.cc +++ b/parser/test/parser_test.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" // has to go first as it violates our requirements #include "common/common.h" #include "core/Error.h" diff --git a/parser/tools/generate_ast.cc b/parser/tools/generate_ast.cc index f6328988f1..fa10801bb9 100644 --- a/parser/tools/generate_ast.cc +++ b/parser/tools/generate_ast.cc @@ -306,6 +306,18 @@ NodeDef nodes[] = { "forwarded_args", vector(), }, + // "*" argument forwarding in call site + { + "ForwardedRestArg", + "forwarded_restarg", + vector(), + }, + // "**" argument forwarding in call site + { + "ForwardedKwrestArg", + "forwarded_kwrestarg", + vector(), + }, // float literal like "1.2" { "Float", diff --git a/payload/payload.cc b/payload/payload.cc index 3ec0e9d27a..c7cf127b8e 100644 --- a/payload/payload.cc +++ b/payload/payload.cc @@ -1,6 +1,6 @@ #include "payload/payload.h" #include "common/Random.h" -#include "common/Timer.h" +#include "common/timers/Timer.h" #include "core/serialize/serialize.h" #include "payload/binary/binary.h" #include "payload/text/text.h" @@ -98,7 +98,6 @@ bool retainGlobalState(core::GlobalState &gs, const realmain::options::Options & // Verify that no other GlobalState was written to kvstore between when we read GlobalState and wrote it // into the databaase. if (kvstoreUnchangedSinceGsCreation(gs, maybeGsBytes.data)) { - Timer timeit(gs.tracer(), "write_global_state.kvstore"); // Generate a new UUID, since this GS has changed since it was read. gs.kvstoreUuid = Random::uniformU4(); kvstore->write(GLOBAL_STATE_KEY, core::serialize::Serializer::storePayloadAndNameTable(gs)); diff --git a/proto/File.proto b/proto/File.proto index b36ea2dc38..b9b78e6d02 100644 --- a/proto/File.proto +++ b/proto/File.proto @@ -30,6 +30,9 @@ message File { StrictLevel strict = 3; StrictLevel min_error_level = 4; CompiledLevel compiled = 5; + // Note: a value of zero and an unset field look the same. + int32 untyped_usages = 6; + string pkg = 7; } message FileTable { diff --git a/rbi/core/argf.rbi b/rbi/core/argf.rbi index a33094d8ae..9c5f5752f4 100644 --- a/rbi/core/argf.rbi +++ b/rbi/core/argf.rbi @@ -226,22 +226,6 @@ module ARGF def self.each_codepoint(*several_variants, &blk) #This is a stub, used for indexing end - # This is a deprecated alias for each_line. - def self.lines(*args, &blk) - #This is a stub, used for indexing - end - # This is a deprecated alias for each_byte. - def self.bytes(&blk) - #This is a stub, used for indexing - end - # This is a deprecated alias for each_char. - def self.chars(&blk) - #This is a stub, used for indexing - end - # This is a deprecated alias for each_codepoint. - def self.codepoints(&blk) - #This is a stub, used for indexing - end # ARGF.read([length [, outbuf]]) -> string, outbuf, or nil # # Reads _length_ bytes from ARGF. The files named on the command line diff --git a/rbi/core/array.rbi b/rbi/core/array.rbi index 463563a005..e1feccd1a1 100644 --- a/rbi/core/array.rbi +++ b/rbi/core/array.rbi @@ -444,16 +444,10 @@ class Array < Object # See also # [`Array#concat`](https://docs.ruby-lang.org/en/2.7.0/Array.html#method-i-concat). sig do - params( - arg0: T::Enumerable[Elem], - ) - .returns(T::Array[Elem]) - end - sig do - params( - arg0: T::Array[Elem], + type_parameters(:T).params( + arg0: T::Enumerable[T.type_parameter(:T)], ) - .returns(T::Array[Elem]) + .returns(T::Array[T.any(Elem, T.type_parameter(:T))]) end def +(arg0); end @@ -1279,18 +1273,20 @@ class Array < Object .returns(Elem) end sig do - params( + type_parameters(:Fallback) + .params( arg0: Integer, - arg1: Elem, + arg1: T.type_parameter(:Fallback), ) - .returns(Elem) + .returns(T.any(Elem, T.type_parameter(:Fallback))) end sig do - params( + type_parameters(:Fallback) + .params( arg0: Integer, - blk: T.proc.params(arg0: Integer).returns(Elem), + blk: T.proc.params(arg0: Integer).returns(T.type_parameter(:Fallback)), ) - .returns(Elem) + .returns(T.any(Elem, T.type_parameter(:Fallback))) end def fetch(arg0, arg1=T.unsafe(nil), &blk); end @@ -1566,6 +1562,20 @@ class Array < Object end def intersection(*arrays); end + + # Returns `true` if the array and `other_ary` have at least one element in + # common, otherwise returns `false`: + # + # ```ruby + # a = [ 1, 2, 3 ] + # b = [ 3, 4, 5 ] + # c = [ 5, 6, 7 ] + # a.intersect?(b) #=> true + # a.intersect?(c) #=> false + # ``` + sig { params(other_ary: T.untyped).returns(T::Boolean) } + def intersect?(other_ary); end + # Returns a string created by converting each element of the array to a # string, separated by the given `separator`. If the `separator` is `nil`, it # uses current `$,`. If both the `separator` and `$,` are `nil`, it uses an diff --git a/rbi/core/class.rbi b/rbi/core/class.rbi index 0b0da6b527..1559d3f034 100644 --- a/rbi/core/class.rbi +++ b/rbi/core/class.rbi @@ -69,6 +69,13 @@ # obj--->OtherClass---------->(OtherClass)-----------... # ``` class Class < Module + # Intentionally does not write extend T::Generic so we don't pollute the + # stdlib with an ancestor that doesn't exist at runtime. It doesn't matter, + # because RBI files are not typechecked anyways. + has_attached_class!(:out) + + ### TODO(jez) After T::Class change: Use `T.attached_class` in `allocate` + # Allocates space for a new object of *class*'s class and does not call # initialize on the new instance. The returned object must be an instance of # *class*. @@ -89,6 +96,23 @@ class Class < Module sig {returns(T.untyped)} def allocate(); end + + # Returns the object for which the receiver is the singleton class. + # + # Raises a `TypeError` if the class is not a singleton class. + # + # ```ruby + # class Foo; end + # + # Foo.singleton_class.attached_object #=> Foo + # Foo.attached_object #=> TypeError: `Foo' is not a singleton class + # Foo.new.singleton_class.attached_object #=> # + # TrueClass.attached_object #=> TypeError: `TrueClass' is not a singleton class + # NilClass.attached_object #=> TypeError: `NilClass' is not a singleton class + # ``` + sig { returns(BasicObject) } + def attached_object; end + ### Sorbet hijacks Class#new to re-use the sig from MyClass#initialize when creating new instances of a class. ### This method must be here so that all calls to MyClass.new aren't forced to take 0 arguments. @@ -97,9 +121,26 @@ class Class < Module # to create a new object of *class*'s class, then invokes that object's # initialize method, passing it *args*. This is the method that ends up # getting called whenever an object is constructed using `.new`. - sig {params(args: T.untyped, blk: T.untyped).returns(T.untyped)} + sig {params(args: T.untyped, blk: T.untyped).returns(T.attached_class)} def new(*args, &blk); end + # Creates a new anonymous (unnamed) class with the given superclass (or + # Object if no parameter is given). You can give a class a name by assigning + # the class object to a constant. + # + # If a block is given, it is passed the class object, and the block is + # evaluated in the context of this class like class_eval. + sig { params(blk: T.untyped).returns(T::Class[Object]) } + sig do + type_parameters(:Parent) + .params( + super_class: T.all(T::Class[T.anything], T.type_parameter(:Parent)), + blk: T.untyped + ) + .returns(T.all(T::Class[T.anything], T.type_parameter(:Parent))) + end + def self.new(super_class = Object, &blk); end + # Callback invoked whenever a subclass of the current class is created. # # Example: @@ -126,7 +167,7 @@ class Class < Module # ``` sig do params( - arg0: Class, + arg0: T::Class[T.anything], ) .returns(T.untyped) end @@ -157,7 +198,7 @@ class Class < Module # B.subclasses #=> [C] # C.subclasses #=> [] # ``` - sig { returns(T::Array[Class]) } + sig { returns(T::Array[T::Class[T.anything]]) } def subclasses(); end # Returns the superclass of *class*, or `nil`. @@ -176,26 +217,26 @@ class Class < Module # ```ruby # BasicObject.superclass #=> nil # ``` - sig {returns(T.nilable(Class))} + sig {returns(T.nilable(T::Class[T.anything]))} def superclass(); end sig {void} sig do params( - superclass: Class, + superclass: T::Class[T.anything], ) .void end sig do params( - blk: T.proc.params(arg0: Class).returns(BasicObject), + blk: T.proc.params(arg0: T::Class[T.anything]).returns(BasicObject), ) .void end sig do params( - superclass: Class, - blk: T.proc.params(arg0: Class).returns(BasicObject), + superclass: T::Class[T.anything], + blk: T.proc.params(arg0: T::Class[T.anything]).returns(BasicObject), ) .void end diff --git a/rbi/core/data.rbi b/rbi/core/data.rbi index 759d29bf59..e31f4ce053 100644 --- a/rbi/core/data.rbi +++ b/rbi/core/data.rbi @@ -1,6 +1,488 @@ # typed: __STDLIB_INTERNAL -# This is a deprecated class, base class for C extensions using -# Data\_Make\_Struct or Data\_Wrap\_Struct. +# Class Data provides a convenient way to define simple classes for value-alike +# objects. +# +# The simplest example of usage: +# +# ```ruby +# Measure = Data.define(:amount, :unit) +# +# # Positional arguments constructor is provided +# distance = Measure.new(100, 'km') +# #=> # +# +# # Keyword arguments constructor is provided +# weight = Measure.new(amount: 50, unit: 'kg') +# #=> # +# +# # Alternative form to construct an object: +# speed = Measure[10, 'mPh'] +# #=> # +# +# # Works with keyword arguments, too: +# area = Measure[amount: 1.5, unit: 'm^2'] +# #=> # +# +# # Argument accessors are provided: +# distance.amount #=> 100 +# distance.unit #=> "km" +# ``` +# +# Constructed object also has a reasonable definitions of +# [`#==`](https://docs.ruby-lang.org/en/3.2/Data.html#method-i-3D-3D) +# operator, [`#to_h`](https://docs.ruby-lang.org/en/3.2/Data.html#method-i-to_h) +# hash conversion, and +# [`#deconstruct`](https://docs.ruby-lang.org/en/3.2/Data.html#method-i-deconstruct)/#deconstruct_keys +# to be used in pattern matching. +# +# [`::define`](https://docs.ruby-lang.org/en/3.2/Data.html#method-c-define) +# method accepts an optional block and evaluates it in the context of the +# newly defined class. That allows to define additional methods: +# +# ```ruby +# Measure = Data.define(:amount, :unit) do +# def <=>(other) +# return unless other.is_a?(self.class) && other.unit == unit +# amount <=> other.amount +# end +# +# include Comparable +# end +# +# Measure[3, 'm'] < Measure[5, 'm'] #=> true +# Measure[3, 'm'] < Measure[5, 'kg'] +# # comparison of Measure with Measure failed (ArgumentError) +# ``` +# +# Data provides no member writers, or enumerators: it is meant to be a storage +# for immutable atomic values. But note that if some of data members is of a +# mutable class, Data does no additional immutability enforcement: +# +# ```ruby +# Event = Data.define(:time, :weekdays) +# event = Event.new('18:00', %w[Tue Wed Fri]) +# #=> # +# +# # There is no #time= or #weekdays= accessors, but changes are +# # still possible: +# event.weekdays << 'Sat' +# event +# #=> # +# ``` +# +# See also [`Struct`](https://docs.ruby-lang.org/en/3.2/Struct.html), which is +# a similar concept, but has more container-alike API, allowing to change +# contents of the object and enumerate it. class Data < Object + + # Defines a new Data class. + # + # ```ruby + # measure = Data.define(:amount, :unit) + # #=> # + # measure.new(1, 'km') + # #=> # + # + # # It you store the new class in the constant, it will + # # affect #inspect and will be more natural to use: + # Measure = Data.define(:amount, :unit) + # #=> Measure + # Measure.new(1, 'km') + # #=> # + # ``` + # + # Note that member-less Data is acceptable and might be a useful technique + # for defining several homogenous data classes, like + # + # ```ruby + # class HTTPFetcher + # Response = Data.define(:body) + # NotFound = Data.define + # # ... implementation + # end + # ``` + # + # Now, different kinds of responses from +HTTPFetcher+ would have consistent + # representation: + # + # ```ruby + # # + # # + # ``` + # + # And are convenient to use in pattern matching: + # + # ```ruby + # case fetcher.get(url) + # in HTTPFetcher::Response(body) + # # process body variable + # in HTTPFetcher::NotFound + # # handle not found case + # end + # ``` + sig do + params( + arg0: T.any(Symbol, String), + arg1: T.any(Symbol, String), + blk: T.untyped, + ) + .returns(Data) + end + def self.define(arg0, *arg1, &blk); end + + # Returns an array of member names of the data class: + # + # ```ruby + # Measure = Data.define(:amount, :unit) + # Measure.members # => [:amount, :unit] + # ``` + sig { returns(T::Array[Symbol]) } + def self.members; end + + # Constructors for classes defined with + # [::define](https://docs.ruby-lang.org/en/3.2/Data.html#method-c-define) + # accept both positional and keyword arguments. + # + # ```ruby + # Measure = Data.define(:amount, :unit) + # + # Measure.new(1, 'km') + # #=> # + # Measure.new(amount: 1, unit: 'km') + # #=> # + # + # # Alternative shorter intialization with [] + # Measure[1, 'km'] + # #=> # + # Measure[amount: 1, unit: 'km'] + # #=> # + # ``` + # + # All arguments are mandatory (unlike [Struct](https://docs.ruby-lang.org/en/3.2/Struct.html)), + # and converted to keyword arguments: + # + # ```ruby + # Measure.new(amount: 1) + # # in `initialize': missing keyword: :unit (ArgumentError) + # + # Measure.new(1) + # # in `initialize': missing keyword: :unit (ArgumentError) + # ``` + # + # Note that `Measure#initialize` always receives keyword arguments, and that + # mandatory arguments are checked in `initialize`, not in `new`. This can be + # important for redefining initialize in order to convert arguments or provide + # defaults: + # + # ```ruby + # Measure = Data.define(:amount, :unit) do + # NONE = Data.define + # + # def initialize(amount:, unit: NONE.new) + # super(amount: Float(amount), unit:) + # end + # end + # + # Measure.new('10', 'km') # => # + # Measure.new(10_000) # => #> + # ```ruby + # + # ``` + # static VALUE + # rb_data_initialize_m(int argc, const VALUE *argv, VALUE self) + # { + # VALUE klass = rb_obj_class(self); + # rb_struct_modify(self); + # VALUE members = struct_ivar_get(klass, id_members); + # size_t num_members = RARRAY_LEN(members); + # + # if (argc == 0) { + # if (num_members > 0) { + # rb_exc_raise(rb_keyword_error_new("missing", members)); + # } + # return Qnil; + # } + # if (argc > 1 || !RB_TYPE_P(argv[0], T_HASH)) { + # rb_error_arity(argc, 0, 0); + # } + # + # if (RHASH_SIZE(argv[0]) < num_members) { + # VALUE missing = rb_ary_diff(members, rb_hash_keys(argv[0])); + # rb_exc_raise(rb_keyword_error_new("missing", missing)); + # } + # + # struct struct_hash_set_arg arg; + # rb_mem_clear((VALUE *)RSTRUCT_CONST_PTR(self), num_members); + # arg.self = self; + # arg.unknown_keywords = Qnil; + # rb_hash_foreach(argv[0], struct_hash_set_i, (VALUE)&arg); + # // Freeze early before potentially raising, so that we don't leave an + # // unfrozen copy on the heap, which could get exposed via ObjectSpace. + # OBJ_FREEZE_RAW(self); + # if (arg.unknown_keywords != Qnil) { + # rb_exc_raise(rb_keyword_error_new("unknown", arg.unknown_keywords)); + # } + # return Qnil; + # } + # ``` + sig { params(args: T.untyped).returns(Data) } + sig { params(kwargs: T.untyped).returns(Data) } + def new(*args, **kwargs); end + + sig { params(args: T.untyped).returns(Data) } + sig { params(kwargs: T.untyped).returns(Data) } + def self.[](*args, **kwargs); end + + # Returns `true` if `other` is the same class as `self`, and all members are + # equal. + # + # Examples: + # + # ```ruby + # Measure = Data.define(:amount, :unit) + # + # Measure[1, 'km'] == Measure[1, 'km'] #=> true + # Measure[1, 'km'] == Measure[2, 'km'] #=> false + # Measure[1, 'km'] == Measure[1, 'm'] #=> false + # + # Measurement = Data.define(:amount, :unit) + # Even though Measurement and Measure have the same "shape" + # their instances are never equal + # Measure[1, 'km'] == Measurement[1, 'km'] #=> false + #``` + sig { params(other: BasicObject).returns(T::Boolean) } + def ==(other); end + + # Returns the values in `self` as an array, to use in pattern matching: + # + # ```ruby + # Measure = Data.define(:amount, :unit) + # + # distance = Measure[10, 'km'] + # distance.deconstruct #=> [10, "km"] + # + # # usage + # case distance + # in n, 'km' # calls #deconstruct underneath + # puts "It is #{n} kilometers away" + # else + # puts "Don't know how to handle it" + # end + # # prints "It is 10 kilometers away" + # ``` + # + # Or, with checking the class, too: + # + # ``` + # case distance + # in Measure(n, 'km') + # puts "It is #{n} kilometers away" + # # ... + # end + # ``` + sig { returns(T::Array[T.untyped]) } + def deconstruct; end + + # Returns a hash of the name/value pairs, to use in pattern matching. + # + # ```ruby + # Measure = Data.define(:amount, :unit) + # + # distance = Measure[10, 'km'] + # distance.deconstruct_keys(nil) #=> {:amount=>10, :unit=>"km"} + # distance.deconstruct_keys([:amount]) #=> {:amount=>10} + # + # # usage + # case distance + # in amount:, unit: 'km' # calls #deconstruct_keys underneath + # puts "It is #{amount} kilometers away" + # else + # puts "Don't know how to handle it" + # end + # # prints "It is 10 kilometers away" + # ``` + # + # Or, with checking the class, too: + # + # ```ruby + # case distance + # in Measure(amount:, unit: 'km') + # puts "It is #{amount} kilometers away" + # # ... + # end + # ``` + sig do + params( + array_of_names_or_nil: T.nilable(T::Array[T.any(Symbol, String)]) + ) + .returns(T::Hash[Symbol, T.untyped]) + end + def deconstruct_keys(array_of_names_or_nil); end + + # Equality check that is used when two items of data are keys of a + # [`Hash`](https://docs.ruby-lang.org/en/3.2/Hash.html). + # + # The subtle difference with + # [`==`](https://docs.ruby-lang.org/en/3.2/Data.html#method-i-3D-3D) is that + # members are also compared with their + # [`eql?`](https://docs.ruby-lang.org/en/3.2/Data.html#method-i-eql-3F) + # method, which might be important in some cases: + # + # ```ruby + # Measure = Data.define(:amount, :unit) + # + # Measure[1, 'km'] == Measure[1.0, 'km'] #=> true, they are equal as values + # # ...but... + # Measure[1, 'km'].eql? Measure[1.0, 'km'] #=> false, they represent different hash keys + # ``` + # + # See also + # [`Object#eql?`](https://docs.ruby-lang.org/en/3.2/Object.html#method-i-eql-3F) + # for further explanations of the method usage. + sig { params(other: BasicObject).returns(T::Boolean) } + def eql?(other); end + + # Redefines + # [`Object#hash`](https://docs.ruby-lang.org/en/3.2/Object.html#method-i-hash) + # (used to distinguish objects as + # [`Hash`](https://docs.ruby-lang.org/en/3.2/Hash.html) keys) so that data + # objects of the same class with same content would have the same `hash`` + # value, and represented the same Hash key. + # + # ```ruby + # Measure = Data.define(:amount, :unit) + # + # Measure[1, 'km'].hash == Measure[1, 'km'].hash #=> true + # Measure[1, 'km'].hash == Measure[10, 'km'].hash #=> false + # Measure[1, 'km'].hash == Measure[1, 'm'].hash #=> false + # Measure[1, 'km'].hash == Measure[1.0, 'km'].hash #=> false + # + # # Structurally similar data class, but shouldn't be considered + # # the same hash key + # Measurement = Data.define(:amount, :unit) + # + # Measure[1, 'km'].hash == Measurement[1, 'km'].hash #=> false + # ``` + sig { returns(Integer) } + def hash; end + + # Returns a string representation of `self`: + # + # ```ruby + # Measure = Data.define(:amount, :unit) + # + # distance = Measure[10, 'km'] + # + # p distance # uses #inspect underneath + # # + # + # puts distance # uses #to_s underneath, same representation + # # + # ``` + # + # Also aliased as: + # [`to_s`](https://docs.ruby-lang.org/en/3.2/Data.html#method-i-to_s) + sig { returns(String) } + def inspect; end + + # Returns the member names from self as an array: + # + # ```ruby + # Measure = Data.define(:amount, :unit) + # distance = Measure[10, 'km'] + # + # distance.members #=> [:amount, :unit] + # ``` + sig { returns(T::Array[Symbol]) } + def members; end + + # Returns [`Hash`](https://docs.ruby-lang.org/en/3.2/Hash.html) representation + # of the data object. + # + # ```ruby + # Measure = Data.define(:amount, :unit) + # distance = Measure[10, 'km'] + + # distance.to_h + # #=> {:amount=>10, :unit=>"km"} + # ``` + # + # Like + # [`Enumerable#to_h`](https://docs.ruby-lang.org/en/3.2/Enumerable.html#method-i-to_h), + # if the block is provided, it is expected to produce key-value pairs to + # construct a hash: + # + # ```ruby + # distance.to_h { |name, val| [name.to_s, val.to_s] } + # #=> {"amount"=>"10", "unit"=>"km"} + # ``` + # + # Note that there is a useful symmetry between + # [`to_h`](https://docs.ruby-lang.org/en/3.2/Data.html#method-i-to_h) + # and initialize: + # + # ```ruby + # distance2 = Measure.new(**distance.to_h) + # #=> # + # distance2 == distance + # #=> true + # ``` + sig do + params( + blk: T.nilable( + T.proc.params(name: Symbol, val: T.untyped).returns(T.untyped) + ) + ) + .returns(T::Hash[T.untyped, T.untyped]) + end + def to_h(&blk); end + + # Returns a string representation of `self`: + # + # ```ruby + # Measure = Data.define(:amount, :unit) + # + # distance = Measure[10, 'km'] + # + # p distance # uses #inspect underneath + # # + # + # puts distance # uses #to_s underneath, same representation + # # + # ``` + # + # Alias for: + # [`inspect`](https://docs.ruby-lang.org/en/3.2/Data.html#method-i-inspect) + sig { returns(String) } + def to_s; end + + # Returns a shallow copy of `self` — the instance variables of `self` are + # copied, but not the objects they reference. + + # If the method is supplied any keyword arguments, the copy will be created + # with the respective field values updated to use the supplied keyword + # argument values. Note that it is an error to supply a keyword that the + # [`Data`](https://docs.ruby-lang.org/en/3.2/Data.html) class does not have + # as a member. + # + # ```ruby + # Point = Data.define(:x, :y) + # + # origin = Point.new(x: 0, y: 0) + # + # up = origin.with(x: 1) + # right = origin.with(y: 1) + # up_and_right = up.with(y: 1) + # + # p origin # # + # p up # # + # p right # # + # p up_and_right # # + # + # out = origin.with(z: 1) # ArgumentError: unknown keyword: :z + # some_point = origin.with(1, 2) # ArgumentError: expected keyword arguments, got positional arguments + # ``` + sig { params(kwargs: T.untyped).returns(T.self_type) } + def with(**kwargs); end end diff --git a/rbi/core/enumerable.rbi b/rbi/core/enumerable.rbi index bd97f94491..d916e5ec99 100644 --- a/rbi/core/enumerable.rbi +++ b/rbi/core/enumerable.rbi @@ -91,7 +91,9 @@ module Enumerable # ``` sig do type_parameters(:U).params(enums: T::Enumerable[T.type_parameter(:U)]) - .returns(T::Enumerator[T.any(Elem, T.type_parameter(:U))]) + .returns( + T::Enumerator::Chain[T.any(Elem, T.type_parameter(:U))] + ) end def chain(*enums); end @@ -1742,8 +1744,12 @@ module Enumerable # ```ruby # ["a", "b", "c", "b"].tally #=> {"a"=>1, "b"=>2, "c"=>1} # ``` - sig {returns(T::Hash[Elem, Integer])} - def tally(); end + # + # If a hash is given, the number of occurrences is added to each value + # in the hash, and the hash is returned. The value corresponding to + # each element must be an integer. + sig {params(hash: T::Hash[Elem, Integer]).returns(T::Hash[Elem, Integer])} + def tally(hash = {}); end ### Implemented in C++ @@ -1818,17 +1824,28 @@ module Enumerable # (1..100).find { |i| i % 5 == 0 && i % 7 == 0 } #=> 35 # ``` sig do - params( - ifnone: Proc, + type_parameters(:U) + .params( + ifnone: T.proc.returns(T.type_parameter(:U)), blk: T.proc.params(arg0: Elem).returns(BasicObject), - ) - .returns(T.nilable(Elem)) + ) + .returns(T.any(T.type_parameter(:U), Elem)) + end + sig do + type_parameters(:U) + .params( + ifnone: T.proc.returns(T.type_parameter(:U)), + ) + .returns(T::Enumerator[T.any(T.type_parameter(:U), Elem)]) end sig do params( - ifnone: Proc, + blk: T.proc.params(arg0: Elem).returns(BasicObject), ) - .returns(T::Enumerator[Elem]) + .returns(T.nilable(Elem)) + end + sig do + returns(T::Enumerator[Elem]) end def find(ifnone=T.unsafe(nil), &blk); end diff --git a/rbi/core/enumerator.rbi b/rbi/core/enumerator.rbi index e63d19e2da..6c29c4863c 100644 --- a/rbi/core/enumerator.rbi +++ b/rbi/core/enumerator.rbi @@ -1023,3 +1023,20 @@ class Enumerator::Yielder < Object end def yield(*arg0); end end + +# [`Chain`](https://docs.ruby-lang.org/en/2.7.0/Enumerator/Chain.html) +class Enumerator::Chain < Enumerator + include Enumerable + + extend T::Generic + Elem = type_member(:out) + + sig do + type_parameters(:U).params( + arg0: T::Enumerable[T.type_parameter(:U)], + ).returns( + T::Enumerator::Chain[T.type_parameter(:U)] + ) + end + def self.new(*arg0); end +end diff --git a/rbi/core/errors.rbi b/rbi/core/errors.rbi index 953777358f..b3c91689fd 100644 --- a/rbi/core/errors.rbi +++ b/rbi/core/errors.rbi @@ -335,6 +335,9 @@ end class NotImplementedError < ScriptError end +class NoMatchingPatternError < StandardError +end + # Raised when a given numerical value is out of range. # # ```ruby diff --git a/rbi/core/exception.rbi b/rbi/core/exception.rbi index 5b2817cc7f..c7126e1349 100644 --- a/rbi/core/exception.rbi +++ b/rbi/core/exception.rbi @@ -213,7 +213,7 @@ class Exception < Object sig do params( - arg0: T.any(String, Symbol, NilClass, Exception), + arg0: BasicObject, ) .void end diff --git a/rbi/core/gc.rbi b/rbi/core/gc.rbi index 572bc90bbd..f4623ca466 100644 --- a/rbi/core/gc.rbi +++ b/rbi/core/gc.rbi @@ -138,6 +138,40 @@ module GC sig {params(arg0: Symbol).returns(Integer)} def self.stat(arg0={}); end + # Returns information for memory pools in the \GC. + # + # If the first optional argument, +heap_name+, is passed in and not +nil+, it + # returns a +Hash+ containing information about the particular memory pool. + # Otherwise, it will return a +Hash+ with memory pool names as keys and + # a +Hash+ containing information about the memory pool as values. + # + # If the second optional argument, +hash_or_key+, is given as +Hash+, it will + # be overwritten and returned. This is intended to avoid the probe effect. + # + # If both optional arguments are passed in and the second optional argument is + # a symbol, it will return a +Numeric+ of the value for the particular memory + # pool. + # + # On CRuby, +heap_name+ is of the type +Integer+ but may be of type +String+ + # on other implementations. + # + # The contents of the hash are implementation specific and may change in + # the future without notice. + # + # If the optional argument, hash, is given, it is overwritten and returned. + # + # This method is only expected to work on CRuby. + sig { returns(T::Hash[Integer, T::Hash[Symbol, Integer]]) } + sig { params(heap_name: T.any(Integer, String), hash_or_key: Symbol).returns(Integer) } + sig do + params( + heap_name: T.any(Integer, String, NilClass), + hash_or_key: T::Hash[T.untyped, T.untyped], + ).returns(T::Hash[T.untyped, T.untyped]) + end + sig { params(heap_name: T.any(Integer, String)).returns(T::Hash[Symbol, Integer]) } + def self.stat_heap(heap_name = nil, hash_or_key = nil); end + # Returns current status of # [`GC`](https://docs.ruby-lang.org/en/2.7.0/GC.html) stress mode. sig {returns(T.any(Integer, TrueClass, FalseClass))} diff --git a/rbi/core/hash.rbi b/rbi/core/hash.rbi index 6d84b24e5c..deac3ec381 100644 --- a/rbi/core/hash.rbi +++ b/rbi/core/hash.rbi @@ -788,8 +788,6 @@ class Hash < Object # [`Object#hash`](https://docs.ruby-lang.org/en/2.7.0/Object.html#method-i-hash). def hash; end - def index(_); end - sig {void} sig {params(default: V).void} sig do @@ -1200,14 +1198,14 @@ class Hash < Object def filter!(&blk); end # Removes a key-value pair from *hsh* and returns it as the two-item array `[` - # *key, value* `]`, or the hash's default value if the hash is empty. + # *key, value* `]`, or nil if the hash is empty. # # ```ruby # h = { 1 => "a", 2 => "b", 3 => "c" } # h.shift #=> [1, "a"] # h #=> {2=>"b", 3=>"c"} # ``` - sig {returns(T::Array[T.any(K, V)])} + sig {returns(T.nilable(T::Array[T.any(K, V)]))} def shift(); end # Returns the number of key-value pairs in the hash. diff --git a/rbi/core/io.rbi b/rbi/core/io.rbi index 1c6a56487a..ca8de698c3 100644 --- a/rbi/core/io.rbi +++ b/rbi/core/io.rbi @@ -1565,7 +1565,7 @@ class IO < Object sig do params( fd: Integer, - mode: Integer, + mode: T.any(String, Integer), opt: T.untyped, ) .void @@ -3423,39 +3423,6 @@ class IO < Object end def self.for_fd(fd, mode=T.unsafe(nil), opt=T.unsafe(nil)); end - # This is a deprecated alias for - # [`each_byte`](https://docs.ruby-lang.org/en/2.7.0/IO.html#method-i-each_byte). - sig do - params( - blk: T.proc.params(arg0: Integer).returns(BasicObject), - ) - .returns(T.self_type) - end - sig {returns(T::Enumerator[Integer])} - def bytes(&blk); end - - # This is a deprecated alias for - # [`each_char`](https://docs.ruby-lang.org/en/2.7.0/IO.html#method-i-each_char). - sig do - params( - blk: T.proc.params(arg0: String).returns(BasicObject), - ) - .returns(T.self_type) - end - sig {returns(T::Enumerator[String])} - def chars(&blk); end - - # This is a deprecated alias for - # [`each_codepoint`](https://docs.ruby-lang.org/en/2.7.0/IO.html#method-i-each_codepoint). - sig do - params( - blk: T.proc.params(arg0: Integer).returns(BasicObject), - ) - .returns(T.self_type) - end - sig {returns(T::Enumerator[Integer])} - def codepoints(&blk); end - # Executes the block for every line in *ios*, where lines are separated by # *sep*. *ios* must be opened for reading or an # [`IOError`](https://docs.ruby-lang.org/en/2.7.0/IOError.html) will be @@ -3546,25 +3513,6 @@ class IO < Object sig {returns(T::Boolean)} def eof?(); end - # This is a deprecated alias for - # [`each_line`](https://docs.ruby-lang.org/en/2.7.0/IO.html#method-i-each_line). - sig do - params( - sep: String, - limit: Integer, - blk: T.proc.params(arg0: String).returns(BasicObject), - ) - .returns(T.self_type) - end - sig do - params( - sep: String, - limit: Integer, - ) - .returns(T::Enumerator[String]) - end - def lines(sep=T.unsafe(nil), limit=T.unsafe(nil), &blk); end - # Returns the integer file descriptor for the stream: # # ```ruby diff --git a/rbi/core/kernel.rbi b/rbi/core/kernel.rbi index 5b1bad23c3..61fb14737f 100644 --- a/rbi/core/kernel.rbi +++ b/rbi/core/kernel.rbi @@ -25,6 +25,26 @@ module Kernel RUBYGEMS_ACTIVATION_MONITOR = T.let(T.unsafe(nil), Monitor) + # Generates a [`Continuation`](https://ruby-doc.org/3.2.1/Continuation.html) + # object, which it passes to the associated block. You need to + # `require 'continuation'` before using this method. Performing a + # cont.call will cause the + # [`callcc`](https://ruby-doc.org/3.2.1/Kernel.html#method-i-callcc) to + # return (as will falling through the end + # of the block). The value returned by the + # [`callcc`](https://ruby-doc.org/3.2.1/Kernel.html#method-i-callcc) is the + # value of the block, or the value passed to cont.call. See class + # [`Continuation`](https://ruby-doc.org/3.2.1/Continuation.html) for more + # details. Also see + # [`Kernel#throw`](https://ruby-doc.org/3.2.1/Kernel.html#method-i-throw) + # for an alternative mechanism for unwinding a call stack. + sig do + type_parameters(:U).params( + block: T.proc.params(cont: Continuation).returns(T.type_parameter(:U)) + ).returns(T.type_parameter(:U)) + end + def callcc(&block); end + ### A note on global functions: ### ### Ruby tends to define global (e.g. "require", "puts") as @@ -473,7 +493,7 @@ module Kernel sig do params( - arg0: Class, + arg0: T::Class[T.anything], ) .returns(T::Boolean) end @@ -604,21 +624,21 @@ module Kernel sig do params( arg0: T.any(String, Symbol), - arg1: BasicObject, + arg1: T.anything, ) .returns(T.untyped) end sig do params( arg0: T.any(String, Symbol), - arg1: BasicObject, + arg1: T.anything, blk: T.untyped, ) .returns(T.untyped) end def send(arg0, *arg1, &blk); end - sig {returns(Class)} + sig {returns(T::Class[T.anything])} def singleton_class(); end sig do @@ -1252,7 +1272,7 @@ module Kernel end sig do params( - arg0: Class, + arg0: T::Class[T.anything], arg1: T.any(String, T::Array[String]), ) .returns(T.noreturn) @@ -2273,6 +2293,75 @@ module Kernel end def select(read_array, write_array=nil, error_array=nil, timeout=nil); end + # Establishes _proc_ as the handler for tracing, or disables + # tracing if the parameter is +nil+. + # + # *Note:* this method is obsolete, please use TracePoint instead. + # + # _proc_ takes up to six parameters: + # + # * an event name + # * a filename + # * a line number + # * an object id + # * a binding + # * the name of a class + # + # _proc_ is invoked whenever an event occurs. + # + # Events are: + # + # +c-call+:: call a C-language routine + # +c-return+:: return from a C-language routine + # +call+:: call a Ruby method + # +class+:: start a class or module definition + # +end+:: finish a class or module definition + # +line+:: execute code on a new line + # +raise+:: raise an exception + # +return+:: return from a Ruby method + # + # Tracing is disabled within the context of _proc_. + # + # class Test + # def test + # a = 1 + # b = 2 + # end + # end + # + # set_trace_func proc { |event, file, line, id, binding, classname| + # printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname + # } + # t = Test.new + # t.test + # + # line prog.rb:11 false + # c-call prog.rb:11 new Class + # c-call prog.rb:11 initialize Object + # c-return prog.rb:11 initialize Object + # c-return prog.rb:11 new Class + # line prog.rb:12 false + # call prog.rb:2 test Test + # line prog.rb:3 test Test + # line prog.rb:4 test Test + # return prog.rb:4 test Test + sig do + params( + arg0: T.nilable( + T.proc.params( + event: String, + file: String, + line: Integer, + id: T.nilable(Symbol), + binding: T.nilable(Binding), + classname: Object, + ).returns(T.untyped) + ) + ).void + end + sig { params(arg0: NilClass).returns(NilClass) } + def set_trace_func(arg0); end + # Suspends the current thread for *duration* seconds (which may be any number, # including a `Float` with fractional seconds). Returns the actual number of # seconds slept (rounded), which may be less than that asked for if another @@ -2821,21 +2910,13 @@ module Kernel sig {returns(T.noreturn)} sig do params( - arg0: String, + arg0: T.any(T::Class[T.anything], Exception, String), ) .returns(T.noreturn) end sig do params( - arg0: Class, - arg1: T.untyped, - arg2: T.nilable(T::Array[String]), - ) - .returns(T.noreturn) - end - sig do - params( - arg0: Exception, + arg0: T.any(T::Class[T.anything], Exception), arg1: T.untyped, arg2: T.nilable(T::Array[String]), ) diff --git a/rbi/core/match_data.rbi b/rbi/core/match_data.rbi index 99e7306af9..6eff765bc7 100644 --- a/rbi/core/match_data.rbi +++ b/rbi/core/match_data.rbi @@ -135,12 +135,32 @@ class MatchData < Object # ``` sig do params( - n: Integer, + n: T.any(Integer, Symbol, String), ) .returns(Integer) end def begin(n); end + # Returns a two-element array containing the beginning and ending byte-based + # offsets of the nth match. n can be a string or symbol to reference a named capture. + # + # ```ruby + # m = /(.)(.)(\d+)(\d)/.match("THX1138.") + # m.byteoffset(0) #=> [1, 7] + # m.byteoffset(4) #=> [6, 7] + # + # m = /(?.)(.)(?.)/.match("hoge") + # m.byteoffset(:foo) #=> [0, 1] + # m.byteoffset(:bar) #=> [2, 3] + # ``` + sig do + params( + n: T.any(Integer, Symbol, String), + ) + .returns(T::Array[Integer]) + end + def byteoffset(n); end + # Returns the array of captures; equivalent to `mtch.to_a[1..-1]`. # # ```ruby @@ -168,7 +188,7 @@ class MatchData < Object # ``` sig do params( - n: Integer, + n: T.any(Integer, Symbol, String), ) .returns(Integer) end diff --git a/rbi/core/method.rbi b/rbi/core/method.rbi index dc0ce4de4a..3a5eea4656 100644 --- a/rbi/core/method.rbi +++ b/rbi/core/method.rbi @@ -259,7 +259,7 @@ class Method < Object # ```ruby # (1..3).method(:map).owner #=> Enumerable # ``` - sig {returns(T.any(Class, Module))} + sig {returns(Module)} def owner; end # Returns the parameter information of this method. diff --git a/rbi/core/module.rbi b/rbi/core/module.rbi index 713e0da3e0..9b5f955202 100644 --- a/rbi/core/module.rbi +++ b/rbi/core/module.rbi @@ -516,6 +516,31 @@ class Module < Object end def class_variables(inherit=T.unsafe(nil)); end + # Invoked as a callback whenever a constant is assigned on the receiver + # + # ```ruby + # module Chatty + # def self.const_added(const_name) + # super + # puts "Added #{const_name.inspect}" + # end + # FOO = 1 + # end + # ``` + # + # *produces:* + # + # ``` + # Added :FOO + # ``` + sig do + params( + const_name: T.any(Symbol) + ) + .returns(T.untyped) + end + def const_added(const_name); end + # Says whether *mod* or its ancestors have a constant with the given name: # # ```ruby @@ -1609,13 +1634,34 @@ class Module < Object # Returns a module, where refined methods are defined. sig do params( - arg0: Class, + arg0: T::Class[T.anything], blk: T.proc.params(arg0: T.untyped).returns(BasicObject), ) .returns(T.self_type) end def refine(arg0, &blk); end + # Returns a list of refinements included in the receiver. + # + # ```ruby + # module A + # refine Integer do + # end + + # refine String do + # end + # end + + # p A.refinements + # ``` + # *produces:* + # + # ```ruby + # [#, #] + # ``` + sig {returns(T::Array[Module])} + def refinements; end + # Removes the named class variable from the receiver, returning that # variable's value. # @@ -1826,4 +1872,30 @@ class Module < Object # [B, A] # ``` def self.used_modules; end + + # Returns an array of all refinements used in the current scope. The ordering + # of refinements in the resulting array is not defined. + # ```ruby + # module A + # refine Object do + # end + # end + # + # module B + # refine Object do + # end + # end + # + # using A + # using B + # p Module.used_refinements + # ``` + # + # *produces:* + # + # ```ruby + # [#, #] + # ``` + sig {returns(T::Array[Module])} + def self.used_refinements; end end diff --git a/rbi/core/proc.rbi b/rbi/core/proc.rbi index c67b880e0d..497d69fa8f 100644 --- a/rbi/core/proc.rbi +++ b/rbi/core/proc.rbi @@ -695,14 +695,29 @@ class Proc < Object sig {returns(T::Boolean)} def lambda?(); end - # Returns the parameter information of this proc. + + + # Returns the parameter information of this proc. If the lambda keyword is + # provided and not nil, treats the proc as a lambda if true and as a + # non-lambda if false. # # ```ruby + # prc = proc{|x, y=42, *other|} + # prc.parameters #=> [[:opt, :x], [:opt, :y], [:rest, :other]] # prc = lambda{|x, y=42, *other|} # prc.parameters #=> [[:req, :x], [:opt, :y], [:rest, :other]] + # prc = proc{|x, y=42, *other|} + # prc.parameters(lambda: true) #=> [[:req, :x], [:opt, :y], [:rest, :other]] + # prc = lambda{|x, y=42, *other|} + # prc.parameters(lambda: false) #=> [[:opt, :x], [:opt, :y], [:rest, :other]] # ``` - sig {returns(T::Array[[Symbol, Symbol]])} - def parameters(); end + sig do + params( + lambda: T.nilable(T::Boolean), + ) + .returns(T::Array[T::Array[Symbol]]) + end + def parameters(lambda=nil); end # Marks the proc as passing keywords through a normal argument splat. This # should only be called on procs that accept an argument splat (`*args`) but diff --git a/rbi/core/refinement.rbi b/rbi/core/refinement.rbi new file mode 100644 index 0000000000..4ba955ac7c --- /dev/null +++ b/rbi/core/refinement.rbi @@ -0,0 +1,54 @@ +# typed: __STDLIB_INTERNAL + +# [Refinement](https://docs.ruby-lang.org/en/3.2/Refinement.html) is a class of +# the self (current context) inside refine statement. It allows to import +# methods from other modules, see +# [import_methods](https://docs.ruby-lang.org/en/3.2/Refinement.html#method-i-import_methods). +class Refinement < Object + # Imports methods from modules. Unlike + # [Module#include](https://docs.ruby-lang.org/en/3.2/Module.html#method-i-include), + # [Refinement#import_methods](https://docs.ruby-lang.org/en/3.2/Refinement.html#method-i-import_methods) + # copies methods and adds them into the refinement, so the refinement is + # activated in the imported methods. + # + # Note that due to method copying, only methods defined in Ruby code can be imported. + # + # ```ruby + # module StrUtils + # def indent(level) + # ' ' * level + self + # end + # end + # + # module M + # refine String do + # import_methods StrUtils + # end + # end + # + # using M + # "foo".indent(3) + # #=> " foo" + # + # module M + # refine String do + # import_methods Enumerable + # # Can't import method which is not defined with Ruby code: Enumerable#drop + # end + # end + # ``` + # + # Also aliased as: import_methods + sig do + params( + mod: Module, + rest: Module, + ) + .returns(Refinement) + end + def import_methods(mod, *rest); end + + # Return the class refined by the receiver. + sig {returns(T.class_of(Object))} + def refined_class; end +end diff --git a/rbi/core/regexp.rbi b/rbi/core/regexp.rbi index 495a199251..62c9a9a9d5 100644 --- a/rbi/core/regexp.rbi +++ b/rbi/core/regexp.rbi @@ -1308,6 +1308,20 @@ class Regexp < Object end def self.quote(arg0); end + # It returns the current default timeout interval for Regexp matching in + # second. nil means no default timeout configuration. + sig { returns(T.nilable(Float)) } + def self.timeout; end + + # It sets the default timeout interval for + # [`Regexp`](https://docs.ruby-lang.org/en/3.2/Regexp.html) matching in + # second. `nil` means no default timeout configuration. This configuration + # is process-global. If you want to set timeout for each + # [`Regexp`](https://docs.ruby-lang.org/en/3.2/Regexp.html), use `timeout` + # keyword for `Regexp.new`. + sig { params(value: T.nilable(Float)).returns(T.nilable(Float)) } + def self.timeout=(value); end + # Equality---Two regexps are equal if their patterns are identical, they have # the same character set code, and their `casefold?` values are the same. # diff --git a/rbi/core/ruby_vm.rbi b/rbi/core/ruby_vm.rbi index 6df33a16ad..9acb4856b9 100644 --- a/rbi/core/ruby_vm.rbi +++ b/rbi/core/ruby_vm.rbi @@ -76,21 +76,68 @@ module RubyVM::AbstractSyntaxTree # RubyVM::AbstractSyntaxTree.of(method(:hello)) # # => # # ``` - sig { params(arg: T.any(T::proc.void, Method)).returns(RubyVM::AbstractSyntaxTree::Node) } - def self.of(arg); end + # + # See [::parse](https://docs.ruby-lang.org/en/3.2/RubyVM/AbstractSyntaxTree.html#method-c-parse) + # for explanation of keyword argument meaning and usage. + sig do + params( + arg: T.any(T::proc.void, Method), + keep_script_lines: T::Boolean, + error_tolerant: T::Boolean, + keep_tokens: T::Boolean, + ) + .returns(RubyVM::AbstractSyntaxTree::Node) + end + def self.of(arg, keep_script_lines: false, error_tolerant: false, keep_tokens: false); end # Parses the given *string* into an abstract syntax tree, returning the root # node of that tree. # - # [`SyntaxError`](https://docs.ruby-lang.org/en/2.7.0/SyntaxError.html) is - # raised if the given *string* is invalid syntax. - # # ```ruby # RubyVM::AbstractSyntaxTree.parse("x = 1 + 2") # # => # # ``` - sig { params(string: String).returns(RubyVM::AbstractSyntaxTree::Node) } - def self.parse(string); end + # If `keep_script_lines: true` option is provided, the text of the parsed + # source is associated with nodes and is available via + # [Node#script_lines](https://docs.ruby-lang.org/en/3.2/RubyVM/AbstractSyntaxTree/Node.html#method-i-script_lines). + # + # If `keep_tokens: true` option is provided, + # [Node#tokens](https://docs.ruby-lang.org/en/3.2/RubyVM/AbstractSyntaxTree/Node.html#method-i-tokens) + # are populated. + # + # [`SyntaxError`](https://docs.ruby-lang.org/en/2.7.0/SyntaxError.html) is + # raised if the given *string* is invalid syntax. To overwrite this behavior, + # `error_tolerant: true`` can be provided. In this case, the parser will + # produce a tree where expressions with syntax errors would be represented by + # [Node](https://docs.ruby-lang.org/en/3.2/RubyVM/AbstractSyntaxTree/Node.html) + # with `type=:ERROR`. + # + # ```ruby + # root = RubyVM::AbstractSyntaxTree.parse("x = 1; p(x; y=2") + # # :33:in `parse': syntax error, unexpected ';', expecting ')' (SyntaxError) + # # x = 1; p(x; y=2 + # # ^ + # + # root = RubyVM::AbstractSyntaxTree.parse("x = 1; p(x; y=2", error_tolerant: true) + # # (SCOPE@1:0-1:15 + # # tbl: [:x, :y] + # # args: nil + # # body: (BLOCK@1:0-1:15 (LASGN@1:0-1:5 :x (LIT@1:4-1:5 1)) (ERROR@1:7-1:11) (LASGN@1:12-1:15 :y (LIT@1:14-1:15 2)))) + # root.children.last.children + # # [(LASGN@1:0-1:5 :x (LIT@1:4-1:5 1)), + # # (ERROR@1:7-1:11), + # # (LASGN@1:12-1:15 :y (LIT@1:14-1:15 2))] + # ``` + sig do + params( + string: String, + keep_script_lines: T::Boolean, + error_tolerant: T::Boolean, + keep_tokens: T::Boolean, + ) + .returns(RubyVM::AbstractSyntaxTree::Node) + end + def self.parse(string, keep_script_lines: false, error_tolerant: false, keep_tokens: false); end # Reads the file from *pathname*, then parses it like # [`::parse`](https://docs.ruby-lang.org/en/2.7.0/RubyVM/AbstractSyntaxTree.html#method-c-parse), @@ -103,8 +150,19 @@ module RubyVM::AbstractSyntaxTree # RubyVM::AbstractSyntaxTree.parse_file("my-app/app.rb") # # => # # ``` - sig { params(pathname: String).returns(RubyVM::AbstractSyntaxTree::Node) } - def self.parse_file(pathname); end + # + # See [::parse](https://docs.ruby-lang.org/en/3.2/RubyVM/AbstractSyntaxTree.html#method-c-parse) + # for explanation of keyword argument meaning and usage. + sig do + params( + pathname: String, + keep_script_lines: T::Boolean, + error_tolerant: T::Boolean, + keep_tokens: T::Boolean, + ) + .returns(RubyVM::AbstractSyntaxTree::Node) + end + def self.parse_file(pathname, keep_script_lines: false, error_tolerant: false, keep_tokens: false); end end # [`RubyVM::AbstractSyntaxTree::Node`](https://docs.ruby-lang.org/en/2.7.0/RubyVM/AbstractSyntaxTree/Node.html) diff --git a/rbi/core/string.rbi b/rbi/core/string.rbi index 92554d1afd..d5cf43070d 100644 --- a/rbi/core/string.rbi +++ b/rbi/core/string.rbi @@ -338,6 +338,140 @@ class String < Object sig {returns(String)} def b(); end + + # Returns the Integer byte-based index of the first occurrence of the given + # `substring``, or `nil`` if none found: + # + # ```ruby + # 'foo'.byteindex('f') # => 0 + # 'foo'.byteindex('o') # => 1 + # 'foo'.byteindex('oo') # => 1 + # 'foo'.byteindex('ooo') # => nil + # ``` + # + # Returns the Integer byte-based index of the first match for the given + # Regexp `regexp`, or `nil` if none found: + # + # ```ruby + # 'foo'.byteindex(/f/) # => 0 + # 'foo'.byteindex(/o/) # => 1 + # 'foo'.byteindex(/oo/) # => 1 + # 'foo'.byteindex(/ooo/) # => nil + # ``` + # + # Integer argument `offset`, if given, specifies the byte-based position + # in the string to begin the search: + # + # ```ruby + # 'foo'.byteindex('o', 1) # => 1 + # 'foo'.byteindex('o', 2) # => 2 + # 'foo'.byteindex('o', 3) # => nil + # ``` + # + # If `offset` is negative, counts backward from the end of `self`: + # + # ```ruby + # 'foo'.byteindex('o', -1) # => 2 + # 'foo'.byteindex('o', -2) # => 1 + # 'foo'.byteindex('o', -3) # => 1 + # 'foo'.byteindex('o', -4) # => nil + # ``` + # + # If `offset` does not land on character (codepoint) boundary, `IndexError` + # is raised. + # + # Related: + # [`String#index`](https://ruby-doc.org/3.2.0/String.html#method-i-index), + # [`String#byterindex`](https://ruby-doc.org/3.2.0/String.html#method-i-byterindex). + sig do + params( + arg0: T.any(Regexp, String), + arg1: Integer, + ) + .returns(T.nilable(Integer)) + end + def byteindex(arg0, arg1=T.unsafe(nil)); end + + # Returns the Integer byte-based index of the last occurrence of the given + # `substring`, or `nil` if none found: + # + # ```ruby + # 'foo'.byterindex('f') # => 0 + # 'foo'.byterindex('o') # => 2 + # 'foo'.byterindex('oo') # => 1 + # 'foo'.byterindex('ooo') # => nil + # ``` + # + # Returns the Integer byte-based index of the last match for the given Regexp + # `regexp`, or `nil` if none found: + # + # ```ruby + # 'foo'.byterindex(/f/) # => 0 + # 'foo'.byterindex(/o/) # => 2 + # 'foo'.byterindex(/oo/) # => 1 + # 'foo'.byterindex(/ooo/) # => nil + # ``` + # + # The _last_ match means starting at the possible last position, not the last + # of longest matches. + # + # ```ruby + # 'foo'.byterindex(/o+/) # => 2 + # $~ #=> # + # ``` + # + # To get the last longest match, needs to combine with negative lookbehind. + # + # ```ruby + # 'foo'.byterindex(/(? 1 + # $~ #=> # + # ``` + # + # Or + # [`String#byteindex`](https://ruby-doc.org/3.2.0/String.html#method-i-byteindex) + # with negative lookforward. + # + # ```ruby + # 'foo'.byteindex(/o+(?!.*o)/) # => 1 + # $~ #=> # + # ``` + # + # Integer argument `offset`, if given and non-negative, specifies the maximum + # starting byte-based position in the + # + # string to _end_ the search: + # + # ```ruby + # 'foo'.byterindex('o', 0) # => nil + # 'foo'.byterindex('o', 1) # => 1 + # 'foo'.byterindex('o', 2) # => 2 + # 'foo'.byterindex('o', 3) # => 2 + # ``` + # + # If `offset` is a negative Integer, the maximum starting position in the + # string to end the search is the sum of the string’s length and `offset`: + # + # ```ruby + # 'foo'.byterindex('o', -1) # => 2 + # 'foo'.byterindex('o', -2) # => 1 + # 'foo'.byterindex('o', -3) # => nil + # 'foo'.byterindex('o', -4) # => nil + # ``` + # + # If `offset` does not land on character (codepoint) boundary, `IndexError` is + # raised. + # + # Related: + # [`String#byteindex`](https://ruby-doc.org/3.2.0/String.html#method-i-byteindex). + sig do + params( + arg0: T.any(Regexp, String), + arg1: Integer, + ) + .returns(T.nilable(Integer)) + end + def byterindex(arg0, arg1=T.unsafe(nil)); end + # Returns an array of bytes in *str*. This is a shorthand for # `str.each_byte.to_a`. # @@ -390,6 +524,38 @@ class String < Object end def byteslice(arg0, arg1=T.unsafe(nil)); end + # Replaces some or all of the content of `self` with `str`, and returns + # `self`. The portion of the string affected is determined using the same + # criteria as + # [`String#byteslice`](https://docs.ruby-lang.org/en/3.2/String.html#method-i-byteslice), + # except that `length` cannot be omitted. If the replacement string is not + # the same length as the text it is replacing, the string will be adjusted + # accordingly. The form that take an + # [`Integer`](https://docs.ruby-lang.org/en/3.2/Integer.html) will raise an + # [`IndexError`](https://docs.ruby-lang.org/en/3.2/IndexError.html) if the + # value is out of range; the + # [`Range`](https://docs.ruby-lang.org/en/3.2/Range.html) form will raise a + # [`RangeError`](https://docs.ruby-lang.org/en/3.2/RangeError.html). If the + # beginning or ending offset does not land on character (codepoint) boundary, + # an [`IndexError`](https://docs.ruby-lang.org/en/3.2/IndexError.html) will + # be raised. + sig do + params( + arg0: Integer, + arg1: Integer, + arg2: String, + ) + .returns(String) + end + sig do + params( + arg0: T::Range[Integer], + arg1: String, + ) + .returns(String) + end + def bytesplice(arg0, arg1, arg2=T.unsafe(nil)); end + # Returns a copy of *str* with the first character converted to uppercase and # the remainder to lowercase. # @@ -1059,24 +1225,11 @@ class String < Object params( arg0: T.any(String, Encoding), arg1: T.any(String, Encoding), - arg2: T::Hash[Symbol, T.untyped] - ) - .returns(String) - end - sig do - params( - arg0: T.any(String, Encoding), - arg1: T::Hash[Symbol, T.untyped] - ) - .returns(String) - end - sig do - params( - arg0: T::Hash[Symbol, T.untyped] + arg2: T.untyped ) .returns(String) end - def encode(arg0=T.unsafe(nil), arg1=T.unsafe(nil), arg2=T.unsafe(nil)); end + def encode(arg0=T.unsafe(nil), arg1=T.unsafe(nil), **arg2); end # Returns the [`Encoding`](https://docs.ruby-lang.org/en/2.7.0/Encoding.html) # object that represents the encoding of obj. @@ -1090,7 +1243,15 @@ class String < Object # conversion. See # [`String#encode`](https://docs.ruby-lang.org/en/2.7.0/String.html#method-i-encode) # for details. Returns the string even if no changes were made. - def encode!(*_); end + sig do + params( + arg0: T.any(String, Encoding), + arg1: T.any(String, Encoding), + arg2: T.untyped + ) + .returns(String) + end + def encode!(arg0, arg1=T.unsafe(nil), **arg2); end # Returns true if `str` ends with one of the `suffixes` given. # @@ -1316,10 +1477,11 @@ class String < Object params( str: String, encoding: T.nilable(Encoding), + capacity: T.nilable(Integer), ) .void end - def initialize(str=T.unsafe(nil), encoding: nil); end + def initialize(str=T.unsafe(nil), encoding: nil, capacity: nil); end # Inserts *other\_str* before the character at the given *index*, modifying # *str*. Negative indices count from the end of the string, and insert *after* @@ -1980,7 +2142,7 @@ class String < Object params( arg0: String, ) - .returns(String) + .returns(T.nilable(String)) end def squeeze!(arg0=T.unsafe(nil)); end diff --git a/rbi/core/symbol.rbi b/rbi/core/symbol.rbi index e0d590579b..49e915133e 100644 --- a/rbi/core/symbol.rbi +++ b/rbi/core/symbol.rbi @@ -70,7 +70,8 @@ class Symbol < Object def ==(obj); end # Equality---If *sym* and *obj* are exactly the same symbol, returns `true`. - def ===(_); end + sig {params(obj: T.anything).returns(T::Boolean)} + def ===(obj); end # Returns `sym.to_s =~ obj`. sig do @@ -233,6 +234,10 @@ class Symbol < Object end def match?(*args); end + # Returns the name or string corresponding to *sym*. Unlike `to_s`, the returned string is frozen. + sig {returns(String)} + def name(); end + # Same as `sym.to_s.succ.intern`. sig {returns(Symbol)} def next(); end diff --git a/rbi/core/thread.rbi b/rbi/core/thread.rbi index eadb12e8a8..b2aef02cff 100644 --- a/rbi/core/thread.rbi +++ b/rbi/core/thread.rbi @@ -845,15 +845,6 @@ class Thread < Object sig {params(abort_on_exception: T.untyped).returns(T.untyped)} def self.abort_on_exception=(abort_on_exception); end - # Wraps the block in a single, VM-global - # [`Mutex.synchronize`](https://docs.ruby-lang.org/en/2.7.0/Mutex.html#method-i-synchronize), - # returning the value of the block. A thread executing inside the exclusive - # section will only block other threads which also use the - # [`Thread.exclusive`](https://docs.ruby-lang.org/en/2.7.0/Thread.html#method-c-exclusive) - # mechanism. - sig {params(block: T.untyped).returns(T.untyped)} - def self.exclusive(&block); end - # Terminates the currently running thread and schedules another thread to be # run. # @@ -1479,6 +1470,11 @@ end # consumer.join # ``` class Thread::Queue < Object + # Creates a new queue instance, optionally using the contents of an enumerable for its initial state. + # https://ruby-doc.org/core-3.1.0/Thread/Queue.html#method-c-new + sig {params(enumerable: T::Enumerable[T.untyped]).void} + def initialize(enumerable=T.unsafe(nil)); end + # Alias for: # [`push`](https://docs.ruby-lang.org/en/2.7.0/Queue.html#method-i-push) sig {params(obj: T.untyped).returns(T.untyped)} diff --git a/rbi/core/time.rbi b/rbi/core/time.rbi index 452981d38e..932f0d4fbe 100644 --- a/rbi/core/time.rbi +++ b/rbi/core/time.rbi @@ -689,7 +689,7 @@ class Time < Object sig do params( - year: T.any(Integer, String), + year: Integer, month: T.any(Integer, String), day: T.any(Integer, String), hour: T.any(Integer, String), @@ -1093,24 +1093,6 @@ class Time < Object sig {returns(Numeric)} def subsec(); end - # Returns a new [`Time`](https://docs.ruby-lang.org/en/2.7.0/Time.html) - # object, one second later than *time*. - # [`Time#succ`](https://docs.ruby-lang.org/en/2.7.0/Time.html#method-i-succ) - # is obsolete since 1.9.2 for time is not a discrete value. - # - # ```ruby - # t = Time.now #=> 2007-11-19 08:23:57 -0600 - # t.succ #=> 2007-11-19 08:23:58 -0600 - # ``` - # - # Use instead `time + 1` - # - # ```ruby - # t + 1 #=> 2007-11-19 08:23:58 -0600 - # ``` - sig {returns(Time)} - def succ(); end - # Returns `true` if *time* represents Sunday. # # ```ruby diff --git a/rbi/sorbet/builder.rbi b/rbi/sorbet/builder.rbi index 548eebc70a..75e21aef39 100644 --- a/rbi/sorbet/builder.rbi +++ b/rbi/sorbet/builder.rbi @@ -21,8 +21,8 @@ class T::Private::Methods::DeclBuilder sig {params(claz: T.untyped).returns(T::Private::Methods::DeclBuilder)} def bind(claz); end - sig {params(params: T.untyped).returns(T::Private::Methods::DeclBuilder)} - def params(**params); end + sig {params(unused_positional_params: T.untyped, params: T.untyped).returns(T::Private::Methods::DeclBuilder)} + def params(*unused_positional_params, **params); end sig {params(type: T.untyped).returns(T::Private::Methods::DeclBuilder)} def returns(type); end diff --git a/rbi/sorbet/sorbet.rbi b/rbi/sorbet/sorbet.rbi index aaf642e534..8896d9ac6f 100644 --- a/rbi/sorbet/sorbet.rbi +++ b/rbi/sorbet/sorbet.rbi @@ -338,6 +338,18 @@ class Sorbet::Private::Static::ENVClass sig {returns(T::Enumerator[Elem])} def select!(&blk); end + # Returns a Hash of the given ENV names and their corresponding values + # + # ```ruby + # ENV.slice('foo', 'baz') # => {"foo"=>"0", "baz"=>"2"} + # ENV.slice('baz', 'foo') # => {"baz"=>"2", "foo"=>"0"} + # ``` + sig do + params(names: String) + .returns(T::Hash[String, String]) + end + def slice(*names); end + sig do returns(T::Hash[String, T.nilable(String)]) end diff --git a/rbi/sorbet/t.rbi b/rbi/sorbet/t.rbi index 554ebea137..c4904be054 100644 --- a/rbi/sorbet/t.rbi +++ b/rbi/sorbet/t.rbi @@ -120,6 +120,10 @@ module T # For more information, see https://sorbet.org/docs/noreturn def self.noreturn; end + # Type syntax to declare the "top" type in Sorbet. Every type is a subtype of + # this type, but absolutely nothing is known about values of this type. + def self.anything; end + # Deprecated. Use `T::Enum` instead. # # For more information, see https://sorbet.org/docs/tenum @@ -244,6 +248,14 @@ module T::Generic # # For more information, see https://sorbet.org/docs/generics#generics-and-runtime-checks def [](*types); end + + # Allows using `T.attached_class` in this module, at the expense of only + # being allowed to `extend` this module, never `include` it (unless the + # module it's included into is also marked `has_attached_class!`). + # + # For more information, see https://sorbet.org/docs/attached-class + sig {params(variance: Symbol, blk: T.untyped).void} + def has_attached_class!(variance=:invariant, &blk); end end module T::Helpers @@ -323,6 +335,10 @@ module T::Range # Type syntax to specify the element type of a standard library Range def self.[](type); end end +module T::Class + # Type syntax to specify the element type of a standard library Class + def self.[](type); end +end module T::Enumerable # Type syntax to specify the element type of a standard library Enumerable def self.[](type); end @@ -335,6 +351,10 @@ module T::Enumerator::Lazy # Type syntax to specify the element type of a standard library Enumerator::Lazy def self.[](type); end end +module T::Enumerator::Chain + # Type syntax to specify the element type of a standard library Enumerator::Chain + def self.[](type); end +end # Type syntax for either a `true` or `false` value. # diff --git a/rbi/sorbet/ttypes.rbi b/rbi/sorbet/ttypes.rbi index da177e1843..4335199bd5 100644 --- a/rbi/sorbet/ttypes.rbi +++ b/rbi/sorbet/ttypes.rbi @@ -73,6 +73,17 @@ class T::Types::Untyped < T::Types::Base def valid?(obj); end end +class T::Types::Anything < T::Types::Base + sig {void} + def initialize; end + + sig {returns(String)} + def name; end + + sig {params(obj: T.anything).returns(T::Boolean)} + def valid?(obj); end +end + class T::Types::Proc < T::Types::Base def initialize(arg_types, returns); end def name; end @@ -95,6 +106,14 @@ class T::Types::Enum < T::Types::Base def values; end end +class T::Types::TEnum < T::Types::Base + def initialize(val); end + def valid?(obj); end + def name; end + def describe_obj(obj); end + def val; end +end + class T::Types::SelfType < T::Types::Base def initialize(); end def name; end @@ -122,6 +141,7 @@ class T::Types::TypeParameter < T::Types::Base def valid?(obj); end def subtype_of_single?(type); end def name; end + def self.make(name); end end # --- stdlib generics --- @@ -176,3 +196,26 @@ class T::Types::TypedEnumeratorLazy < T::Types::TypedEnumerable def type; end end +class T::Types::TypedEnumeratorChain < T::Types::TypedEnumerable + def name; end + def valid?(obj); end + def new(*args); end + def type; end +end + +class T::Types::TypedClass < T::Types::Base + sig {params(type: T.untyped).void} + def initialize(type); end + + sig {returns(String)} + def name; end + + sig {params(obj: T.anything).returns(T::Boolean)} + def valid?(obj); end + + sig {returns(T::Types::Base)} + def type; end + + sig {returns(T.class_of(Class))} + def underlying_class; end +end diff --git a/rbi/stdlib/csv.rbi b/rbi/stdlib/csv.rbi index 298684ac58..304aaffe63 100644 --- a/rbi/stdlib/csv.rbi +++ b/rbi/stdlib/csv.rbi @@ -459,11 +459,11 @@ class CSV < Object sig do params( io: T.any(::Sorbet::Private::Static::IOLike, String), - options: T::Hash[Symbol, T.untyped], + options: T.untyped, ) .void end - def initialize(io=T.unsafe(nil), options=T.unsafe(nil)); end + def initialize(io=T.unsafe(nil), **options); end # This method can be used to easily parse # [`CSV`](https://docs.ruby-lang.org/en/2.7.0/CSV.html) out of a @@ -479,8 +479,8 @@ class CSV < Object # understands. sig do params( - str: String, - options: T::Hash[Symbol, T.untyped], + str: T.any(String, ::Sorbet::Private::Static::IOLike), + options: T.untyped, ) .returns( T.any( @@ -491,12 +491,12 @@ class CSV < Object end sig do params( - str: String, - options: T::Hash[Symbol, T.untyped], + str: T.any(String, ::Sorbet::Private::Static::IOLike), + options: T.untyped, blk: T.proc.params(arg0: T.any(CSV::Row, T::Array[T.untyped])).void ).void end - def self.parse(str, options=T.unsafe(nil), &blk); end + def self.parse(str, **options, &blk); end # This method is a shortcut for converting a single line of a # [`CSV`](https://docs.ruby-lang.org/en/2.7.0/CSV.html) diff --git a/rbi/stdlib/date.rbi b/rbi/stdlib/date.rbi index 6b768898a2..e72c1a9dfd 100644 --- a/rbi/stdlib/date.rbi +++ b/rbi/stdlib/date.rbi @@ -933,8 +933,8 @@ class Date # ```ruby # Date.today #=> # # ``` - sig {params(arg0: T.untyped).returns(Date)} - def self.today(*arg0); end + sig {params(start: T.any(Integer, Float)).returns(Date)} + def self.today(start=Date::ITALY); end # Parses the given representation of date and time, and creates a date object. # This method does not function as a validator. @@ -947,8 +947,8 @@ class Date # Date.parse('20010203') #=> # # Date.parse('3rd Feb 2001') #=> # # ``` - sig {params(arg0: T.untyped).returns(T.attached_class)} - def self.parse(*arg0); end + sig {params(string: String, comp: T::Boolean, state: T.any(Integer, Float)).returns(T.attached_class)} + def self.parse(string='-4712-01-01', comp=true, state=Date::ITALY); end # Creates a date object denoting the given chronological Julian day number. # @@ -960,8 +960,8 @@ class Date # # See also # [`::new`](https://docs.ruby-lang.org/en/2.7.0/Date.html#method-c-new). - sig {params(arg0: T.untyped).returns(T.attached_class)} - def self.jd(*arg0); end + sig {params(jd: T.any(Integer, Float), start: T.any(Integer, Float)).returns(T.attached_class)} + def self.jd(jd=0, start=Date::ITALY); end # Just returns true. It's nonsense, but is for symmetry. # @@ -971,8 +971,8 @@ class Date # # See also # [`::jd`](https://docs.ruby-lang.org/en/2.7.0/Date.html#method-c-jd). - sig {params(arg0: T.untyped).returns(T::Boolean)} - def self.valid_jd?(*arg0); end + sig {params(jd: T.any(Integer, Float), start: T.any(Integer, Float)).returns(T::Boolean)} + def self.valid_jd?(jd, start=Date::ITALY); end # Returns true if the given ordinal date is valid, and false if not. # @@ -984,8 +984,8 @@ class Date # See also [`::jd`](https://docs.ruby-lang.org/en/2.7.0/Date.html#method-c-jd) # and # [`::ordinal`](https://docs.ruby-lang.org/en/2.7.0/Date.html#method-c-ordinal). - sig {params(arg0: T.untyped).returns(T::Boolean)} - def self.valid_ordinal?(*arg0); end + sig {params(year: T.any(Integer, Float), yday: T.any(Integer, Float), start: T.any(Integer, Float)).returns(T::Boolean)} + def self.valid_ordinal?(year, yday, start=Date::ITALY); end # Returns true if the given calendar date is valid, and false if not. Valid in # this context is whether the arguments passed to this method would be @@ -1001,8 +1001,8 @@ class Date # See also [`::jd`](https://docs.ruby-lang.org/en/2.7.0/Date.html#method-c-jd) # and # [`::civil`](https://docs.ruby-lang.org/en/2.7.0/Date.html#method-c-civil). - sig {params(arg0: T.untyped).returns(T::Boolean)} - def self.valid_civil?(*arg0); end + sig {params(year: T.any(Integer, Float), month: T.any(Integer, Float), mday: T.any(Integer, Float), start: T.any(Integer, Float)).returns(T::Boolean)} + def self.valid_civil?(year, month, mday, start=Date::ITALY); end # Returns true if the given calendar date is valid, and false if not. Valid in # this context is whether the arguments passed to this method would be diff --git a/rbi/stdlib/e2mmap.rbi b/rbi/stdlib/e2mmap.rbi index 4c2f757a18..ffb9a48426 100644 --- a/rbi/stdlib/e2mmap.rbi +++ b/rbi/stdlib/e2mmap.rbi @@ -84,7 +84,7 @@ module Exception2MessageMapper # m: message_form # define exception c with message m. # ``` - sig {params(c: Class, m: String).void} + sig {params(c: T::Class[T.anything], m: String).void} def def_e2message(c, m); end # [`def_exception`](https://docs.ruby-lang.org/en/2.6.0/Exception2MessageMapper.html#method-i-def_exception)(n, @@ -96,6 +96,6 @@ module Exception2MessageMapper # s: superclass(default: StandardError) # define exception named ``c'' with message m. # ``` - sig {params(n: Symbol, m: String, s: Class).void} + sig {params(n: Symbol, m: String, s: T::Class[T.anything]).void} def def_exception(n, m, s = StandardError); end end diff --git a/rbi/stdlib/fileutils.rbi b/rbi/stdlib/fileutils.rbi index 5f86fac3e4..fdf11fefa7 100644 --- a/rbi/stdlib/fileutils.rbi +++ b/rbi/stdlib/fileutils.rbi @@ -123,7 +123,7 @@ module FileUtils params( src: T.any(String, Pathname), dest: T.any(String, Pathname), - preserve: T.nilable(T::Hash[Symbol, T::Boolean]), + preserve: T.nilable(T::Boolean), noop: T.nilable(T::Boolean), verbose: T.nilable(T::Boolean), dereference_root: T::Boolean, diff --git a/rbi/stdlib/json.rbi b/rbi/stdlib/json.rbi index 7cb51d06af..2abc300bd2 100644 --- a/rbi/stdlib/json.rbi +++ b/rbi/stdlib/json.rbi @@ -235,6 +235,26 @@ module JSON end def self.load(source, proc=T.unsafe(nil), options=T.unsafe(nil)); end + # https://docs.ruby-lang.org/en/master/JSON.html#method-i-load_file + sig do + params( + path: ::T.untyped, + opts: ::T.untyped, + ) + .returns(::T.untyped) + end + def self.load_file(path, opts=T.unsafe({})); end + + # https://docs.ruby-lang.org/en/master/JSON.html#method-i-load_file-21 + sig do + params( + path: ::T.untyped, + opts: ::T.untyped, + ) + .returns(::T.untyped) + end + def self.load_file!(path, opts=T.unsafe({})); end + # The global default options for the # [`JSON.load`](https://docs.ruby-lang.org/en/2.7.0/JSON.html#method-i-load) # method: diff --git a/rbi/stdlib/matrix.rbi b/rbi/stdlib/matrix.rbi index af9a2c0628..18b17fc88d 100644 --- a/rbi/stdlib/matrix.rbi +++ b/rbi/stdlib/matrix.rbi @@ -76,7 +76,7 @@ class Vector include ::Enumerable - Elem = type_member(:out) + Elem = type_member {{ fixed: T.untyped }} # [`Vector.new`](https://docs.ruby-lang.org/en/2.7.0/Vector.html#method-c-new) # is private; use Vector[] or @@ -321,7 +321,7 @@ end class Matrix include ::Enumerable - Elem = type_member(:out) + Elem = type_member {{ fixed: T.untyped }} # Yields all elements of the matrix, starting with those of the first row, or # returns an diff --git a/rbi/stdlib/objspace.rbi b/rbi/stdlib/objspace.rbi index 5f62bdd76e..5ad8e42cb2 100644 --- a/rbi/stdlib/objspace.rbi +++ b/rbi/stdlib/objspace.rbi @@ -168,7 +168,7 @@ class ObjectSpace::WeakMap < Object include Enumerable extend T::Generic - Elem = type_member(:out) + Elem = type_member {{ fixed: T.untyped }} # Retrieves a weakly referenced object with the given key def [](_); end diff --git a/rbi/stdlib/pathname.rbi b/rbi/stdlib/pathname.rbi index 0e0fc68029..ed26c6c8cc 100644 --- a/rbi/stdlib/pathname.rbi +++ b/rbi/stdlib/pathname.rbi @@ -244,14 +244,14 @@ class Pathname < Object # [`Dir.glob`](https://docs.ruby-lang.org/en/2.7.0/Dir.html#method-c-glob). sig do params( - p1: T.any(String, Pathname), + p1: T.any(String, Pathname, T::Array[T.any(String, Pathname)]), p2: Integer, ) .returns(T::Array[Pathname]) end sig do params( - p1: T.any(String, Pathname), + p1: T.any(String, Pathname, T::Array[T.any(String, Pathname)]), p2: Integer, blk: T.proc.params(arg0: Pathname).void, ) @@ -414,7 +414,7 @@ class Pathname < Object length: Integer, offset: Integer, ) - .returns(String) + .returns(T.nilable(String)) end def binread(length=T.unsafe(nil), offset=T.unsafe(nil)); end @@ -864,14 +864,14 @@ class Pathname < Object sig do params( - p1: T.any(String, Pathname), + p1: T.any(String, Pathname, T::Array[T.any(String, Pathname)]), p2: Integer, ) .returns(T::Array[Pathname]) end sig do params( - p1: T.any(String, Pathname), + p1: T.any(String, Pathname, T::Array[T.any(String, Pathname)]), p2: Integer, blk: T.proc.params(arg0: Pathname).void ) @@ -1450,5 +1450,6 @@ module Kernel # See also # [`Pathname::new`](https://docs.ruby-lang.org/en/2.7.0/Pathname.html#method-c-new) # for more information. - def Pathname(_); end + sig { params(path: T.any(String, Pathname)).returns(Pathname) } + def Pathname(path); end end diff --git a/rbi/stdlib/psych.rbi b/rbi/stdlib/psych.rbi index b0fd2f93db..d332fb7501 100644 --- a/rbi/stdlib/psych.rbi +++ b/rbi/stdlib/psych.rbi @@ -319,7 +319,7 @@ module Psych params( yaml: T.any(String, StringIO, IO), legacy_filename: Object, - permitted_classes: T::Array[Class], + permitted_classes: T::Array[T::Class[T.anything]], permitted_symbols: T::Array[Symbol], aliases: T::Boolean, filename: T.nilable(String), @@ -396,7 +396,7 @@ module Psych legacy_permitted_symbols: Object, legacy_aliases: Object, legacy_filename: Object, - permitted_classes: T::Array[Class], + permitted_classes: T::Array[T::Class[T.anything]], permitted_symbols: T::Array[Symbol], aliases: T::Boolean, filename: T.nilable(String), diff --git a/rbi/stdlib/rdoc.rbi b/rbi/stdlib/rdoc.rbi index 27b534a6e2..bd06d04329 100644 --- a/rbi/stdlib/rdoc.rbi +++ b/rbi/stdlib/rdoc.rbi @@ -1508,10 +1508,6 @@ class RDoc::Context::Section # Removes a comment from this section if it is from the same file as `comment` def remove_comment(comment); end - # [`Section`](https://docs.ruby-lang.org/en/2.6.0/RDoc/Context/Section.html) - # sequence number (deprecated) - def sequence; end - # [`Section`](https://docs.ruby-lang.org/en/2.6.0/RDoc/Context/Section.html) # title def title; end diff --git a/rbi/stdlib/ripper.rbi b/rbi/stdlib/ripper.rbi index d7ff8a7327..569260b8d3 100644 --- a/rbi/stdlib/ripper.rbi +++ b/rbi/stdlib/ripper.rbi @@ -76,6 +76,12 @@ # * aamine@loveruby.net # * http://i.loveruby.net class Ripper + # This hash contains parser event names and arity to handle them. + PARSER_EVENT_TABLE = T.let(T.unsafe(nil), T::Hash[Symbol, Integer]) + + # This hash contains scanner event names and arity to handle them. + SCANNER_EVENT_TABLE = T.let(T.unsafe(nil), T::Hash[Symbol, Integer]) + # This array contains name of all ripper events. EVENTS = T.let(T.unsafe(nil), T::Array[Symbol]) diff --git a/rbi/stdlib/singleton.rbi b/rbi/stdlib/singleton.rbi index f0376ba9c6..2951b0cb35 100644 --- a/rbi/stdlib/singleton.rbi +++ b/rbi/stdlib/singleton.rbi @@ -106,9 +106,8 @@ # ``` module Singleton module SingletonClassMethods - # Correctly modeling this return value is blocked by this issue: - # https://github.com/sorbet/sorbet/issues/62 - sig {returns(T.untyped)} + has_attached_class! + sig {returns(T.attached_class)} def instance; end private diff --git a/rbi/stdlib/stringio.rbi b/rbi/stdlib/stringio.rbi index 69e76d2c33..7e5d3a144b 100644 --- a/rbi/stdlib/stringio.rbi +++ b/rbi/stdlib/stringio.rbi @@ -493,39 +493,6 @@ class StringIO end def write(arg0); end - # This is a deprecated alias for - # [`each_byte`](https://docs.ruby-lang.org/en/2.7.0/StringIO.html#method-i-each_byte). - sig do - params( - blk: T.proc.params(arg0: Integer).returns(BasicObject), - ) - .returns(T.self_type) - end - sig {returns(T::Enumerator[Integer])} - def bytes(&blk); end - - # This is a deprecated alias for - # [`each_char`](https://docs.ruby-lang.org/en/2.7.0/StringIO.html#method-i-each_char). - sig do - params( - blk: T.proc.params(arg0: String).returns(BasicObject), - ) - .returns(T.self_type) - end - sig {returns(T::Enumerator[String])} - def chars(&blk); end - - # This is a deprecated alias for - # [`each_codepoint`](https://docs.ruby-lang.org/en/2.7.0/StringIO.html#method-i-each_codepoint). - sig do - params( - blk: T.proc.params(arg0: Integer).returns(BasicObject), - ) - .returns(T.self_type) - end - sig {returns(T::Enumerator[Integer])} - def codepoints(&blk); end - # See [`IO#each`](https://docs.ruby-lang.org/en/2.7.0/IO.html#method-i-each). sig do params( @@ -548,25 +515,6 @@ class StringIO # The stream must be opened for reading or an `IOError` will be raised. sig {returns(T::Boolean)} def eof?(); end - - # This is a deprecated alias for - # [`each_line`](https://docs.ruby-lang.org/en/2.7.0/StringIO.html#method-i-each_line). - sig do - params( - sep: String, - limit: Integer, - blk: T.proc.params(arg0: String).returns(BasicObject), - ) - .returns(T.self_type) - end - sig do - params( - sep: String, - limit: Integer, - ) - .returns(T::Enumerator[String]) - end - def lines(sep=T.unsafe(nil), limit=T.unsafe(nil), &blk); end end # Pseudo I/O on [`String`](https://docs.ruby-lang.org/en/2.7.0/String.html) diff --git a/rbi/stdlib/strscan.rbi b/rbi/stdlib/strscan.rbi index 5c6d10d503..ba4e52149d 100644 --- a/rbi/stdlib/strscan.rbi +++ b/rbi/stdlib/strscan.rbi @@ -339,7 +339,7 @@ class StringScanner < Object # s.getch # => "\244\242" # Japanese hira-kana "A" in EUC-JP # s.getch # => nil # ``` - sig {returns(String)} + sig {returns(T.nilable(String))} def getch(); end # Returns a string that represents the diff --git a/rbi/stdlib/uri.rbi b/rbi/stdlib/uri.rbi index baef1f2aff..4a74c7fbaf 100644 --- a/rbi/stdlib/uri.rbi +++ b/rbi/stdlib/uri.rbi @@ -101,8 +101,6 @@ end # * URI::REGEXP::PATTERN - (in uri/common.rb) # # * URI::Util - (in uri/common.rb) -# * [`URI::Escape`](https://docs.ruby-lang.org/en/2.7.0/URI/Escape.html) - (in -# uri/common.rb) # * [`URI::Error`](https://docs.ruby-lang.org/en/2.7.0/URI/Error.html) - (in # uri/common.rb) # * [`URI::InvalidURIError`](https://docs.ruby-lang.org/en/2.7.0/URI/InvalidURIError.html) @@ -265,21 +263,15 @@ module URI end def self.encode_www_form_component(str, enc=nil); end + # Like URI.encode_www_form_component, except that ' ' (space) + # is encoded as '%20' (instead of '+'). sig do params( - arg: String, - arg0: Regexp, - ) - .returns(String) - end - sig do - params( - arg: String, - arg0: String, - ) - .returns(String) + str: Object, + enc: T.nilable(Encoding) + ).returns(String) end - def self.escape(arg, *arg0); end + def self.encode_uri_component(str, enc=nil);end # ## Synopsis # @@ -460,7 +452,7 @@ module URI # Returns a [`Hash`](https://docs.ruby-lang.org/en/2.7.0/Hash.html) of the # defined schemes. - sig {returns(T::Hash[String, Class])} + sig {returns(T::Hash[String, T::Class[T.anything]])} def self.scheme_list(); end # ## Synopsis @@ -507,14 +499,6 @@ module URI end def self.split(uri); end - sig do - params( - arg: String, - ) - .returns(String) - end - def self.unescape(*arg); end - sig do params( arg: String, @@ -550,105 +534,9 @@ end class URI::Error < StandardError end -# [`Module`](https://docs.ruby-lang.org/en/2.7.0/Module.html) for escaping -# unsafe characters with codes. -module URI::Escape - # Alias for: - # [`unescape`](https://docs.ruby-lang.org/en/2.7.0/URI/Escape.html#method-i-unescape) - def decode(*arg); end - - # Alias for: - # [`escape`](https://docs.ruby-lang.org/en/2.7.0/URI/Escape.html#method-i-escape) - def encode(*arg); end - - # ## Synopsis - # - # ``` - # URI.escape(str [, unsafe]) - # ``` - # - # ## Args - # - # `str` - # : [`String`](https://docs.ruby-lang.org/en/2.7.0/String.html) to replaces - # in. - # `unsafe` - # : [`Regexp`](https://docs.ruby-lang.org/en/2.7.0/Regexp.html) that matches - # all symbols that must be replaced with codes. By default uses `UNSAFE`. - # When this argument is a - # [`String`](https://docs.ruby-lang.org/en/2.7.0/String.html), it - # represents a character set. - # - # - # ## Description - # - # Escapes the string, replacing all unsafe characters with codes. - # - # This method is obsolete and should not be used. Instead, use - # [`CGI.escape`](https://docs.ruby-lang.org/en/2.7.0/CGI/Util.html#method-i-escape), - # [`URI.encode_www_form`](https://docs.ruby-lang.org/en/2.7.0/URI.html#method-c-encode_www_form) - # or - # [`URI.encode_www_form_component`](https://docs.ruby-lang.org/en/2.7.0/URI.html#method-c-encode_www_form_component) - # depending on your specific use case. - # - # ## Usage - # - # ```ruby - # require 'uri' - # - # enc_uri = URI.escape("http://example.com/?a=\11\15") - # # => "http://example.com/?a=%09%0D" - # - # URI.unescape(enc_uri) - # # => "http://example.com/?a=\t\r" - # - # URI.escape("@?@!", "!?") - # # => "@%3F@%21" - # ``` - # - # - # Also aliased as: - # [`encode`](https://docs.ruby-lang.org/en/2.7.0/URI/Escape.html#method-i-encode) - def escape(*arg); end - - # ## Synopsis - # - # ```ruby - # URI.unescape(str) - # ``` - # - # ## Args - # - # `str` - # : [`String`](https://docs.ruby-lang.org/en/2.7.0/String.html) to unescape. - # - # - # ## Description - # - # This method is obsolete and should not be used. Instead, use - # [`CGI.unescape`](https://docs.ruby-lang.org/en/2.7.0/CGI/Util.html#method-i-unescape), - # [`URI.decode_www_form`](https://docs.ruby-lang.org/en/2.7.0/URI.html#method-c-decode_www_form) - # or - # [`URI.decode_www_form_component`](https://docs.ruby-lang.org/en/2.7.0/URI.html#method-c-decode_www_form_component) - # depending on your specific use case. - # - # ## Usage - # - # ```ruby - # require 'uri' - # - # enc_uri = URI.escape("http://example.com/?a=\11\15") - # # => "http://example.com/?a=%09%0D" - # - # URI.unescape(enc_uri) - # # => "http://example.com/?a=\t\r" - # ``` - # - # - # Also aliased as: - # [`decode`](https://docs.ruby-lang.org/en/2.7.0/URI/Escape.html#method-i-decode) - def unescape(*arg); end - +# The "file" [`URI`](https://docs.ruby-lang.org/en/2.7.0/URI.html) is defined by +# RFC8089. +class URI::File < URI::Generic end # [`FTP`](https://docs.ruby-lang.org/en/2.7.0/URI/FTP.html) @@ -2352,7 +2240,51 @@ end module URI::Util end -# The "file" [`URI`](https://docs.ruby-lang.org/en/2.7.0/URI.html) is defined by -# RFC8089. -class URI::File < URI::Generic +# The syntax of WS URIs is defined in RFC6455 section 3. +# +# Note that the Ruby URI library allows WS URLs containing usernames and +# passwords. This is not legal as per the RFC, but used to be +# supported in Internet Explorer 5 and 6, before the MS04-004 security +# update. See . +class URI::WS < URI::Generic + # A Default port of 80 for URI::WS. + DEFAULT_PORT = T.let(T.unsafe(nil), Integer) + + # An Array of the available components for URI::WS. + COMPONENT = T.let(T.unsafe(nil), T::Array[Symbol]) + + # == Description + # + # Creates a new URI::WS object from components, with syntax checking. + # + # The components accepted are userinfo, host, port, path, and query. + # + # The components should be provided either as an Array, or as a Hash + # with keys formed by preceding the component names with a colon. + # + # If an Array is used, the components must be passed in the + # order [userinfo, host, port, path, query]. + # + # Example: + # + # uri = URI::WS.build(host: 'www.example.com', path: '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/foo/bar') + # + # uri = URI::WS.build([nil, "www.example.com", nil, "/path", "query"]) + # + # Currently, if passed userinfo components this method generates + # invalid WS URIs as per RFC 1738. + def self.build(args); end + + # == Description + # + # Returns the full path for a WS URI, as required by Net::HTTP::Get. + # + # If the URI contains a query, the full path is URI#path + '?' + URI#query. + # Otherwise, the path is simply URI#path. + # + # Example: + # + # uri = URI::WS.build(path: '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/foo/bar', query: 'test=true') + # uri.request_uri # => "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/foo/bar?test=true" + def request_uri; end end diff --git a/resolver/CorrectTypeAlias.cc b/resolver/CorrectTypeAlias.cc index b6f8374336..21b9d992e5 100644 --- a/resolver/CorrectTypeAlias.cc +++ b/resolver/CorrectTypeAlias.cc @@ -1,7 +1,7 @@ #include #include "absl/strings/str_split.h" -#include "common/formatting.h" +#include "common/strings/formatting.h" #include "resolver/CorrectTypeAlias.h" using namespace std; diff --git a/resolver/GlobalPass.cc b/resolver/GlobalPass.cc index 3a2929378c..dbd9018e41 100644 --- a/resolver/GlobalPass.cc +++ b/resolver/GlobalPass.cc @@ -1,4 +1,4 @@ -#include "common/Timer.h" +#include "common/timers/Timer.h" #include "core/Names.h" #include "core/core.h" #include "core/errors/resolver.h" @@ -39,32 +39,71 @@ core::TypeMemberRef dealiasAt(const core::GlobalState &gs, core::TypeMemberRef t } } +namespace { +core::ErrorClass getRedeclarationErrorCode(const core::GlobalState &gs, core::ClassOrModuleRef parent, + core::NameRef name) { + if (parent == core::Symbols::Enumerable() || parent.data(gs)->derivesFrom(gs, core::Symbols::Enumerable())) { + return core::errors::Resolver::EnumerableParentTypeNotDeclared; + } else if (name == core::Names::Constants::AttachedClass()) { + return core::errors::Resolver::HasAttachedClassIncluded; + } else { + return core::errors::Resolver::ParentTypeNotDeclared; + } +} + +// Note having this error is intentional, as it makes it easier to implement Sorbet's incremental mode. +// +// In the past, we have floated ideas of allowing users to skip redeclaring a parent's type members if +// those type members are fixed, and simply copying the fixed type member down onto the child on the +// user's behalf. +// +// But that runs afoul of our (simple) heuristic to retypecheck all files that simply mention a symbol +// name on the LSP fast path. If a grandchild class was not forced to redeclare a grandparent's +// `type_member`, then the grandparent class's file could be edited and Sorbet wouldn't include the +// grandchild class's file in the set of files to retypecheck. +void reportRedeclarationError(core::GlobalState &gs, core::ClassOrModuleRef parent, + core::TypeMemberRef parentTypeMember, core::ClassOrModuleRef sym) { + auto name = parentTypeMember.data(gs)->name; + auto code = getRedeclarationErrorCode(gs, parent, name); + + if (auto e = gs.beginError(sym.data(gs)->loc(), code)) { + if (code == core::errors::Resolver::HasAttachedClassIncluded) { + auto hasAttachedClass = core::Names::declareHasAttachedClass().show(gs); + if (sym.data(gs)->isModule()) { + e.setHeader("`{}` declared by parent `{}` must be re-declared in `{}`", hasAttachedClass, + parent.show(gs), sym.show(gs)); + } else if (sym.data(gs)->isSingletonClass(gs)) { + // We'd only get this type member redeclaration error in a singleton class if + // the attached class of this singleton class is a module (because all classes' + // singleton classes get the `` type member declared) + ENFORCE(sym.data(gs)->attachedClass(gs).data(gs)->isModule()); + e.setHeader("`{}` was declared `{}` and so cannot be `{}`ed into the module `{}`", parent.show(gs), + hasAttachedClass, "extend", sym.data(gs)->attachedClass(gs).show(gs)); + } else if (sym.data(gs)->derivesFrom(gs, core::Symbols::Class())) { + e.setHeader("`{}` is a subclass of `{}` which is not allowed", sym.show(gs), "Class"); + } else { + // sym is a normal, non singleton class + e.setHeader("`{}` was declared `{}` and so must be `{}`ed into the class `{}`", parent.show(gs), + hasAttachedClass, "extend", sym.show(gs)); + } + e.addErrorLine(parentTypeMember.data(gs)->loc(), "`{}` declared in parent here", hasAttachedClass); + } else { + e.setHeader("Type `{}` declared by parent `{}` must be re-declared in `{}`", name.show(gs), parent.show(gs), + sym.show(gs)); + e.addErrorLine(parentTypeMember.data(gs)->loc(), "`{}` declared in parent here", name.show(gs)); + } + } +} +} // namespace + bool resolveTypeMember(core::GlobalState &gs, core::ClassOrModuleRef parent, core::TypeMemberRef parentTypeMember, core::ClassOrModuleRef sym, vector>> &typeAliases) { core::NameRef name = parentTypeMember.data(gs)->name; - core::SymbolRef my = sym.data(gs)->findMember(gs, name); + core::SymbolRef my = sym.data(gs)->findMemberNoDealias(gs, name); if (!my.exists()) { - auto code = - parent == core::Symbols::Enumerable() || parent.data(gs)->derivesFrom(gs, core::Symbols::Enumerable()) - ? core::errors::Resolver::EnumerableParentTypeNotDeclared - : core::errors::Resolver::ParentTypeNotDeclared; - - if (auto e = gs.beginError(sym.data(gs)->loc(), code)) { - // Note having this error is intentional, as it makes it easier to implement Sorbet's incremental mode. - // - // In the past, we have floated ideas of allowing users to skip redeclaring a parent's type members if - // those type members are fixed, and simply copying the fixed type member down onto the child on the - // user's behalf. - // - // But that runs afoul of our (simple) heuristic to retypecheck all files that simply mention a symbol - // name on the LSP fast path. If a grandchild class was not forced to redeclare a grandparent's - // `type_member`, then the grandparent class's file could be edited and Sorbet wouldn't include the - // grandchild class's file in the set of files to retypecheck. - e.setHeader("Type `{}` declared by parent `{}` must be re-declared in `{}`", name.show(gs), parent.show(gs), - sym.show(gs)); - e.addErrorLine(parentTypeMember.data(gs)->loc(), "`{}` declared in parent here", name.show(gs)); - } + reportRedeclarationError(gs, parent, parentTypeMember, sym); + auto typeMember = gs.enterTypeMember(sym.data(gs)->loc(), sym, name, core::Variance::Invariant); typeMember.data(gs)->flags.isFixed = true; auto untyped = core::Types::untyped(gs, sym); @@ -74,7 +113,21 @@ bool resolveTypeMember(core::GlobalState &gs, core::ClassOrModuleRef parent, cor } if (!my.isTypeMember()) { if (auto e = gs.beginError(my.loc(gs), core::errors::Resolver::NotATypeVariable)) { - e.setHeader("Type variable `{}` needs to be declared as `= type_member(SOMETHING)`", name.show(gs)); + auto defaultError = true; + if (my.isClassAlias(gs)) { + auto dealiased = my.dealias(gs); + if (dealiased.owner(gs) == sym.data(gs)->lookupSingletonClass(gs) && dealiased.name(gs) == name) { + e.setHeader("`{}` must be declared as a type_member (not a type_template) to match the parent", + name.show(gs)); + e.addErrorLine(parentTypeMember.data(gs)->loc(), "Declared in parent `{}` here", + parentTypeMember.data(gs)->owner.show(gs)); + defaultError = false; + } + } + if (defaultError) { + e.setHeader("Type variable `{}` needs to be declared as a type_member or type_template, not a {}", + name.show(gs), my.showKind(gs)); + } } auto synthesizedName = gs.freshNameUnique(core::UniqueNameKind::TypeVarName, name, 1); auto typeMember = gs.enterTypeMember(sym.data(gs)->loc(), sym, synthesizedName, core::Variance::Invariant); @@ -127,7 +180,7 @@ void resolveTypeMembers(core::GlobalState &gs, core::ClassOrModuleRef sym, // check that type params are in the same order. for (auto parentTypeMember : parentTypeMembers) { auto my = dealiasAt(gs, parentTypeMember, sym, typeAliases); - ENFORCE(my.exists(), "resolver failed to register type member aliases"); + ENFORCE(my.exists(), "resolver failed to register type member aliases sym={}", sym.show(gs)); if (sym.data(gs)->typeMembers()[parentIdx] != my) { if (auto e = gs.beginError(my.data(gs)->loc(), core::errors::Resolver::TypeMembersInWrongOrder)) { e.setHeader("Type members for `{}` repeated in wrong order", sym.show(gs)); @@ -160,26 +213,6 @@ void resolveTypeMembers(core::GlobalState &gs, core::ClassOrModuleRef sym, } } - if (sym.data(gs)->isClass()) { - for (auto tm : sym.data(gs)->typeMembers()) { - // AttachedClass is covariant, but not controlled by the user. - if (tm.data(gs)->name == core::Names::Constants::AttachedClass()) { - continue; - } - - auto myVariance = tm.data(gs)->variance(); - if (myVariance != core::Variance::Invariant) { - auto loc = tm.data(gs)->loc(); - if (!loc.file().data(gs).isPayload()) { - if (auto e = gs.beginError(loc, core::errors::Resolver::VariantTypeMemberInClass)) { - e.setHeader("Classes can only have invariant type members"); - } - return; - } - } - } - } - // If this class has no type members, fix attached class early. if (sym.data(gs)->typeMembers().empty()) { sym.data(gs)->unsafeComputeExternalType(gs); @@ -220,11 +253,13 @@ void Resolver::finalizeAncestors(core::GlobalState &gs) { if (!ref.data(gs)->isClassModuleSet()) { // we did not see a declaration for this type not did we see it used. Default to module. ref.data(gs)->setIsModule(true); - - // allow us to catch undeclared modules in LSP fast path, so we can report ambiguous - // definition errors. - ref.data(gs)->flags.isUndeclared = true; + ref.data(gs)->singletonClass(gs); // force singleton class into existence } + } + + auto n = gs.classAndModulesUsed(); + for (int i = 1; i < n; ++i) { + auto ref = core::ClassOrModuleRef(gs, i); auto loc = ref.data(gs)->loc(); if (loc.file().exists() && loc.file().data(gs).sourceType == core::File::Type::Normal) { if (ref.data(gs)->isClass()) { @@ -255,8 +290,11 @@ void Resolver::finalizeAncestors(core::GlobalState &gs) { ref.data(gs)->setSuperClass(core::Symbols::Module()); } else { ENFORCE(attached.data(gs)->superClass() != core::Symbols::todo()); - auto singleton = attached.data(gs)->superClass().data(gs)->singletonClass(gs); - ref.data(gs)->setSuperClass(singleton); + auto singletonSuperClass = attached.data(gs)->superClass().data(gs)->lookupSingletonClass(gs); + if (!singletonSuperClass.exists()) { + singletonSuperClass = core::Symbols::Class(); + } + ref.data(gs)->setSuperClass(singletonSuperClass); } } else { if (ref.data(gs)->isClass()) { @@ -271,6 +309,8 @@ void Resolver::finalizeAncestors(core::GlobalState &gs) { } } } + ENFORCE(n == gs.classAndModulesUsed(), + "Cannot add new classes in this loop--might not have finalized the new classes!") prodCounterAdd("types.input.modules.total", moduleCount); prodCounterAdd("types.input.classes.total", classCount); @@ -285,39 +325,43 @@ void Resolver::finalizeSymbols(core::GlobalState &gs) { // that resolves types and we don't want to introduce additional passes if // we don't have to. It would be a tractable refactor to merge it // `ResolveConstantsWalk` if it becomes necessary to process earlier. - for (uint32_t i = 1; i < gs.classAndModulesUsed(); ++i) { - auto sym = core::ClassOrModuleRef(gs, i); - - if (sym.data(gs)->flags.isLinearizationComputed) { - // Without this, the addMixin below for mixedInClassMethods is not idempotent on the - // fast path, and will accidentally mix a `ClassMethods` module into all children (not - // just the class that has the `include` triggering the mixes_in_class_methods, but all - // subclasses of that class). - continue; - } + { + Timer timer(gs.tracer(), "resolver.mix_in_class_methods"); + + for (uint32_t i = 1; i < gs.classAndModulesUsed(); ++i) { + auto sym = core::ClassOrModuleRef(gs, i); - core::ClassOrModuleRef singleton; - for (auto ancst : sym.data(gs)->mixins()) { - // Reading the fake property created in resolver#resolveClassMethodsJob(){} - auto mixedInClassMethods = ancst.data(gs)->findMethod(gs, core::Names::mixedInClassMethods()); - if (!mixedInClassMethods.exists()) { + if (sym.data(gs)->flags.isLinearizationComputed) { + // Without this, the addMixin below for mixedInClassMethods is not idempotent on the + // fast path, and will accidentally mix a `ClassMethods` module into all children (not + // just the class that has the `include` triggering the mixes_in_class_methods, but all + // subclasses of that class). continue; } - if (!singleton.exists()) { - singleton = sym.data(gs)->singletonClass(gs); - } - auto &resultType = mixedInClassMethods.data(gs)->resultType; - ENFORCE(resultType != nullptr && core::isa_type(resultType)); - auto types = core::cast_type(resultType); - - for (auto &type : types->elems) { - ENFORCE(core::isa_type(type)); - auto classType = core::cast_type_nonnull(type); - if (!singleton.data(gs)->addMixin(gs, classType.symbol)) { - // Should never happen. We check in ResolveConstantsWalk that classMethods are a module before - // adding it as a member. - ENFORCE(false); + core::ClassOrModuleRef singleton; + for (auto ancst : sym.data(gs)->mixins()) { + // Reading the fake property created in resolver#resolveClassMethodsJob(){} + auto mixedInClassMethods = ancst.data(gs)->findMethod(gs, core::Names::mixedInClassMethods()); + if (!mixedInClassMethods.exists()) { + continue; + } + if (!singleton.exists()) { + singleton = sym.data(gs)->singletonClass(gs); + } + + auto &resultType = mixedInClassMethods.data(gs)->resultType; + ENFORCE(resultType != nullptr && core::isa_type(resultType)); + auto types = core::cast_type(resultType); + + for (auto &type : types->elems) { + ENFORCE(core::isa_type(type)); + auto classType = core::cast_type_nonnull(type); + if (!singleton.data(gs)->addMixin(gs, classType.symbol)) { + // Should never happen. We check in ResolveConstantsWalk that classMethods are a module before + // adding it as a member. + ENFORCE(false); + } } } } @@ -325,17 +369,21 @@ void Resolver::finalizeSymbols(core::GlobalState &gs) { gs.computeLinearization(); - vector>> typeAliases; - typeAliases.resize(gs.classAndModulesUsed()); - vector resolved; - resolved.resize(gs.classAndModulesUsed()); - for (int i = 1; i < gs.classAndModulesUsed(); ++i) { - auto sym = core::ClassOrModuleRef(gs, i); - resolveTypeMembers(gs, sym, typeAliases, resolved); + { + Timer timer(gs.tracer(), "resolver.resolve_type_members"); + + vector>> typeAliases; + typeAliases.resize(gs.classAndModulesUsed()); + vector resolved; + resolved.resize(gs.classAndModulesUsed()); + for (int i = 1; i < gs.classAndModulesUsed(); ++i) { + auto sym = core::ClassOrModuleRef(gs, i); + resolveTypeMembers(gs, sym, typeAliases, resolved); - if (gs.requiresAncestorEnabled) { - // Precompute the list of all required ancestors for this symbol - sym.data(gs)->computeRequiredAncestorLinearization(gs); + if (gs.requiresAncestorEnabled) { + // Precompute the list of all required ancestors for this symbol + sym.data(gs)->computeRequiredAncestorLinearization(gs); + } } } } diff --git a/resolver/resolver.cc b/resolver/resolver.cc index c1ae893db7..ca7962d940 100644 --- a/resolver/resolver.cc +++ b/resolver/resolver.cc @@ -3,7 +3,7 @@ #include "ast/Trees.h" #include "ast/ast.h" #include "ast/treemap/treemap.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "core/Error.h" #include "core/Names.h" #include "core/StrictLevel.h" @@ -17,8 +17,8 @@ #include "absl/algorithm/container.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_split.h" -#include "common/Timer.h" #include "common/concurrency/ConcurrentQueue.h" +#include "common/timers/Timer.h" #include "core/Symbols.h" #include #include @@ -129,8 +129,17 @@ class ResolveConstantsWalk { Nesting(shared_ptr parent, core::SymbolRef scope) : parent(std::move(parent)), scope(scope) {} }; + shared_ptr nesting_; + // Map of SymbolRef to first location of a symbol definition in a file. This is used for out-of-order reference + // checking. When present in this map, the loc stored is different than a symbol's canonical loc. Specifically, + // in case there are multiple definitions of a symbol in the same file, we store the loc of the *first* one. + UnorderedMap firstDefinitionLocs; + + // Increments to track whether we're at a class top level or whether we're inside a block/method body. + int loadScopeDepth_; + struct ConstantResolutionItem { shared_ptr scope; ast::ConstantLit *out; @@ -295,10 +304,47 @@ class ResolveConstantsWalk { return !checker.seenUnresolved; } + // Find load-time out-of-order references. + // Algorithm: + // if resolved symbol is only defined in the current file, and + // the definition is after the reference loc, + // then, report an error. + static void checkReferenceOrder(core::Context ctx, core::SymbolRef resolutionResult, + const ast::UnresolvedConstantLit &c, + const UnorderedMap &firstDefinitionLocs) { + if (!ctx.file.data(ctx).isRBI() && resolutionResult.exists() && + resolutionResult.isOnlyDefinedInFile(ctx.state, ctx.file)) { + core::LocOffsets defLoc; + const auto it = firstDefinitionLocs.find(resolutionResult); + + if (it != firstDefinitionLocs.end()) { + // Use the loc from the local first defs table, if it exists. + // When the symbol is declared multiple times in the same file, we want to ensure that + // we compare against the *first* definition. + defLoc = it->second; + } else { + // If the symbol isn't in the local first defs table, that means its loc + // in the symbol table is the same as its first definition in this file. + // (We skip storing redundant information in firstDefinitionLocs to save memory) + defLoc = resolutionResult.loc(ctx).offsets(); + } + + // Check for ordering between reference loc and definition loc. + const auto &refLoc = c.loc; + if (defLoc.beginPos() > refLoc.endPos()) { + if (auto e = ctx.beginError(c.loc, core::errors::Resolver::OutOfOrderConstantAccess)) { + e.setHeader("`{}` referenced before it is defined", resolutionResult.show(ctx)); + e.addErrorLine(ctx.locAt(defLoc), "Defined here"); + } + } + } + } + static core::SymbolRef resolveConstant(core::Context ctx, const shared_ptr &nesting, const ast::UnresolvedConstantLit &c, bool &resolutionFailed) { if (ast::isa_tree(c.scope)) { core::SymbolRef result = resolveLhs(ctx, nesting, c.cnst); + return result; } if (auto *id = ast::cast_tree(c.scope)) { @@ -329,6 +375,7 @@ class ResolveConstantsWalk { e.setHeader("Non-private reference to private constant `{}` referenced", result.show(ctx)); } } + return result; } @@ -396,7 +443,7 @@ class ResolveConstantsWalk { PackageStub stub; vector exports; - // NOTE: these are the public-facing exports of the package that start with the `Test::` special prefix. They + // NOTE: these are the public-facing exports of the package that start with the `Test::` special prefix. They // are not the names exported to the implicit test package via `export_for_test`. vector testExports; @@ -488,6 +535,7 @@ class ResolveConstantsWalk { ast::cast_tree(out->original)->cnst); auto data = symbol.data(ctx); + data->setIsModule(true); // This is what would happen in finalizeAncestors // force a singleton into existence auto singletonClass = data->singletonClass(ctx); if (possibleGenericType) { @@ -731,48 +779,86 @@ class ResolveConstantsWalk { if (!foundCommonTypo && suggestionCount < MAX_SUGGESTION_COUNT && suggestScope.exists() && suggestScope.isClassOrModule()) { suggestionCount++; + auto suggested = suggestScope.asClassOrModuleRef().data(ctx)->findMemberFuzzyMatch(ctx, original.cnst); + + if (ctx.file.data(ctx).isPackage() && + !suggestScope.asClassOrModuleRef().isPackageSpecSymbol(ctx.state)) { + // In case the file is a __package.rb file, and the scope is not a PackageSpec-scoped symbol, + // the resolution error must be in an export statement. In this case, suggestions must be + // restricted to within the current package only. They must not cross package boundaries as + // out-of-package suggestions would be inherently invalid. + + // TODO (aadi-stripe) Find a less brittle way of ascertaining whether the error comes from an + // export statement. Currently (1/9/23) this happens to work because export statements are the + // only part of the packager DSL that do not prepend PackageSpec to the relevant constant. + + // Can't use pkg.ownsSymbol since it uses symbol definition locs, which have an edge case that + // isn't handled until the VisibilityChecker pass. + const auto pkgRootSymbol = ctx.state.packageDB() + .getPackageForFile(ctx.state, ctx.file) + .getRootSymbolForAutocorrectSearch(ctx.state, suggestScope); + + auto it = std::remove_if(suggested.begin(), suggested.end(), + [&pkgRootSymbol, &gs](auto &suggestion) -> bool { + return !suggestion.symbol.isUnderNamespace(gs, pkgRootSymbol); + }); + suggested.erase(it, suggested.end()); + } + if (suggested.size() > 3) { suggested.resize(3); } - if (!suggested.empty()) { - for (auto suggestion : suggested) { - const auto replacement = suggestion.symbol.show(ctx); - e.didYouMean(replacement, ctx.locAt(job.out->loc)); - e.addErrorLine(suggestion.symbol.loc(ctx), "`{}` defined here", replacement); + for (auto suggestion : suggested) { + const auto replacement = suggestion.symbol.show(ctx); + auto replaceLoc = ctx.locAt(job.out->loc); + if (replaceLoc.source(ctx) == replacement) { + // The replacement is the same as the original. + // This can happen for a number of reasons, usually due to things + // where one of the names has an unprintable name from a rewriter. + // It's confusing to see those bad did you mean and they don't + // provide value. + continue; } + e.didYouMean(replacement, replaceLoc); + e.addErrorLine(suggestion.symbol.loc(ctx), "`{}` defined here", replacement); } } } } } - static bool resolveJob(core::Context ctx, ConstantResolutionItem &job) { - if (isAlreadyResolved(ctx, *job.out)) { - if (job.possibleGenericType) { + static bool resolveConstantJob(core::Context ctx, const shared_ptr &nesting, ast::ConstantLit *out, + bool &resolutionFailed, const bool possibleGenericType) { + if (isAlreadyResolved(ctx, *out)) { + if (possibleGenericType) { return false; } return true; } - auto &original = ast::cast_tree_nonnull(job.out->original); - auto resolved = resolveConstant(ctx.withOwner(job.scope->scope), job.scope, original, job.resolutionFailed); + auto &original = ast::cast_tree_nonnull(out->original); + auto resolved = resolveConstant(ctx.withOwner(nesting->scope), nesting, original, resolutionFailed); if (!resolved.exists()) { return false; } if (resolved.isTypeAlias(ctx)) { auto resolvedField = resolved.asFieldRef(); if (resolvedField.data(ctx)->resultType != nullptr) { - job.out->symbol = resolved; + out->symbol = resolved; return true; } return false; } - job.out->symbol = resolved; + out->symbol = resolved; return true; } + static bool resolveJob(core::Context ctx, ConstantResolutionItem &job) { + return resolveConstantJob(ctx, job.scope, job.out, job.resolutionFailed, job.possibleGenericType); + } + static bool resolveConstantResolutionItems(const core::GlobalState &gs, vector> &jobs, WorkerPool &workers) { @@ -1059,7 +1145,7 @@ class ResolveConstantsWalk { continue; } auto idSymbol = id->symbol.asClassOrModuleRef(); - if (idSymbol.data(gs)->isUndeclared()) { + if (!idSymbol.data(gs)->isDeclared()) { if (auto e = gs.beginError(idLoc, core::errors::Resolver::InvalidMixinDeclaration)) { e.setHeader("`{}` is declared implicitly, but must be defined as a `{}` explicitly", id->symbol.show(gs), "module"); @@ -1200,18 +1286,6 @@ class ResolveConstantsWalk { owner.data(gs)->recordRequiredAncestor(gs, symbol, blockLoc); } - static void tryRegisterSealedSubclass(core::MutableContext ctx, AncestorResolutionItem &job) { - ENFORCE(job.ancestor->symbol.exists(), "Ancestor must exist, or we can't check whether it's sealed."); - auto ancestorSym = job.ancestor->symbol.dealias(ctx).asClassOrModuleRef(); - - if (!ancestorSym.data(ctx)->flags.isSealed) { - return; - } - Timer timeit(ctx.state.tracer(), "resolver.registerSealedSubclass"); - - ancestorSym.data(ctx)->recordSealedSubclass(ctx, job.klass); - } - void transformAncestor(core::Context ctx, core::ClassOrModuleRef klass, ast::ExpressionPtr &ancestor, bool isInclude, bool isSuperclass = false) { if (auto *constScope = ast::cast_tree(ancestor)) { @@ -1266,10 +1340,28 @@ class ResolveConstantsWalk { walkUnresolvedConstantLit(ctx, c->scope); auto loc = c->loc; auto out = ast::make_expression(loc, core::Symbols::noSymbol(), std::move(tree)); - ConstantResolutionItem job{nesting_, ast::cast_tree(out)}; - if (resolveJob(ctx, job)) { + auto *constant = ast::cast_tree(out); + bool resolutionFailed = false; + const bool possibleGenericType = false; + if (resolveConstantJob(ctx, nesting_, constant, resolutionFailed, possibleGenericType)) { categoryCounterInc("resolve.constants.nonancestor", "firstpass"); + if (loadScopeDepth_ == 0 && (!constant->symbol.isClassOrModule() || + constant->symbol.asClassOrModuleRef().data(ctx)->isDeclared())) { + // While Sorbet treats class A::B; end like an implicit definition of A, it's actually a + // reference of A--Ruby will require a proper definition of A elsewhere. Long term, + // Sorbet should be taught to emit errors when these references are not actually defined, + // matching Ruby's behavior. Then the reference order checks will be able to check all + // references against their definitions, and not limit them to only isDeclared symbols here. + + // (Historically, Stripe's custom autoloader used static analysis to predeclare these + // intermediate namespaces, so they would always be defined at the right time. As Stripe's + // codebase moves away from this legacy autoloader, it will be easier to introduce such + // changes into Sorbet.) + checkReferenceOrder(ctx, constant->symbol, *c, firstDefinitionLocs); + } } else { + ConstantResolutionItem job{nesting_, constant}; + job.resolutionFailed = resolutionFailed; todo_.emplace_back(std::move(job)); } tree = std::move(out); @@ -1285,16 +1377,64 @@ class ResolveConstantsWalk { } public: - ResolveConstantsWalk() : nesting_(nullptr) {} + ResolveConstantsWalk() : nesting_(nullptr), loadScopeDepth_(0) {} - void preTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { - nesting_ = make_unique(std::move(nesting_), ast::cast_tree_nonnull(tree).symbol); + void preTransformMethodDef(core::Context ctx, ast::ExpressionPtr &tree) { + ENFORCE(loadScopeDepth_ >= 0); + loadScopeDepth_++; + } + + void postTransformMethodDef(core::Context ctx, ast::ExpressionPtr &tree) { + ENFORCE(loadScopeDepth_ > 0); + loadScopeDepth_--; + } + + void preTransformBlock(core::Context ctx, ast::ExpressionPtr &tree) { + ENFORCE(loadScopeDepth_ >= 0); + loadScopeDepth_++; + } + + void postTransformBlock(core::Context ctx, ast::ExpressionPtr &tree) { + ENFORCE(loadScopeDepth_ > 0); + loadScopeDepth_--; } void postTransformUnresolvedConstantLit(core::Context ctx, ast::ExpressionPtr &tree) { walkUnresolvedConstantLit(ctx, tree); } + void preTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { + auto &original = ast::cast_tree_nonnull(tree); + auto sym = original.symbol; + + // Populate local first definitions table for out-of-order reference checking. + // We only do this when we know we're going to need to consult the first definition loc and + // we know the information in the symbol table is insufficient. This avoids reundant memory + // storage overhead. + // + // In particular, `firstDefinitionLocs` does not store anything if this symbol is defined in + // more than one non-RBI file or if it's only defined once in this file. + // Otherwise, it stores the loc of the first definition of the symbol in this file. + if (!ctx.file.data(ctx).isRBI() && loadScopeDepth_ == 0 && sym.isOnlyDefinedInFile(ctx.state, ctx.file)) { + auto defLoc = sym.data(ctx)->loc(); + + if (!defLoc.file().data(ctx).isRBI()) { + ENFORCE(defLoc.file() == ctx.file); + auto declLoc = original.declLoc; + + if (defLoc.beginPos() > declLoc.endPos()) { + // When this condition is met, it means the file has multiple definitions of the symbol. + // If the insert succeeds below, the current definition is the first one. + // We need to use the first def for out-of-order checking, since any + // reference *before* the first def will be out of order. + firstDefinitionLocs.insert(std::make_pair(sym, declLoc)); + } + } + } + + nesting_ = make_unique(std::move(nesting_), sym); + } + void postTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { auto &original = ast::cast_tree_nonnull(tree); @@ -1386,7 +1526,7 @@ class ResolveConstantsWalk { } const auto &precedingSymKlass = precedingSymForCurDef.asClassOrModuleRef().data(ctx); - if (!precedingSymKlass->isUndeclared()) { + if (precedingSymKlass->isDeclared()) { // Not a filler def, but a real def return defaultSymbol; } @@ -1420,6 +1560,32 @@ class ResolveConstantsWalk { return; } + // Populate local first definitions table for out-of-order reference checking. + // We only do this when we know we're going to need to consult the first definition loc and + // we know the information in the symbol table is insufficient. This avoids reundant memory + // storage overhead. + // + // In particular, `firstDefinitionLocs` does not store anything if this symbol is defined in + // more than one non-RBI file or if it's only defined once in this file. + // Otherwise, it stores the loc of the first definition of the symbol in this file. + if (!ctx.file.data(ctx).isRBI() && loadScopeDepth_ == 0 && + id->symbol.isOnlyDefinedInFile(ctx.state, ctx.file)) { + auto defLoc = id->symbol.loc(ctx); + + if (!defLoc.file().data(ctx).isRBI()) { + ENFORCE(defLoc.file() == ctx.file); + auto declLoc = asgn.loc; + + if (defLoc.beginPos() > declLoc.endPos()) { + // When this condition is met, it means the file has multiple definitions of the symbol. + // If the insert succeeds below, the current definition is the first one. + // We need to use the first def for out-of-order checking, since any + // reference *before* the first def will be out of order. + firstDefinitionLocs.insert(std::make_pair(id->symbol, declLoc)); + } + } + } + auto *send = ast::cast_tree(asgn.rhs); if (send != nullptr && send->fun == core::Names::typeAlias()) { if (!send->hasBlock()) { @@ -1659,9 +1825,6 @@ class ResolveConstantsWalk { const auto origSize = job.items.size(); auto g = [&](AncestorResolutionItem &item) -> bool { auto resolved = resolveAncestorJob(ctx, item, false); - if (resolved) { - tryRegisterSealedSubclass(ctx, item); - } return resolved; }; auto fileIt = remove_if(job.items.begin(), job.items.end(), std::move(g)); @@ -1803,12 +1966,10 @@ class ResolveConstantsWalk { } } - if (singlePackageRbiGeneration) { - for (auto &job : todoClassAliases) { - core::MutableContext ctx(gs, core::Symbols::root(), job.file); - for (auto &item : job.items) { - resolveClassAliasJob(ctx, item); - } + for (auto &job : todoClassAliases) { + core::MutableContext ctx(gs, core::Symbols::root(), job.file); + for (auto &item : job.items) { + resolveClassAliasJob(ctx, item); } } @@ -1893,6 +2054,11 @@ class ResolveTypeMembersAndFieldsWalk { core::NameRef fromName; }; + struct RecordSealedSubclassItem { + core::ClassOrModuleRef sealedClass; + core::ClassOrModuleRef subclass; + }; + struct ResolveTypeMembersAndFieldsWorkerResult { vector files; vector todoAssigns; @@ -1903,6 +2069,7 @@ class ResolveTypeMembersAndFieldsWalk { vector todoResolveStaticFieldItems; vector todoResolveSimpleStaticFieldItems; vector todoMethodAliasItems; + vector todoSealedSubclassItems; }; struct ResolveTypeMembersAndFieldsResult { @@ -1918,6 +2085,7 @@ class ResolveTypeMembersAndFieldsWalk { vector todoResolveStaticFieldItems_; vector todoResolveSimpleStaticFieldItems_; vector todoMethodAliasItems_; + vector todoSealedSubclassItems_; // State for tracking type usage inside of a type alias or type member // definition @@ -2119,7 +2287,8 @@ class ResolveTypeMembersAndFieldsWalk { return; } - if (cast.cast != core::Names::let() && cast.cast != core::Names::uncheckedLet()) { + if (cast.cast != core::Names::let() && cast.cast != core::Names::uncheckedLet() && + cast.cast != core::Names::assumeType()) { if (auto e = ctx.beginError(cast.loc, core::errors::Resolver::ConstantAssertType)) { e.setHeader("Use `{}` to specify the type of constants", "T.let"); } @@ -2708,6 +2877,25 @@ class ResolveTypeMembersAndFieldsWalk { if (isGenericResolved(ctx, klass.symbol)) { todoAttachedClassItems_.emplace_back(ResolveAttachedClassItem{ctx.owner, klass.symbol, ctx.file}); } + + for (const auto &ancestor : klass.ancestors) { + auto *ancestorCnst = ast::cast_tree(ancestor); + if (ancestorCnst == nullptr) { + continue; + } + + auto dealiased = ancestorCnst->symbol.dealias(ctx); + if (!dealiased.isClassOrModule()) { + continue; + } + + auto ancestorSym = dealiased.asClassOrModuleRef(); + if (!ancestorSym.data(ctx)->flags.isSealed) { + continue; + } + + todoSealedSubclassItems_.emplace_back(RecordSealedSubclassItem{ancestorSym, klass.symbol}); + } } void postTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { @@ -2805,10 +2993,26 @@ class ResolveTypeMembersAndFieldsWalk { } void postTransformCast(core::Context ctx, ast::ExpressionPtr &tree) { + auto *cast = ast::cast_tree(tree); + if (cast->cast == core::Names::assumeType()) { + // This cast was not written by the user. Before we attempt to parse it as a type, let's + // make sure that it's even possible to be valid. + auto *cnst = ast::cast_tree(cast->typeExpr); + ENFORCE(cnst != nullptr, "Rewriter should always use const for typeExpr, which should now be resolved"); + if (!cnst->symbol.isClassOrModule() || cnst->symbol.asClassOrModuleRef().data(ctx)->flags.isModule || + cnst->symbol.asClassOrModuleRef().data(ctx)->typeArity(ctx) > 0) { + // The rewriter was over-eager in attempting to infer type `A` for `A.new` because + // `A` was not a class (or was a generic class, and thus generated the wrong annotation). + // Get rid of the cast, replace it with the original arg. + tree = move(cast->arg); + return; + } + } + ResolveCastItem item; item.file = ctx.file; item.owner = ctx.owner; - item.cast = ast::cast_tree(tree); + item.cast = cast; item.inFieldAssign = this->inFieldAssign.back(); if (!tryResolveSimpleClassCastItem(ctx.state, item)) { todoResolveCastItems_.emplace_back(move(item)); @@ -2841,6 +3045,7 @@ class ResolveTypeMembersAndFieldsWalk { case core::Names::let().rawId(): case core::Names::bind().rawId(): case core::Names::uncheckedLet().rawId(): + case core::Names::assumeType().rawId(): case core::Names::assertType().rawId(): case core::Names::cast().rawId(): { if (send.numPosArgs() < 2) { @@ -3025,6 +3230,7 @@ class ResolveTypeMembersAndFieldsWalk { output.todoResolveStaticFieldItems = move(walk.todoResolveStaticFieldItems_); output.todoResolveSimpleStaticFieldItems = move(walk.todoResolveSimpleStaticFieldItems_); output.todoMethodAliasItems = move(walk.todoMethodAliasItems_); + output.todoSealedSubclassItems = move(walk.todoSealedSubclassItems_); auto count = output.files.size(); outputq->push(move(output), count); } @@ -3041,6 +3247,7 @@ class ResolveTypeMembersAndFieldsWalk { vector> combinedTodoResolveStaticFieldItems; vector> combinedTodoResolveSimpleStaticFieldItems; vector> combinedTodoMethodAliasItems; + vector> combinedTodoSealedSubclassItems; { ResolveTypeMembersAndFieldsWorkerResult threadResult; @@ -3062,6 +3269,7 @@ class ResolveTypeMembersAndFieldsWalk { combinedTodoResolveSimpleStaticFieldItems.emplace_back( move(threadResult.todoResolveSimpleStaticFieldItems)); combinedTodoMethodAliasItems.emplace_back(move(threadResult.todoMethodAliasItems)); + combinedTodoSealedSubclassItems.emplace_back(move(threadResult.todoSealedSubclassItems)); } } } @@ -3171,6 +3379,14 @@ class ResolveTypeMembersAndFieldsWalk { resolveMethodAlias(ctx, job); } } + { + Timer timeit(gs.tracer(), "resolver.registerSealedSubclass"); + for (auto &threadTodos : combinedTodoSealedSubclassItems) { + for (auto &[sealedClass, subclass] : threadTodos) { + sealedClass.data(gs)->recordSealedSubclass(gs, subclass); + } + } + } return {move(combinedFiles), move(stillPendingTodoResolveCastItems)}; } @@ -3438,9 +3654,7 @@ class ResolveSignaturesWalk { if (spec != sig.argTypes.end()) { ENFORCE(spec->type != nullptr); - // TODO(#4095) Raise error if `bind` used for non-`&blk` arg - if (!isBlkArg && (spec->rebind == core::Symbols::MagicBindToAttachedClass() || - spec->rebind == core::Symbols::MagicBindToSelfType())) { + if (!isBlkArg && spec->rebind.exists()) { if (auto e = ctx.state.beginError(spec->nameLoc, core::errors::Resolver::BindNonBlockParameter)) { e.setHeader("Using `{}` is not permitted here", "bind"); e.addErrorNote("Only block arguments can use `{}`", "bind"); @@ -3774,6 +3988,7 @@ class ResolveSignaturesWalk { overloadSym = ctx.state.enterNewMethodOverload(ctx.locAt(sig.loc), mdef.symbol, originalName, i, sig.argsToKeep); overloadSym.data(ctx)->setMethodVisibility(mdef.symbol.data(ctx)->methodVisibility()); + overloadSym.data(ctx)->intrinsicOffset = mdef.symbol.data(ctx)->intrinsicOffset; if (i != sigs.size() - 1) { overloadSym.data(ctx)->flags.isOverloaded = true; } @@ -4082,14 +4297,15 @@ vector resolveSigs(core::GlobalState &gs, vector &trees) { - if (debug_mode) { + SLOW_DEBUG_ONLY({ Timer timeit(gs.tracer(), "resolver.sanity_check"); ResolveSanityCheckWalk sanity; for (auto &tree : trees) { core::Context ctx(gs, core::Symbols::root(), tree.file); + ENFORCE(tree.tree); ast::TreeWalk::apply(ctx, sanity, tree.tree); } - } + }); } void verifyLinearizationComputed(const core::GlobalState &gs) { @@ -4116,6 +4332,7 @@ ast::ParsedFilesOrCancelled Resolver::run(core::GlobalState &gs, vector trees) { +ast::ParsedFilesOrCancelled Resolver::runIncremental(core::GlobalState &gs, vector trees, + bool ranIncrementalNamer) { auto workers = WorkerPool::create(0, gs.tracer()); trees = ResolveConstantsWalk::resolveConstants(gs, std::move(trees), *workers); // NOTE: Linearization does not need to be recomputed as we do not mutate mixins() during incremental resolve. @@ -4139,7 +4357,13 @@ ast::ParsedFilesOrCancelled Resolver::runIncremental(core::GlobalState &gs, vect // (verifyLinearizationComputed vs finalizeAncestors is currently the only difference between // `run` and `runIncremental`. If we ever change the fast path in a way that needs linearization // to be recomputed, we can simply make `runIncremental` be `run`.) - Resolver::finalizeSymbols(gs); + // Note: While this ^ is technically true from a correctness perspective, finalizeSymbols is too + // slow to run on all fast path edits, and so is skipped for performance (unless required). + // If we had a faster/incremental way to do finalizeSymbols, we could maybe start + // unconditionally finalizing symbols again, and then the above note about lineraization would apply. + if (ranIncrementalNamer) { + Resolver::finalizeSymbols(gs); + } auto rtmafResult = ResolveTypeMembersAndFieldsWalk::run(gs, std::move(trees), *workers); auto result = resolveSigs(gs, std::move(rtmafResult.trees), *workers); ResolveTypeMembersAndFieldsWalk::resolvePendingCastItems(gs, rtmafResult.todoResolveCastItems); diff --git a/resolver/resolver.h b/resolver/resolver.h index 2d95aeebef..1a8fcdb2e6 100644 --- a/resolver/resolver.h +++ b/resolver/resolver.h @@ -21,7 +21,8 @@ class Resolver final { * * These two versions are explicitly instantiated in resolver.cc */ - static ast::ParsedFilesOrCancelled runIncremental(core::GlobalState &gs, std::vector trees); + static ast::ParsedFilesOrCancelled runIncremental(core::GlobalState &gs, std::vector trees, + bool ranIncrementalNamer); // used by autogen only static std::vector runConstantResolution(core::GlobalState &gs, std::vector trees, diff --git a/resolver/type_syntax/type_syntax.cc b/resolver/type_syntax/type_syntax.cc index 6738513251..4792f75995 100644 --- a/resolver/type_syntax/type_syntax.cc +++ b/resolver/type_syntax/type_syntax.cc @@ -518,12 +518,14 @@ optional parseSigWithSelfTypeParams(core::Context ctx, const ast::Sen return nullopt; } sig.returns = move(maybeReturns.value()); + sig.returnsLoc = ctx.locAt(send->loc); break; } case core::Names::void_().rawId(): sig.seen.void_ = true; sig.returns = core::Types::void_(); + sig.returnsLoc = ctx.locAt(send->loc); break; case core::Names::checked().rawId(): sig.seen.checked = true; @@ -606,6 +608,106 @@ void unexpectedKwargs(core::Context ctx, const ast::Send &send) { } } +core::ClassOrModuleRef sendLooksLikeBadTypeApplication(core::Context ctx, const ast::Send &send) { + core::SymbolRef maybeScopeClass; + if (auto *recv = ast::cast_tree(send.recv)) { + maybeScopeClass = recv->symbol; + } else if (send.recv.isSelfReference()) { + // Let's not try to reinvent constant resolution here and just pick a heuristic that tends to + // work in some cases and is simple. + maybeScopeClass = core::Symbols::root(); + } else { + return core::Symbols::noClassOrModule(); + } + + if (!maybeScopeClass.isClassOrModule()) { + return core::Symbols::noClassOrModule(); + } + + auto scope = maybeScopeClass.asClassOrModuleRef(); + + auto className = ctx.state.lookupNameConstant(send.fun); + if (!className.exists()) { + // The name itself doesn't even exist, so definitely no class with this name can exist + return core::Symbols::noClassOrModule(); + } + + auto maybeSym = scope.data(ctx)->findMember(ctx, className); + if (!maybeSym.exists()) { + return core::Symbols::noClassOrModule(); + } + + if (!maybeSym.isClassOrModule()) { + return core::Symbols::noClassOrModule(); + } + + auto klass = maybeSym.asClassOrModuleRef(); + + if (klass.data(ctx)->typeArity(ctx) == 0) { + return core::Symbols::noClassOrModule(); + } + + return klass; +} + +optional parseTClassOf(core::Context ctx, const ast::Send &send, const ParsedSig &sig, + TypeSyntaxArgs args) { + if (send.numPosArgs() != 1 || send.hasKwArgs()) { + unexpectedKwargs(ctx, send); + return core::Symbols::untyped(); + } + + auto *obj = ast::cast_tree(send.getPosArg(0)); + if (!obj) { + if (auto e = ctx.beginError(send.loc, core::errors::Resolver::InvalidTypeDeclaration)) { + auto maybeType = getResultTypeWithSelfTypeParams(ctx, send.getPosArg(0), sig, args); + if (!maybeType.has_value()) { + return nullopt; + } + auto type = move(maybeType.value()); + std::vector classes; + auto shouldAutoCorrect = recurseOrType(ctx, type, classes); + if (core::isa_type(type) && shouldAutoCorrect) { + auto autocorrect = fmt::format("T.any({})", fmt::join(classes, ", ")); + e.setHeader("`{}` must wrap each individual class type, not the outer `{}`", "T.class_of", "T.any"); + e.replaceWith("Distribute `T.class_of`", ctx.locAt(send.loc), "{}", autocorrect); + } else { + e.setHeader("`{}` needs a class or module as its argument", "T.class_of"); + } + } + return core::Symbols::untyped(); + } + auto maybeAliased = obj->symbol; + if (maybeAliased.isTypeAlias(ctx)) { + if (auto e = ctx.beginError(send.loc, core::errors::Resolver::InvalidTypeDeclaration)) { + e.setHeader("T.class_of can't be used with a T.type_alias"); + } + return core::Symbols::untyped(); + } + if (maybeAliased.isTypeMember()) { + if (auto e = ctx.beginError(send.loc, core::errors::Resolver::InvalidTypeDeclaration)) { + e.setHeader("T.class_of can't be used with a T.type_member"); + } + return core::Symbols::untyped(); + } + auto sym = maybeAliased.dealias(ctx); + if (sym.isStaticField(ctx)) { + if (auto e = ctx.beginError(send.loc, core::errors::Resolver::InvalidTypeDeclaration)) { + e.setHeader("T.class_of can't be used with a constant field"); + } + return core::Symbols::untyped(); + } + + auto singleton = sym.asClassOrModuleRef().data(ctx)->lookupSingletonClass(ctx); + if (!singleton.exists()) { + if (auto e = ctx.beginError(send.loc, core::errors::Resolver::InvalidTypeDeclaration)) { + e.setHeader("Unknown class"); + } + return core::Symbols::untyped(); + } + return singleton; +} + optional interpretTCombinator(core::Context ctx, const ast::Send &send, const ParsedSig &sig, TypeSyntaxArgs args) { switch (send.fun.rawId()) { @@ -750,61 +852,14 @@ optional interpretTCombinator(core::Context ctx, const a return TypeSyntax::ResultType{result, core::Symbols::noClassOrModule()}; } case core::Names::classOf().rawId(): { - if (send.numPosArgs() != 1 || send.hasKwArgs()) { - unexpectedKwargs(ctx, send); - return TypeSyntax::ResultType{core::Types::untypedUntracked(), core::Symbols::noClassOrModule()}; - } - - auto *obj = ast::cast_tree(send.getPosArg(0)); - if (!obj) { - if (auto e = ctx.beginError(send.loc, core::errors::Resolver::InvalidTypeDeclaration)) { - auto maybeType = getResultTypeWithSelfTypeParams(ctx, send.getPosArg(0), sig, args); - if (!maybeType.has_value()) { - return nullopt; - } - auto type = move(maybeType.value()); - std::vector classes; - auto shouldAutoCorrect = recurseOrType(ctx, type, classes); - if (core::isa_type(type) && shouldAutoCorrect) { - auto autocorrect = fmt::format("T.any({})", fmt::join(classes, ", ")); - e.setHeader("`{}` must wrap each individual class type, not the outer `{}`", "T.class_of", - "T.any"); - e.replaceWith("Distribute `T.class_of`", ctx.locAt(send.loc), "{}", autocorrect); - } else { - e.setHeader("`{}` needs a class or module as its argument", "T.class_of"); - } - } - return TypeSyntax::ResultType{core::Types::untypedUntracked(), core::Symbols::noClassOrModule()}; - } - auto maybeAliased = obj->symbol; - if (maybeAliased.isTypeAlias(ctx)) { - if (auto e = ctx.beginError(send.loc, core::errors::Resolver::InvalidTypeDeclaration)) { - e.setHeader("T.class_of can't be used with a T.type_alias"); - } - return TypeSyntax::ResultType{core::Types::untypedUntracked(), core::Symbols::noClassOrModule()}; - } - if (maybeAliased.isTypeMember()) { - if (auto e = ctx.beginError(send.loc, core::errors::Resolver::InvalidTypeDeclaration)) { - e.setHeader("T.class_of can't be used with a T.type_member"); - } - return TypeSyntax::ResultType{core::Types::untypedUntracked(), core::Symbols::noClassOrModule()}; - } - auto sym = maybeAliased.dealias(ctx); - if (sym.isStaticField(ctx)) { - if (auto e = ctx.beginError(send.loc, core::errors::Resolver::InvalidTypeDeclaration)) { - e.setHeader("T.class_of can't be used with a constant field"); - } - return TypeSyntax::ResultType{core::Types::untypedUntracked(), core::Symbols::noClassOrModule()}; - } - - auto singleton = sym.asClassOrModuleRef().data(ctx)->lookupSingletonClass(ctx); - if (!singleton.exists()) { - if (auto e = ctx.beginError(send.loc, core::errors::Resolver::InvalidTypeDeclaration)) { - e.setHeader("Unknown class"); - } - return TypeSyntax::ResultType{core::Types::untypedUntracked(), core::Symbols::noClassOrModule()}; + if (auto parseResult = parseTClassOf(ctx, send, sig, args)) { + return TypeSyntax::ResultType{ + parseResult.value().data(ctx)->externalType(), + core::Symbols::noClassOrModule(), + }; + } else { + return nullopt; } - return TypeSyntax::ResultType{singleton.data(ctx)->externalType(), core::Symbols::noClassOrModule()}; } case core::Names::untyped().rawId(): return TypeSyntax::ResultType{core::Types::untyped(ctx, args.untypedBlame), @@ -829,26 +884,62 @@ optional interpretTCombinator(core::Context ctx, const a ENFORCE(ctx.owner.isClassOrModule()); auto owner = ctx.owner.asClassOrModuleRef(); - if (!owner.data(ctx)->isSingletonClass(ctx)) { + auto ownerData = owner.data(ctx); + + auto maybeAttachedClass = ownerData->findMember(ctx, core::Names::Constants::AttachedClass()); + if (!maybeAttachedClass.exists()) { if (auto e = ctx.beginError(send.loc, core::errors::Resolver::InvalidTypeDeclaration)) { - e.setHeader("`{}` may only be used in a singleton class method context", "T.attached_class"); - e.addErrorNote("Current context is `{}`, which is an instance class not a singleton class", - owner.show(ctx)); + auto hasAttachedClass = core::Names::declareHasAttachedClass().show(ctx); + if (ownerData->isModule()) { + e.setHeader("`{}` must declare `{}` before module instance methods can use `{}`", + owner.show(ctx), hasAttachedClass, "T.attached_class"); + // TODO(jez) Autocorrect to insert `has_attached_class!` + } else if (ownerData->isSingletonClass(ctx)) { + // Combination of `isSingletonClass` and `` missing means + // this is the singleton class of a module. + ENFORCE(ownerData->attachedClass(ctx).data(ctx)->isModule()); + e.setHeader("`{}` cannot be used in singleton methods on modules, because modules cannot be " + "instantiated", + "T.attached_class"); + } else { + e.setHeader( + "`{}` may only be used in singleton methods on classes or instance methods on `{}` modules", + "T.attached_class", hasAttachedClass); + e.addErrorNote("Current context is `{}`, which is an instance class not a singleton class", + owner.show(ctx)); + } } return TypeSyntax::ResultType{core::Types::untypedUntracked(), core::Symbols::noClassOrModule()}; } else { - // All singletons have an AttachedClass type member, created by `singletonClass` - auto attachedClass = - owner.data(ctx)->findMember(ctx, core::Names::Constants::AttachedClass()).asTypeMemberRef(); - return TypeSyntax::ResultType{attachedClass.data(ctx)->resultType, core::Symbols::noClassOrModule()}; + ENFORCE( + // T::Class[...] support + owner == core::Symbols::Class() || + // isModule is never true for a singleton class, which implies this is a module instance method + ownerData->isModule() || + // In classes, can only use `T.attached_class` on singleton methods + (ownerData->isSingletonClass(ctx) && ownerData->attachedClass(ctx).data(ctx)->isClass())); + + const auto attachedClass = maybeAttachedClass.asTypeMemberRef(); + return TypeSyntax::ResultType{core::make_type(attachedClass), + core::Symbols::noClassOrModule()}; } } case core::Names::noreturn().rawId(): return TypeSyntax::ResultType{core::Types::bottom(), core::Symbols::noClassOrModule()}; + case core::Names::anything().rawId(): + return TypeSyntax::ResultType{core::Types::top(), core::Symbols::noClassOrModule()}; default: if (auto e = ctx.beginError(send.loc, core::errors::Resolver::InvalidTypeDeclaration)) { - e.setHeader("Unsupported method `{}`", "T." + send.fun.show(ctx)); + if (send.numPosArgs() > 0 && send.onlyPosArgs() && send.block() == nullptr && send.argsLoc().exists() && + ctx.locAt(send.funLoc).adjustLen(ctx, -1, 1).source(ctx) == ":") { + auto replacement = + fmt::format("T::{}[{}]", send.fun.show(ctx), ctx.locAt(send.argsLoc()).source(ctx).value()); + e.setHeader("Did you mean to use square brackets: `{}`", replacement); + e.replaceWith("Use square brackets for type args", ctx.locAt(send.loc), "{}", replacement); + } else { + e.setHeader("Unsupported method `{}`", "T." + send.fun.show(ctx)); + } } return TypeSyntax::ResultType{core::Types::untypedUntracked(), core::Symbols::noClassOrModule()}; } @@ -863,6 +954,26 @@ optional getResultTypeWithSelfTypeParams(core::Context ctx, const } } +TypeSyntax::ResultType reportUnknownTypeSyntaxError(core::Context ctx, const ast::Send &s, + TypeSyntax::ResultType &&result) { + if (auto e = ctx.beginError(s.loc, core::errors::Resolver::InvalidTypeDeclaration)) { + auto klass = sendLooksLikeBadTypeApplication(ctx, s); + if (klass.exists()) { + auto scope = + s.recv.isSelfReference() ? "" : fmt::format("{}::", ctx.locAt(s.recv.loc()).source(ctx).value()); + auto replacement = + fmt::format("{}{}[{}]", scope, s.fun.show(ctx), ctx.locAt(s.argsLoc()).source(ctx).value()); + e.setHeader("Did you mean to use square brackets: `{}`", replacement); + e.replaceWith("Use square brackets for type args", ctx.locAt(s.loc), "{}", replacement); + } else { + e.setHeader("Malformed type declaration. Unknown type syntax. Expected a ClassName or T."); + } + } + + result.type = core::Types::untypedUntracked(); + return move(result); +} + optional getResultTypeAndBindWithSelfTypeParamsImpl(core::Context ctx, const ast::ExpressionPtr &expr, const ParsedSig &sigBeingParsed, @@ -940,24 +1051,38 @@ optional getResultTypeAndBindWithSelfTypeParamsImpl(core // the T::Type generics internally have a typeArity of 0, so this allows us to check against them in the // same way that we check against types like `Array` if (klass.isBuiltinGenericForwarder() || klass.data(ctx)->typeArity(ctx) > 0) { - auto level = klass.isLegacyStdlibGeneric() ? core::errors::Resolver::GenericClassWithoutTypeArgsStdlib - : core::errors::Resolver::GenericClassWithoutTypeArgs; + // Class is not isLegacyStdlibGeneric (because its type members don't default to T.untyped), + // but we want to report this syntax error at `# typed: strict` like other stdlib classes. + auto level = klass.isLegacyStdlibGeneric() || klass == core::Symbols::Class() + ? core::errors::Resolver::GenericClassWithoutTypeArgsStdlib + : core::errors::Resolver::GenericClassWithoutTypeArgs; if (auto e = ctx.beginError(i.loc, level)) { e.setHeader("Malformed type declaration. Generic class without type arguments `{}`", klass.show(ctx)); - core::TypeErrorDiagnostics::insertUntypedTypeArguments(ctx, e, klass, ctx.locAt(i.loc)); + core::TypeErrorDiagnostics::insertTypeArguments(ctx, e, klass, ctx.locAt(i.loc)); } } if (klass == core::Symbols::StubModule()) { - // Though for normal types _and_ stub types `infer` should use `externalType`, - // using `externalType` for stub types here will lead to incorrect handling of global state hashing, - // where we won't see difference between two different unresolved stubs(or a mistyped stub). thus, - // while normally we would treat stubs as untyped, in `sig`s we treat them as proper types, so that - // we can correctly hash them. - auto unresolvedPath = i.fullUnresolvedPath(ctx); - ENFORCE(unresolvedPath.has_value()); - result.type = - core::make_type(unresolvedPath->first, move(unresolvedPath->second)); + if (maybeAliased != sym) { + // There is a bug here, where were don't take the fast path when fixing a + // constant resolution error in a class alias. + // We can't use our normal trick with fullUnresolvedPath though, because we only + // store that on the constant lit that fails to resolve. In this case, the + // constant lit itself resolves, but points at something that doesn't resolve, + // so there's no resolutionScopes on the constant that we can use to create an + // UnresolvedClassType. Just default to untyped. + result.type = core::Types::untypedUntracked(); + } else { + // Though for normal types _and_ stub types `infer` should use `externalType`, + // using `externalType` for stub types here will lead to incorrect handling of global state hashing, + // where we won't see difference between two different unresolved stubs(or a mistyped stub). thus, + // while normally we would treat stubs as untyped, in `sig`s we treat them as proper types, so that + // we can correctly hash them. + auto unresolvedPath = i.fullUnresolvedPath(ctx); + ENFORCE(unresolvedPath.has_value()); + result.type = + core::make_type(unresolvedPath->first, move(unresolvedPath->second)); + } } else { result.type = klass.data(ctx)->externalType(); } @@ -1095,36 +1220,31 @@ optional getResultTypeAndBindWithSelfTypeParamsImpl(core return result; } - auto *recvi = ast::cast_tree(s.recv); - if (recvi == nullptr) { - if (auto e = ctx.beginError(s.loc, core::errors::Resolver::InvalidTypeDeclaration)) { - e.setHeader("Malformed type declaration. Unknown type syntax. Expected a ClassName or T."); - } - result.type = core::Types::untypedUntracked(); - return result; - } - if (recvi->symbol == core::Symbols::T()) { - if (auto res = interpretTCombinator(ctx, s, sigBeingParsed, args)) { - return move(res.value()); - } else { - return nullopt; + core::SymbolRef appliedKlass; + if (auto *recvi = ast::cast_tree(s.recv)) { + if (recvi->symbol == core::Symbols::T()) { + if (auto res = interpretTCombinator(ctx, s, sigBeingParsed, args)) { + return move(res.value()); + } else { + return nullopt; + } } - } - if (recvi->symbol == core::Symbols::Magic() && s.fun == core::Names::callWithSplat()) { - if (auto e = ctx.beginError(recvi->loc, core::errors::Resolver::InvalidTypeDeclaration)) { - e.setHeader("Malformed type declaration: splats cannot be used in types"); + if (recvi->symbol == core::Symbols::Magic() && s.fun == core::Names::callWithSplat()) { + if (auto e = ctx.beginError(s.recv.loc(), core::errors::Resolver::InvalidTypeDeclaration)) { + e.setHeader("Malformed type declaration: splats cannot be used in types"); + } + result.type = core::Types::untypedUntracked(); + return result; } - result.type = core::Types::untypedUntracked(); - return result; + + appliedKlass = recvi->symbol; + } else { + return reportUnknownTypeSyntaxError(ctx, s, move(result)); } if (s.fun != core::Names::squareBrackets()) { - if (auto e = ctx.beginError(s.loc, core::errors::Resolver::InvalidTypeDeclaration)) { - e.setHeader("Malformed type declaration. Unknown type syntax. Expected a ClassName or T."); - } - result.type = core::Types::untypedUntracked(); - return result; + return reportUnknownTypeSyntaxError(ctx, s, move(result)); } InlinedVector holders; @@ -1171,22 +1291,22 @@ optional getResultTypeAndBindWithSelfTypeParamsImpl(core } core::SymbolRef corrected; - if (recvi->symbol.isClassOrModule()) { - corrected = recvi->symbol.asClassOrModuleRef().forwarderForBuiltinGeneric(); + if (appliedKlass.isClassOrModule()) { + corrected = appliedKlass.asClassOrModuleRef().forwarderForBuiltinGeneric(); } if (corrected.exists()) { if (auto e = ctx.beginError(s.loc, core::errors::Resolver::BadStdlibGeneric)) { e.setHeader("Use `{}`, not `{}` to declare a typed `{}`", corrected.show(ctx) + "[...]", - recvi->symbol.show(ctx) + "[...]", recvi->symbol.show(ctx)); + appliedKlass.show(ctx) + "[...]", appliedKlass.show(ctx)); e.addErrorNote("`{}` will raise at runtime because this generic was defined in the standard library", - recvi->symbol.show(ctx) + "[...]"); - e.replaceWith(fmt::format("Change `{}` to `{}`", recvi->symbol.show(ctx), corrected.show(ctx)), - ctx.locAt(recvi->loc), "{}", corrected.show(ctx)); + appliedKlass.show(ctx) + "[...]"); + e.replaceWith(fmt::format("Change `{}` to `{}`", appliedKlass.show(ctx), corrected.show(ctx)), + ctx.locAt(s.recv.loc()), "{}", corrected.show(ctx)); } result.type = core::Types::untypedUntracked(); return result; } else { - corrected = recvi->symbol; + corrected = appliedKlass; } corrected = corrected.dealias(ctx); @@ -1198,29 +1318,10 @@ optional getResultTypeAndBindWithSelfTypeParamsImpl(core return result; } - auto correctedSingleton = corrected.asClassOrModuleRef().data(ctx)->lookupSingletonClass(ctx); - ENFORCE_NO_TIMER(correctedSingleton.exists()); - auto ctype = core::make_type(correctedSingleton); - core::TypeAndOrigins ctypeAndOrigins{ctype, ctx.locAt(s.loc)}; - // In `dispatchArgs` this is ordinarily used to specify the origin tag for - // uninitialized variables. Inside of a signature we shouldn't need this: - auto originForUninitialized = core::Loc::none(); - core::CallLocs locs{ - ctx.file, s.loc, recvi->loc, s.loc.copyWithZeroLength(), argLocs, - }; - auto suppressErrors = false; - core::DispatchArgs dispatchArgs{core::Names::squareBrackets(), - locs, - s.numPosArgs(), - targs, - ctype, - ctypeAndOrigins, - ctype, - nullptr, - originForUninitialized, - s.flags.isPrivateOk, - suppressErrors}; - auto out = core::Types::dispatchCallWithoutBlock(ctx, ctype, dispatchArgs); + auto genericClass = corrected.asClassOrModuleRef(); + ENFORCE_NO_TIMER(genericClass.exists()); + core::CallLocs locs{ctx.file, s.loc, s.recv.loc(), s.funLoc, argLocs}; + auto out = core::Types::applyTypeArguments(ctx, locs, s.numPosArgs(), targs, genericClass); if (out.isUntyped()) { // Using a generic untyped type here will lead to incorrect handling of global state hashing, @@ -1232,7 +1333,7 @@ optional getResultTypeAndBindWithSelfTypeParamsImpl(core for (auto &targ : targs) { targPtrs.push_back(targ->type); } - result.type = core::make_type(correctedSingleton, move(targPtrs)); + result.type = core::make_type(genericClass, move(targPtrs)); return result; } if (auto *mt = core::cast_type(out)) { diff --git a/resolver/type_syntax/type_syntax.h b/resolver/type_syntax/type_syntax.h index 3de63a9447..69708a9e65 100644 --- a/resolver/type_syntax/type_syntax.h +++ b/resolver/type_syntax/type_syntax.h @@ -19,6 +19,7 @@ struct ParsedSig { core::ClassOrModuleRef bind; std::vector argTypes; core::TypePtr returns; + core::Loc returnsLoc; struct TypeArgSpec { core::Loc loc; diff --git a/rewriter/ClassNew.cc b/rewriter/ClassNew.cc index 5f960c3233..bcef28100b 100644 --- a/rewriter/ClassNew.cc +++ b/rewriter/ClassNew.cc @@ -125,7 +125,10 @@ bool ClassNew::run(core::MutableContext ctx, ast::Send *send) { ast::ExpressionPtr type; if (argc == 0) { - type = ast::MK::Constant(send->loc, core::Symbols::Class()); + auto zeroLoc = send->loc.copyWithZeroLength(); + type = + ast::MK::Send1(send->loc, ast::MK::Constant(send->recv.loc(), core::Symbols::T_Class()), + core::Names::squareBrackets(), zeroLoc, ast::MK::Constant(zeroLoc, core::Symbols::Object())); } else { auto target = send->getPosArg(0).deepCopy(); type = ast::MK::ClassOf(send->loc, std::move(target)); diff --git a/rewriter/Command.cc b/rewriter/Command.cc index 05db336cc1..a3298fbfb2 100644 --- a/rewriter/Command.cc +++ b/rewriter/Command.cc @@ -85,7 +85,8 @@ void Command::run(core::MutableContext ctx, ast::ClassDef *klass) { flags.isSelfMethod = true; flags.discardDef = true; auto selfCall = ast::MK::SyntheticMethod(call->loc, call->loc, core::LocOffsets::none(), call->name, - std::move(newArgs), ast::MK::UntypedNil(call->loc), flags); + std::move(newArgs), + ast::MK::RaiseTypedUnimplemented(call->declLoc), flags); klass->rhs.insert(klass->rhs.begin() + i + 1, sig->deepCopy()); klass->rhs.insert(klass->rhs.begin() + i + 2, std::move(selfCall)); diff --git a/rewriter/ConstantAssumeType.cc b/rewriter/ConstantAssumeType.cc new file mode 100644 index 0000000000..38014dbc0c --- /dev/null +++ b/rewriter/ConstantAssumeType.cc @@ -0,0 +1,43 @@ +#include "rewriter/ConstantAssumeType.h" +#include "ast/Helpers.h" +#include "ast/ast.h" +#include "core/Names.h" +#include "core/core.h" + +using namespace std; + +namespace sorbet::rewriter { + +void ConstantAssumeType::run(core::MutableContext ctx, ast::Assign *asgn) { + if (ctx.state.runningUnderAutogen) { + return; + } + + if (ctx.file.data(ctx).strictLevel <= core::StrictLevel::False) { + // Only do this transformation in files that are typed: true or higher, so that we know that + // if this assumption about the type is wrong, that it will get checked down the line. + return; + } + auto lhs = ast::cast_tree(asgn->lhs); + if (lhs == nullptr) { + return; + } + + auto send = ast::cast_tree(asgn->rhs); + if (send == nullptr) { + return; + } + + if (send->fun != core::Names::new_()) { + return; + } + + if (!(ast::isa_tree(send->recv) || ast::isa_tree(send->recv))) { + return; + } + + auto type = send->recv.deepCopy(); + asgn->rhs = ast::MK::AssumeType(asgn->rhs.loc(), move(asgn->rhs), move(type)); +} + +}; // namespace sorbet::rewriter diff --git a/rewriter/ConstantAssumeType.h b/rewriter/ConstantAssumeType.h new file mode 100644 index 0000000000..5b69064439 --- /dev/null +++ b/rewriter/ConstantAssumeType.h @@ -0,0 +1,29 @@ +#ifndef SORBET_REWRITER_CONSTANT_T_LET_H +#define SORBET_REWRITER_CONSTANT_T_LET_H +#include "ast/ast.h" + +namespace sorbet::rewriter { + +/** + * Rewrites things like this: + * + * X = A::B::C.new + * + * to this: + * + * X = T.let(A::B::C.new, A::B::C) + * + * but only in `# typed: true` files, so that we can be sure that the type annotation will be + * checked for correctness. + * + */ +class ConstantAssumeType final { +public: + static void run(core::MutableContext ctx, ast::Assign *asgn); + + ConstantAssumeType() = delete; +}; + +} // namespace sorbet::rewriter + +#endif diff --git a/rewriter/Data.cc b/rewriter/Data.cc new file mode 100644 index 0000000000..78424dbb43 --- /dev/null +++ b/rewriter/Data.cc @@ -0,0 +1,135 @@ +#include "rewriter/Data.h" +#include "absl/strings/match.h" +#include "ast/Helpers.h" +#include "ast/ast.h" +#include "core/Context.h" +#include "core/Names.h" +#include "core/core.h" +#include "core/errors/rewriter.h" +#include "rewriter/Util.h" +#include "rewriter/rewriter.h" + +using namespace std; + +namespace sorbet::rewriter { + +namespace { + +bool isMissingInitialize(const core::GlobalState &gs, const ast::Send *send) { + if (!send->hasBlock()) { + return true; + } + + auto block = send->block(); + + if (auto *insSeq = ast::cast_tree(block->body)) { + auto methodDef = ast::cast_tree(insSeq->expr); + + if (methodDef && methodDef->name == core::Names::initialize()) { + return false; + } + + for (auto &&stat : insSeq->stats) { + methodDef = ast::cast_tree(stat); + + if (methodDef && methodDef->name == core::Names::initialize()) { + return false; + } + } + } + + return true; +} + +} // namespace + +vector Data::run(core::MutableContext ctx, ast::Assign *asgn) { + vector empty; + + if (ctx.state.runningUnderAutogen) { + return empty; + } + + auto lhs = ast::cast_tree(asgn->lhs); + if (lhs == nullptr) { + return empty; + } + + auto send = ast::cast_tree(asgn->rhs); + if (send == nullptr) { + return empty; + } + + auto recv = ast::cast_tree(send->recv); + if (recv == nullptr) { + return empty; + } + + if (!ast::MK::isRootScope(recv->scope) || recv->cnst != core::Names::Constants::Data() || + send->fun != core::Names::define() || !send->hasPosArgs()) { + return empty; + } + + auto loc = asgn->loc; + + ast::MethodDef::ARGS_store newArgs; + ast::Send::ARGS_store sigArgs; + ast::ClassDef::RHS_store body; + + for (int i = 0; i < send->numPosArgs(); i++) { + auto *sym = ast::cast_tree(send->getPosArg(i)); + if (!sym || (!sym->isSymbol() && !sym->isString())) { + return empty; + } + core::NameRef name = sym->asSymbol(); + auto symLoc = sym->loc; + auto strname = name.shortName(ctx); + if (!strname.empty() && strname.back() == '=') { + if (auto e = ctx.beginError(symLoc, core::errors::Rewriter::InvalidStructMember)) { + e.setHeader("Data member `{}` cannot end with an equal", strname); + } + } + + if (symLoc.exists() && ctx.locAt(symLoc).adjustLen(ctx, 0, 1).source(ctx) == ":") { + symLoc = ctx.locAt(symLoc).adjust(ctx, 1, 0).offsets(); + } + + sigArgs.emplace_back(ast::MK::Symbol(symLoc, name)); + sigArgs.emplace_back(ast::MK::Constant(symLoc, core::Symbols::BasicObject())); + + auto argName = ast::MK::Local(symLoc, name); + newArgs.emplace_back(ast::MK::OptionalArg(symLoc, move(argName), ast::MK::Nil(symLoc))); + + body.emplace_back(ast::MK::SyntheticMethod0(symLoc, symLoc, symLoc, name, ast::MK::RaiseUnimplemented(loc))); + } + + if (isMissingInitialize(ctx, send)) { + body.emplace_back(ast::MK::SigVoid(loc, std::move(sigArgs))); + body.emplace_back(ast::MK::SyntheticMethod(loc, loc, loc, core::Names::initialize(), std::move(newArgs), + ast::MK::RaiseUnimplemented(loc))); + } + + if (auto *block = send->block()) { + // Steal the trees, because the run is going to remove the original send node from the tree anyway. + if (auto *insSeq = ast::cast_tree(block->body)) { + for (auto &&stat : insSeq->stats) { + body.emplace_back(move(stat)); + } + body.emplace_back(move(insSeq->expr)); + } else { + body.emplace_back(move(block->body)); + } + + // NOTE: the code in this block _STEALS_ trees. No _return empty_'s should go after it + } + + ast::ClassDef::ANCESTORS_store ancestors; + ancestors.emplace_back(ast::MK::UnresolvedConstant(loc, ast::MK::Constant(loc, core::Symbols::root()), + core::Names::Constants::Data())); + + vector stats; + stats.emplace_back(ast::MK::Class(loc, loc, std::move(asgn->lhs), std::move(ancestors), std::move(body))); + return stats; +} + +}; // namespace sorbet::rewriter diff --git a/rewriter/Data.h b/rewriter/Data.h new file mode 100644 index 0000000000..45a17e8801 --- /dev/null +++ b/rewriter/Data.h @@ -0,0 +1,32 @@ +#ifndef SORBET_REWRITER_DATA_H +#define SORBET_REWRITER_DATA_H +#include "ast/ast.h" + +namespace sorbet::rewriter { + +/** + * This class desugars things of the form + * + * A = Data.define(:foo, :bar) + * + * into + * + * class A < Data + * def foo; end + * def bar; end + * sig {params(foo: BasicObject, bar: BasicObject).returns(A)} + * def self.new(foo=nil, bar=nil) + * T.cast(nil, A) + * end + * end + */ +class Data final { +public: + static std::vector run(core::MutableContext ctx, ast::Assign *asgn); + + Data() = delete; +}; + +} // namespace sorbet::rewriter + +#endif diff --git a/rewriter/HasAttachedClass.cc b/rewriter/HasAttachedClass.cc new file mode 100644 index 0000000000..3070e03c5c --- /dev/null +++ b/rewriter/HasAttachedClass.cc @@ -0,0 +1,59 @@ +#include "rewriter/HasAttachedClass.h" +#include "ast/Helpers.h" +#include "core/errors/rewriter.h" + +using namespace std; +namespace sorbet::rewriter { + +vector HasAttachedClass::run(core::MutableContext ctx, bool isClass, ast::Send *send) { + vector empty; + + if (send->fun != core::Names::declareHasAttachedClass() || !send->recv.isSelfReference()) { + return empty; + } + + if (send->numPosArgs() > 0) { + const auto &arg0 = send->posArgs()[0]; + if (const auto *lit = ast::cast_tree(arg0)) { + if (lit->isSymbol() && lit->asSymbol() == core::Names::contravariant()) { + if (auto e = ctx.beginError(arg0.loc(), core::errors::Rewriter::ContravariantHasAttachedClass)) { + e.setHeader("`{}` cannot be declared `{}`, only invariant or `{}`", + core::Names::declareHasAttachedClass().show(ctx), ":in", ":out"); + e.replaceWith("Convert to covariant", ctx.locAt(arg0.loc()), "{}", ":out"); + } + } + } + } + + auto zeroLoc = send->loc.copyWithZeroLength(); + vector result; + auto lhs = ast::MK::UnresolvedConstant(zeroLoc, ast::MK::EmptyTree(), core::Names::Constants::AttachedClass()); + + // `has_attached_class!` and `type_member` have the same arity, so let's just dup the Send and + // change the fun so that everything gets passed through. The rest of the pipeline will validate + // that any args passed here are correct or not. + // + // (If there are other popular Ruby DSLs using `has_attached_class!`, we can consider being more + // conservative in this rewrite by only applying it if the args look correct, but being + // over-eager like this makes it more obvious when the user did something like make a typo + // which would have prevented the rewriter from firing.) + auto rhs = send->deepCopy(); + auto &rhsSend = ast::cast_tree_nonnull(rhs); + rhsSend.fun = core::Names::typeMember(); + + // Need the call to `type_member` in an `Assign` node specicially, because all the downstream + // logic in namer and resolver expects type members to be declared via `Assign` nodes. + result.emplace_back(ast::MK::Assign(send->loc, move(lhs), move(rhs))); + + // Keep the call to `has_attached_class!` in the tree so that it still gets type checked. + // If this proves to be a problem, we can either: change the `Assign` to look like + // + // = has_attached_class!(...) {...} + // + // or we can just drop the call to `has_attached_class!`, or we can try to do some loc munging to + // fix any problems that arise. + result.emplace_back(send->deepCopy()); + return result; +} + +} // namespace sorbet::rewriter diff --git a/rewriter/HasAttachedClass.h b/rewriter/HasAttachedClass.h new file mode 100644 index 0000000000..2b415eba02 --- /dev/null +++ b/rewriter/HasAttachedClass.h @@ -0,0 +1,31 @@ +#ifndef SORBET_REWRITER_HAS_ATTACHED_CLASS_H +#define SORBET_REWRITER_HAS_ATTACHED_CLASS_H +#include "ast/ast.h" + +namespace sorbet::rewriter { + +/** + * Converts things like this + * + * module A + * has_attached_class! + * end + * + * into this: + * + * module A + * = type_member + * has_attached_class! + * end + */ + +class HasAttachedClass final { +public: + static std::vector run(core::MutableContext ctx, bool isClass, ast::Send *send); + + HasAttachedClass() = delete; +}; + +} // namespace sorbet::rewriter + +#endif diff --git a/rewriter/Initializer.cc b/rewriter/Initializer.cc index 646db7c133..7e451a2436 100644 --- a/rewriter/Initializer.cc +++ b/rewriter/Initializer.cc @@ -29,6 +29,30 @@ bool isCopyableType(const ast::ExpressionPtr &typeExpr) { return true; } +// Not checking for being T.proc, it's expected to be checked +void maybeRemoveBind(core::Context ctx, ast::Send *send) { + auto lastSend = send; + while (send != nullptr) { + if (send->fun == core::Names::bind()) { + // `bind` is the last send in the chain of sends. + // This is incorrect syntax which is covered by the resolver. + // Safe to ignore. + if (send == lastSend) { + return; + } + auto recvSend = ast::cast_tree(send->recv); + if (recvSend != nullptr) { + lastSend->recv = move(send->recv); + return; + } + // We handle only one `.bind` call in the whole chain, + // no need to traverse the rest of the tree. + return; + } + send = ast::cast_tree(send->recv); + } +} + // if expr is of the form `@var = local`, and `local` is typed, then replace it with with `@var = T.let(local, // type_of_local)` void maybeAddLet(core::MutableContext ctx, ast::ExpressionPtr &expr, @@ -77,6 +101,11 @@ void maybeAddLet(core::MutableContext ctx, ast::ExpressionPtr &expr, } } + auto send = ast::cast_tree(type); + if (send != nullptr) { + maybeRemoveBind(ctx, send); + } + auto newLet = ast::MK::Let(loc, move(assn->rhs), move(type)); assn->rhs = move(newLet); } @@ -95,56 +124,62 @@ const ast::Send *findParams(const ast::Send *send) { // this function checks if the signature of the initialize method is using returns(Something) // instead of void and provides an auto-correct option void checkSigReturnType(core::MutableContext ctx, const ast::Send *send) { - auto originalSend = send->deepCopy(); - string statementAfterReturns = ""; + auto originalSendLoc = send->loc; + core::NameRef funAfterReturns; // try to find the invocation to returns. Save the source code of the invocation // immediately after returns() so that we can have the exact length it occupies while (send && send->fun != core::Names::returns()) { - statementAfterReturns = send->fun.toString(ctx); + funAfterReturns = send->fun; send = ast::cast_tree(send->recv); } // if the returns exists, then add an error an suggest the auto-correct. We need to account for things // being invoked after returns too. E.g.: sig { returns(Foo).on_failure(...) } - if (send != nullptr) { - if (auto e = ctx.beginError(originalSend.loc(), core::errors::Rewriter::InitializeReturnType)) { - e.setHeader("The {} method should always return {}", "initialize", "void"); - - auto loc = core::Loc(ctx.file, originalSend.loc()); - auto original = string(loc.source(ctx).value()); - unsigned long returnsStart = original.find("returns"); - unsigned long returnsLength, afterReturnsPosition; - string replacement; - - // If there are no statements after returns(), we can use the length of the block to find the length - // we need to replace. If there are statements after it, we need to find the exact length using the next - // statement and remember to add a dot or else it will produce invalid code - returnsLength = original.length() - returnsStart + 1; - - if (statementAfterReturns.empty()) { - replacement = original.replace(returnsStart, returnsLength, "void"); + if (send == nullptr) { + return; + } + + if (auto e = ctx.beginError(originalSendLoc, core::errors::Rewriter::InitializeReturnType)) { + e.setHeader("The {} method should always return {}", "initialize", "void"); + + auto loc = core::Loc(ctx.file, originalSendLoc); + auto original = string(loc.source(ctx).value()); + unsigned long returnsStart = original.find("returns"); + unsigned long returnsLength, afterReturnsPosition; + string replacement; + string statementAfterReturns = ""; + if (funAfterReturns.exists()) { + statementAfterReturns = funAfterReturns.toString(ctx); + } + + // If there are no statements after returns(), we can use the length of the block to find the length + // we need to replace. If there are statements after it, we need to find the exact length using the next + // statement and remember to add a dot or else it will produce invalid code + returnsLength = original.length() - returnsStart + 1; + + if (statementAfterReturns.empty()) { + replacement = original.replace(returnsStart, returnsLength, "void"); + } else { + afterReturnsPosition = original.find(statementAfterReturns, returnsStart); + + // If there is a line break between returns() and the next statement, change the returns() entry and + // re-join the string with the line breaks. Otherwise, everything is on the same line and we can replace + // directly without worrying about line breaks + vector lines = absl::StrSplit(original.substr(returnsStart, afterReturnsPosition), "\n"); + + if (lines.size() > 1) { + lines[0] = "void"; + replacement = original.replace(returnsStart, returnsLength, + fmt::format("{}", fmt::join(lines.begin(), lines.end(), "\n"))); } else { - afterReturnsPosition = original.find(statementAfterReturns, returnsStart); - - // If there is a line break between returns() and the next statement, change the returns() entry and - // re-join the string with the line breaks. Otherwise, everything is on the same line and we can replace - // directly without worrying about line breaks - vector lines = absl::StrSplit(original.substr(returnsStart, afterReturnsPosition), "\n"); - - if (lines.size() > 1) { - lines[0] = "void"; - replacement = original.replace(returnsStart, returnsLength, - fmt::format("{}", fmt::join(lines.begin(), lines.end(), "\n"))); - } else { - returnsLength = original.find(statementAfterReturns, returnsStart) - returnsStart; - replacement = original.replace(returnsStart, returnsLength, "void."); - } + returnsLength = original.find(statementAfterReturns, returnsStart) - returnsStart; + replacement = original.replace(returnsStart, returnsLength, "void."); } - - e.addAutocorrect(core::AutocorrectSuggestion{fmt::format("Replace `{}` with `{}`", original, replacement), - {core::AutocorrectSuggestion::Edit{loc, replacement}}}); } + + e.addAutocorrect(core::AutocorrectSuggestion{fmt::format("Replace `{}` with `{}`", original, replacement), + {core::AutocorrectSuggestion::Edit{loc, replacement}}}); } } diff --git a/rewriter/Mattr.cc b/rewriter/Mattr.cc index ea43f3f55e..d48480a589 100644 --- a/rewriter/Mattr.cc +++ b/rewriter/Mattr.cc @@ -41,7 +41,8 @@ vector Mattr::run(core::MutableContext ctx, const ast::Send doReaders = true; } else if (send->fun == core::Names::mattrWriter() || send->fun == core::Names::cattrWriter()) { doWriters = true; - } else if (send->fun == core::Names::mattrAccessor() || send->fun == core::Names::cattrAccessor()) { + } else if (send->fun == core::Names::mattrAccessor() || send->fun == core::Names::cattrAccessor() || + send->fun == core::Names::threadMattrAccessor() || send->fun == core::Names::threadCattrAccessor()) { doReaders = true; doWriters = true; } else if (classDefKind == ast::ClassDef::Kind::Class && send->fun == core::Names::classAttribute()) { diff --git a/rewriter/Prop.cc b/rewriter/Prop.cc index fd2d5fcd0c..e26ace5857 100644 --- a/rewriter/Prop.cc +++ b/rewriter/Prop.cc @@ -54,32 +54,17 @@ bool isTImmutableStruct(const ast::ExpressionPtr &expr) { return struct_ != nullptr && struct_->cnst == core::Names::Constants::ImmutableStruct() && isT(struct_->scope); } -bool isChalkODMDocument(const ast::ExpressionPtr &expr) { - auto *document = ast::cast_tree(expr); - if (document == nullptr || document->cnst != core::Names::Constants::Document()) { - return false; - } - auto *odm = ast::cast_tree(document->scope); - if (odm == nullptr || odm->cnst != core::Names::Constants::ODM()) { - return false; - } - auto *chalk = ast::cast_tree(odm->scope); - return chalk != nullptr && chalk->cnst == core::Names::Constants::Chalk() && ast::MK::isRootScope(chalk->scope); -} - enum class SyntacticSuperClass { Unknown, TStruct, TInexactStruct, - ChalkODMDocument, TImmutableStruct, }; -bool knownNonModel(SyntacticSuperClass syntacticSuperClass) { +bool wantSimpleIVarGet(SyntacticSuperClass syntacticSuperClass) { switch (syntacticSuperClass) { case SyntacticSuperClass::TStruct: case SyntacticSuperClass::TInexactStruct: - case SyntacticSuperClass::ChalkODMDocument: case SyntacticSuperClass::TImmutableStruct: return true; case SyntacticSuperClass::Unknown: @@ -93,7 +78,6 @@ bool knownNonDocument(SyntacticSuperClass syntacticSuperClass) { case SyntacticSuperClass::TInexactStruct: case SyntacticSuperClass::TImmutableStruct: return true; - case SyntacticSuperClass::ChalkODMDocument: case SyntacticSuperClass::Unknown: return false; } @@ -105,7 +89,6 @@ bool wantTypedInitialize(SyntacticSuperClass syntacticSuperClass) { case SyntacticSuperClass::TImmutableStruct: return true; case SyntacticSuperClass::TInexactStruct: - case SyntacticSuperClass::ChalkODMDocument: case SyntacticSuperClass::Unknown: return false; } @@ -167,11 +150,8 @@ optional parseProp(core::MutableContext ctx, const ast::Send *send) { ret.name = send->fun == core::Names::createdProp() ? core::Names::created() : core::Names::updated(); // 5 is the length of the _prop suffix ret.nameLoc = core::LocOffsets{send->loc.beginPos(), send->loc.endPos() - 5}; - auto chalk = ast::MK::UnresolvedConstant(send->loc, ast::MK::EmptyTree(), core::Names::Constants::Chalk()); - auto chalk_odm = ast::MK::UnresolvedConstant(send->loc, std::move(chalk), core::Names::Constants::ODM()); - ret.type = - ASTUtil::mkNilable(send->loc, ast::MK::UnresolvedConstant(send->loc, std::move(chalk_odm), - core::Names::Constants::DeprecatedNumeric())); + ret.type = ASTUtil::mkNilable(send->loc, ast::MK::UnresolvedConstant(send->loc, ast::MK::EmptyTree(), + core::Names::Constants::Numeric())); break; } case core::Names::merchantProp().rawId(): @@ -233,6 +213,11 @@ optional parseProp(core::MutableContext ctx, const ast::Send *send) { ENFORCE(ctx.locAt(sym->loc).exists()); ENFORCE(!ctx.locAt(sym->loc).source(ctx).value().empty() && ctx.locAt(sym->loc).source(ctx).value()[0] == ':'); ret.nameLoc = core::LocOffsets{sym->loc.beginPos() + 1, sym->loc.endPos()}; + const auto nameValue = ctx.locAt(ret.nameLoc).source(ctx).value(); + if ((nameValue.front() == '\'' && nameValue.back() == '\'') || + (nameValue.front() == '\"' && nameValue.back() == '\"')) { + ret.nameLoc = core::LocOffsets{ret.nameLoc.beginPos() + 1, ret.nameLoc.endPos() - 1}; + } } // ----- What's the prop's type? ----- @@ -294,7 +279,7 @@ optional parseProp(core::MutableContext ctx, const ast::Send *send) { } if (ASTUtil::hasTruthyHashValue(ctx, *rules, core::Names::factory())) { - ret.default_ = ast::MK::RaiseUnimplemented(ret.loc); + ret.default_ = ast::MK::RaiseTypedUnimplemented(ret.loc); } else if (ASTUtil::hasHashValue(ctx, *rules, core::Names::default_())) { auto [key, val] = ASTUtil::extractHashValue(ctx, *rules, core::Names::default_()); ret.default_ = std::move(val); @@ -380,13 +365,13 @@ vector processProp(core::MutableContext ctx, PropInfo &ret, computedByMethodNameLocZero, std::move(raiseUnimplemented)); auto assertTypeMatches = ast::MK::AssertType(computedByMethodNameLoc, std::move(sendComputedMethod), ASTUtil::dupType(getType)); - auto insSeq = ast::MK::InsSeq1(loc, std::move(assertTypeMatches), ast::MK::RaiseUnimplemented(loc)); + auto insSeq = ast::MK::InsSeq1(loc, std::move(assertTypeMatches), ast::MK::RaiseTypedUnimplemented(loc)); nodes.emplace_back(ASTUtil::mkGet(ctx, loc, name, nameLoc, std::move(insSeq))); } else if (propContext.needsRealPropBodies && propContext.classDefKind == ast::ClassDef::Kind::Module) { // Not all modules include Kernel, can't make an initialize, etc. so we're punting on props in modules rn. - nodes.emplace_back(ASTUtil::mkGet(ctx, loc, name, nameLoc, ast::MK::RaiseUnimplemented(loc))); + nodes.emplace_back(ASTUtil::mkGet(ctx, loc, name, nameLoc, ast::MK::RaiseTypedUnimplemented(loc))); } else if (ret.ifunset == nullptr) { - if (knownNonModel(propContext.syntacticSuperClass)) { + if (wantSimpleIVarGet(propContext.syntacticSuperClass)) { ast::MethodDef::Flags flags; flags.isAttrReader = true; if (wantTypedInitialize(propContext.syntacticSuperClass)) { @@ -403,8 +388,8 @@ vector processProp(core::MutableContext ctx, PropInfo &ret, flags.genericPropGetter = true; // Models have a custom decorator, which means we have to forward the prop get to it. - // If this is actually a T::InexactStruct or Chalk::ODM::Document sub-sub-class, this implementation is - // correct but does extra work. + // If this is actually a T::InexactStruct or Chalk::ODM::Base::Document sub-sub-class, + // this implementation is correct but does extra work. auto arg2 = ast::MK::Local(loc, core::Names::arg2()); @@ -420,10 +405,10 @@ vector processProp(core::MutableContext ctx, PropInfo &ret, auto insSeq = ast::MK::InsSeq1(loc, std::move(assign), std::move(propGetLogic)); nodes.emplace_back(ASTUtil::mkGet(ctx, loc, name, nameLoc, std::move(insSeq), flags)); } else { - nodes.emplace_back(ASTUtil::mkGet(ctx, loc, name, nameLoc, ast::MK::RaiseUnimplemented(loc))); + nodes.emplace_back(ASTUtil::mkGet(ctx, loc, name, nameLoc, ast::MK::RaiseTypedUnimplemented(loc))); } } else { - nodes.emplace_back(ASTUtil::mkGet(ctx, loc, name, nameLoc, ast::MK::RaiseUnimplemented(loc))); + nodes.emplace_back(ASTUtil::mkGet(ctx, loc, name, nameLoc, ast::MK::RaiseTypedUnimplemented(loc))); } core::NameRef setName = name.addEq(ctx); @@ -438,7 +423,7 @@ vector processProp(core::MutableContext ctx, PropInfo &ret, if (propContext.needsRealPropBodies && propContext.classDefKind == ast::ClassDef::Kind::Module) { // Not all modules include Kernel, can't make an initialize, etc. so we're punting on props in modules rn. - nodes.emplace_back(ASTUtil::mkSet(ctx, loc, setName, nameLoc, ast::MK::RaiseUnimplemented(loc))); + nodes.emplace_back(ASTUtil::mkSet(ctx, loc, setName, nameLoc, ast::MK::RaiseTypedUnimplemented(loc))); } else if (ret.enum_ == nullptr) { if (knownNonDocument(propContext.syntacticSuperClass)) { if (wantTypedInitialize(propContext.syntacticSuperClass)) { @@ -465,10 +450,10 @@ vector processProp(core::MutableContext ctx, PropInfo &ret, auto insSeq = ast::MK::InsSeq1(loc, std::move(propFreezeLogic), std::move(ivarSet)); nodes.emplace_back(ASTUtil::mkSet(ctx, loc, setName, nameLoc, std::move(insSeq))); } else { - nodes.emplace_back(ASTUtil::mkSet(ctx, loc, setName, nameLoc, ast::MK::RaiseUnimplemented(loc))); + nodes.emplace_back(ASTUtil::mkSet(ctx, loc, setName, nameLoc, ast::MK::RaiseTypedUnimplemented(loc))); } } else { - nodes.emplace_back(ASTUtil::mkSet(ctx, loc, setName, nameLoc, ast::MK::RaiseUnimplemented(loc))); + nodes.emplace_back(ASTUtil::mkSet(ctx, loc, setName, nameLoc, ast::MK::RaiseTypedUnimplemented(loc))); } } @@ -499,7 +484,7 @@ vector processProp(core::MutableContext ctx, PropInfo &ret, ast::MethodDef::Flags fkFlags; fkFlags.discardDef = true; auto fkMethodDef = ast::MK::SyntheticMethod1(loc, loc, nameLoc, fkMethod, std::move(arg), - ast::MK::RaiseUnimplemented(loc), fkFlags); + ast::MK::RaiseTypedUnimplemented(loc), fkFlags); nodes.emplace_back(std::move(fkMethodDef)); // sig {params(opts: T.untyped).returns($foreign)} @@ -515,7 +500,7 @@ vector processProp(core::MutableContext ctx, PropInfo &ret, ast::MethodDef::Flags fkBangFlags; fkBangFlags.discardDef = true; auto fkMethodDefBang = ast::MK::SyntheticMethod1(loc, loc, nameLoc, fkMethodBang, std::move(arg2), - ast::MK::RaiseUnimplemented(loc), fkBangFlags); + ast::MK::RaiseTypedUnimplemented(loc), fkBangFlags); nodes.emplace_back(std::move(fkMethodDefBang)); } @@ -618,8 +603,6 @@ void Prop::run(core::MutableContext ctx, ast::ClassDef *klass) { syntacticSuperClass = SyntacticSuperClass::TInexactStruct; } else if (isTImmutableStruct(superClass)) { syntacticSuperClass = SyntacticSuperClass::TImmutableStruct; - } else if (isChalkODMDocument(superClass)) { - syntacticSuperClass = SyntacticSuperClass::ChalkODMDocument; } } // The compiler is going to turn the bodies of rewritten prop methods into actual diff --git a/rewriter/Prop.h b/rewriter/Prop.h index 2d03e56cff..d78506c5bf 100644 --- a/rewriter/Prop.h +++ b/rewriter/Prop.h @@ -16,8 +16,8 @@ namespace sorbet::rewriter { * sig {params(arg0: Type).returns(Type)} * def foo=(arg0); ...; end * - * We try to implement a simple approximation of the functionality that Chalk::ODM::Document.prop has. Any deviation - * from the expected shape stops the desugaring. + * We try to implement a simple approximation of the functionality that Chalk::ODM::Base::Document.prop has. + * Any deviation from the expected shape stops the desugaring. * * Most other `run`s return just nodes, but we also want to keep track of the prop information so that at the end * of the Rewriter pass on the classDef, we can construct an `initialize` method with good static types. diff --git a/rewriter/SelfNew.cc b/rewriter/SelfNew.cc deleted file mode 100644 index 0a83f4db27..0000000000 --- a/rewriter/SelfNew.cc +++ /dev/null @@ -1,80 +0,0 @@ -#include "rewriter/SelfNew.h" -#include "ast/Helpers.h" -#include "ast/ast.h" -#include "core/core.h" - -namespace sorbet::rewriter { - -namespace { -ast::ExpressionPtr convertSelfNew(core::MutableContext ctx, ast::Send *send) { - ast::Send::ARGS_store args; - - args.emplace_back(std::move(send->recv)); - - for (auto &arg : send->nonBlockArgs()) { - args.emplace_back(std::move(arg)); - } - - if (auto *block = send->rawBlock()) { - args.emplace_back(std::move(*block)); - } - - return ast::MK::SelfNew(send->loc, send->funLoc, send->numPosArgs() + 1, std::move(args), send->flags); -} - -bool isSelfNewCallWithSplat(core::MutableContext ctx, ast::Send *send) { - if (send->fun != core::Names::callWithSplat()) { - return false; - } - - if (!send->getPosArg(0).isSelfReference()) { - return false; - } - - auto *lit = ast::cast_tree(send->getPosArg(1)); - if (lit == nullptr) { - return false; - } - - if (!core::isa_type(lit->value)) { - return false; - } - - const auto &litType = core::cast_type_nonnull(lit->value); - if (litType.literalKind != core::NamedLiteralType::LiteralTypeKind::Symbol) { - return false; - } - - if (litType.asName() != core::Names::new_()) { - return false; - } - - return true; -} - -ast::ExpressionPtr convertSelfNewCallWithSplat(core::MutableContext ctx, ast::Send *send) { - auto magic = ast::MK::Magic(send->loc); - auto &arg0 = send->getPosArg(0); - arg0 = std::move(magic); - - auto &arg1 = send->getPosArg(1); - arg1 = ast::MK::Symbol(send->loc, core::Names::selfNew()); - - // The original expression is now properly mutated, but we need to return an expression not a Send. - return send->withNewBody(send->loc, std::move(send->recv), send->fun); -} -} // namespace - -ast::ExpressionPtr SelfNew::run(core::MutableContext ctx, ast::Send *send) { - if (send->fun == core::Names::new_() && send->recv.isSelfReference()) { - return convertSelfNew(ctx, send); - } - - if (isSelfNewCallWithSplat(ctx, send)) { - return convertSelfNewCallWithSplat(ctx, send); - } - - return nullptr; -} - -} // namespace sorbet::rewriter diff --git a/rewriter/SelfNew.h b/rewriter/SelfNew.h deleted file mode 100644 index bd8c9118d5..0000000000 --- a/rewriter/SelfNew.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef SORBET_REWRITER_SELF_NEW_H -#define SORBET_REWRITER_SELF_NEW_H - -#include "ast/ast.h" - -namespace sorbet::rewriter { - -/** - * This class desugars `self.new` into a call to a special intrinsic: - * - * self.new(arg1, ..., argN) - * - * => - * - * Magic.(self, arg1, ..., argN) - * - * The `Magic.` intrinsic will construct a value of type of the - * enclosing class, and return its type as `C::` - */ -class SelfNew final { -public: - static ast::ExpressionPtr run(core::MutableContext ctx, ast::Send *send); - SelfNew() = delete; -}; - -} // namespace sorbet::rewriter - -#endif /* SORBET_REWRITER_SELF_NEW_H */ diff --git a/rewriter/SigRewriter.cc b/rewriter/SigRewriter.cc index 93b31151b4..d7a3260dee 100644 --- a/rewriter/SigRewriter.cc +++ b/rewriter/SigRewriter.cc @@ -51,7 +51,9 @@ bool SigRewriter::run(core::MutableContext &ctx, ast::Send *send) { // Keep track of old receiver at this point so that we can report whether a method called // sig with the right arity even existed at this point. auto oldRecv = std::move(send->recv); - send->recv = ast::MK::Constant(send->loc, core::Symbols::Sorbet_Private_Static()); + // Even though the receiver is now Sorbet::Private::Static, the loc of the old receiver + // is more relevant. + send->recv = ast::MK::Constant(oldRecv.loc(), core::Symbols::Sorbet_Private_Static()); send->insertPosArg(0, std::move(oldRecv)); return true; } diff --git a/rewriter/Singleton.cc b/rewriter/Singleton.cc deleted file mode 100644 index c3a526e6fa..0000000000 --- a/rewriter/Singleton.cc +++ /dev/null @@ -1,100 +0,0 @@ -#include "absl/algorithm/container.h" -#include // for std::move - -#include "ast/Helpers.h" -#include "core/GlobalState.h" -#include "rewriter/Singleton.h" - -using namespace std; - -namespace sorbet::rewriter { - -namespace { - -bool isFinal(const ast::ExpressionPtr &stmt) { - auto *send = ast::cast_tree(stmt); - if (send == nullptr) { - return false; - } - - if (!send->recv.isSelfReference()) { - return false; - } - - if (send->hasPosArgs() || send->hasKwArgs()) { - return false; - } - - if (send->fun != core::Names::declareFinal()) { - return false; - } - - return true; -} - -bool isIncludeSingleton(const ast::ExpressionPtr &stmt) { - const auto *send = ast::cast_tree(stmt); - if (send == nullptr) { - return false; - } - - if (!send->recv.isSelfReference()) { - return false; - } - - if (send->fun != core::Names::include()) { - return false; - } - - if (send->numPosArgs() != 1 || send->hasKwArgs()) { - return false; - } - - auto *sym = ast::cast_tree(send->getPosArg(0)); - if (sym == nullptr || sym->cnst != core::Names::Constants::Singleton()) { - return false; - } - - return true; -} - -} // namespace - -void Singleton::run(core::MutableContext ctx, ast::ClassDef *cdef) { - auto *it = absl::c_find_if(cdef->rhs, isIncludeSingleton); - if (it == cdef->rhs.end()) { - return; - } - - auto finalKlass = absl::c_any_of(cdef->rhs, isFinal); - auto loc = it->loc(); - - ast::ClassDef::RHS_store newRHS; - newRHS.reserve(cdef->rhs.size() + 2); - - std::move(cdef->rhs.begin(), it, std::back_inserter(newRHS)); - - { - auto sig = ast::MK::Sig0( - loc, ast::MK::Send0(loc, ast::MK::T(loc), core::Names::attachedClass(), loc.copyWithZeroLength())); - if (finalKlass) { - ast::cast_tree_nonnull(sig).addPosArg(ast::MK::Symbol(loc, core::Names::final_())); - } - newRHS.emplace_back(std::move(sig)); - } - - { - auto method = - ast::MK::SyntheticMethod0(loc, loc, loc, core::Names::instance(), ast::MK::RaiseUnimplemented(loc)); - ast::cast_tree_nonnull(method).flags.isSelfMethod = true; - newRHS.emplace_back(std::move(method)); - } - - std::move(it, cdef->rhs.end(), std::back_inserter(newRHS)); - - cdef->rhs = std::move(newRHS); - - return; -} - -} // namespace sorbet::rewriter diff --git a/rewriter/Singleton.h b/rewriter/Singleton.h deleted file mode 100644 index 73a2465083..0000000000 --- a/rewriter/Singleton.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef SORBET_REWRITER_SINGLETON_H -#define SORBET_REWRITER_SINGLETON_H - -#include "ast/ast.h" - -namespace sorbet::rewriter { - -/** - * Rewrite uses of the `Singleton` module to include the `self.instance` method, with a type signature that gives sorbet - * more information. - * - * ``` - * class Foo - * include Singleton - * end - * ``` - * - * Is rewritten to - * - * ``` - * class Foo - * - * sig {returns(T.attached_class)} - * def self.instance - * raise "Not implemented" - * end - * - * include Singleton - * end - * ``` - */ -class Singleton final { -public: - static void run(core::MutableContext ctx, ast::ClassDef *cdef); - - Singleton() = delete; -}; - -} // namespace sorbet::rewriter - -#endif diff --git a/rewriter/TEnum.cc b/rewriter/TEnum.cc index 235033bec8..2f3871f528 100644 --- a/rewriter/TEnum.cc +++ b/rewriter/TEnum.cc @@ -57,14 +57,14 @@ vector badConst(core::MutableContext ctx, core::LocOffsets h return {}; } -ast::Send *findMagicSelfNew(ast::ExpressionPtr &assignRhs) { +ast::Send *findSelfNew(ast::ExpressionPtr &assignRhs) { auto *rhs = ast::cast_tree(assignRhs); if (rhs != nullptr) { - if (rhs->fun != core::Names::selfNew() && rhs->fun != core::Names::let()) { + if (rhs->fun != core::Names::new_() && rhs->fun != core::Names::let()) { return nullptr; } - if (rhs->fun == core::Names::selfNew() && !ast::MK::isMagicClass(rhs->recv)) { + if (rhs->fun == core::Names::new_() && !rhs->recv.isSelfReference()) { return nullptr; } @@ -97,7 +97,7 @@ ast::Send *findMagicSelfNew(ast::ExpressionPtr &assignRhs) { return nullptr; } - return findMagicSelfNew(cast->arg); + return findSelfNew(cast->arg); } vector processStat(core::MutableContext ctx, ast::ClassDef *klass, ast::ExpressionPtr &stat, @@ -112,14 +112,14 @@ vector processStat(core::MutableContext ctx, ast::ClassDef * return {}; } - auto *magicSelfNew = findMagicSelfNew(asgn->rhs); - if (magicSelfNew == nullptr) { + auto *selfNew = findSelfNew(asgn->rhs); + if (selfNew == nullptr) { return badConst(ctx, stat.loc(), klass->loc); } // By this point, we have something that looks like // - // A = Magic.(self) | T.let(Magic.(self), ...) + // A = .new(...) | T.let(.new(...)) // // So we're good to process this thing as a new T::Enum value. @@ -139,17 +139,13 @@ vector processStat(core::MutableContext ctx, ast::ClassDef * auto classDef = ast::MK::Class(stat.loc(), stat.loc(), classCnst.deepCopy(), std::move(parent), std::move(classRhs)); - // Remove one from the number of positional arguments to account for the self param to . - magicSelfNew->removePosArg(0); - ast::Send::Flags flags = {}; flags.isPrivateOk = true; - auto singletonAsgn = - ast::MK::Assign(stat.loc(), std::move(asgn->lhs), - ast::make_expression( - stat.loc(), core::Types::todo(), - magicSelfNew->withNewBody(stat.loc(), classCnst.deepCopy(), core::Names::new_()), - core::Names::uncheckedLet(), std::move(classCnst))); + auto singletonAsgn = ast::MK::Assign( + stat.loc(), std::move(asgn->lhs), + ast::make_expression(stat.loc(), core::Types::todo(), + selfNew->withNewBody(stat.loc(), classCnst.deepCopy(), core::Names::new_()), + core::Names::uncheckedLet(), std::move(classCnst))); vector result; result.emplace_back(std::move(classDef)); result.emplace_back(std::move(singletonAsgn)); diff --git a/rewriter/rewriter.cc b/rewriter/rewriter.cc index 48abbbed93..1f75a0833a 100644 --- a/rewriter/rewriter.cc +++ b/rewriter/rewriter.cc @@ -8,11 +8,14 @@ #include "rewriter/Cleanup.h" #include "rewriter/Command.h" #include "rewriter/Concern.h" +#include "rewriter/ConstantAssumeType.h" #include "rewriter/DSLBuilder.h" +#include "rewriter/Data.h" #include "rewriter/DefDelegator.h" #include "rewriter/Delegate.h" #include "rewriter/Flatfiles.h" #include "rewriter/Flatten.h" +#include "rewriter/HasAttachedClass.h" #include "rewriter/Initializer.h" #include "rewriter/InterfaceWrapper.h" #include "rewriter/Mattr.h" @@ -23,9 +26,7 @@ #include "rewriter/Prop.h" #include "rewriter/Rails.h" #include "rewriter/Regexp.h" -#include "rewriter/SelfNew.h" #include "rewriter/SigRewriter.h" -#include "rewriter/Singleton.h" #include "rewriter/Struct.h" #include "rewriter/TEnum.h" #include "rewriter/TestCase.h" @@ -51,7 +52,6 @@ class Rewriterer { Flatfiles::run(ctx, classDef); Prop::run(ctx, classDef); TypeMembers::run(ctx, classDef); - Singleton::run(ctx, classDef); Concern::run(ctx, classDef); TestCase::run(ctx, classDef); @@ -73,6 +73,12 @@ class Rewriterer { return; } + nodes = Data::run(ctx, &assign); + if (!nodes.empty()) { + replaceNodes[stat.get()] = std::move(nodes); + return; + } + nodes = ClassNew::run(ctx, &assign); if (!nodes.empty()) { replaceNodes[stat.get()] = std::move(nodes); @@ -84,6 +90,9 @@ class Rewriterer { replaceNodes[stat.get()] = std::move(nodes); return; } + + // This has to come after the `Class.new` rewriter, because they would otherwise overlap. + ConstantAssumeType::run(ctx, &assign); }, [&](ast::Send &send) { @@ -138,6 +147,13 @@ class Rewriterer { replaceNodes[stat.get()] = std::move(nodes); return; } + + // This one is also a little different: it gets the ClassDef kind + nodes = HasAttachedClass::run(ctx, isClass, &send); + if (!nodes.empty()) { + replaceNodes[stat.get()] = std::move(nodes); + return; + } }, [&](ast::MethodDef &mdef) { Initializer::run(ctx, &mdef, prevStat); }, @@ -182,11 +198,6 @@ class Rewriterer { return; } - if (auto expr = SelfNew::run(ctx, send)) { - tree = std::move(expr); - return; - } - if (auto expr = TypeAssertion::run(ctx, send)) { tree = std::move(expr); return; diff --git a/scip_indexer/SCIPIndexer.cc b/scip_indexer/SCIPIndexer.cc index 1bb37c1402..f31ee93037 100644 --- a/scip_indexer/SCIPIndexer.cc +++ b/scip_indexer/SCIPIndexer.cc @@ -24,7 +24,7 @@ #include "cfg/CFG.h" #include "common/EarlyReturnWithCode.h" #include "common/common.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "core/Error.h" #include "core/ErrorQueue.h" #include "core/Loc.h" diff --git a/scip_indexer/SCIPSymbolRef.cc b/scip_indexer/SCIPSymbolRef.cc index 853f8aed54..e6b9c40b3f 100644 --- a/scip_indexer/SCIPSymbolRef.cc +++ b/scip_indexer/SCIPSymbolRef.cc @@ -11,7 +11,7 @@ #include "spdlog/fmt/fmt.h" #include "common/FileSystem.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "core/Loc.h" #include "main/lsp/LSPLoop.h" diff --git a/sorbet-README.md b/sorbet-README.md index 0b233f3cba..34e5baf383 100644 --- a/sorbet-README.md +++ b/sorbet-README.md @@ -4,13 +4,6 @@ # Sorbet -NOTE: This code is in Sourcegraph's fork of Sorbet, modified to add support -for emitting SCIP indexes. You probably want to see -the [scip-ruby README](./README.md) instead or -the [upstream Sorbet README](https://github.com/sorbet/sorbet) instead. - ---- - This repository contains Sorbet, a fast, powerful type checker designed for Ruby. It aims to be easy to add to existing codebases with gradual types, and fast to respond with errors and suggestions. @@ -487,6 +480,57 @@ different version number, and also marked `default-arg-value`: This is due to the translation of defaults into the CFG: there is a synthetic conditional that chooses either to initialize the variable from the argument passed at the send, or to the default value when no value is present. +Finding all references works differently in package specification (__package.rb) files. Consider the following: + +```ruby +class Foo < PackageSpec + import Bar +``` + +Calling "find all references" on `Bar` in this file will return only references to `Bar` in the `Foo` package. LSP tests +have access to `import` and `importusage` assertions that you can use to test this functionality. + +```ruby +class Foo < PackageSpec + import Bar + # ^^^ import: bar +``` + +```ruby + class Foo::Baz + Bar.new + # ^^^ importusage: bar + end +``` + +With these annotations, the LSP test will check if "find all references" on `Bar` in `import Bar` statement returns the `Bar.new` usage. + +Note that an `import` assertion is dissimilar to a `def` assertion, in that it is in fact a subclass of a `usage` assertion. +In this case, the `def` corresponding to an `import` is the PackageSpec declaration of the imported package. Calling "find all references" +on a PackageSpec declaration will return all imports of the package. + + +```ruby +class Bar < PackageSpec + # ^^^ def: bar + import Bar +``` + +```ruby +class Foo < PackageSpec + import Bar + # ^^^ import: bar +``` + +```ruby +class Baz < PackageSpec + import Bar + # ^^^ import: bar +``` + +With these annotations, the LSP test will check if "find all references" on `Bar` from the `class Bar < PackageSpec` +declaration returns the declaration itself plus the imports. + #### Testing "Go to Type Definition" This is somewhat similar to "Find Definition" above, but also slightly different @@ -543,6 +587,13 @@ If a location should report the empty string, use the special label `(nothing)`: # ^ hover: (nothing) ``` +Assert the contents of a specific line of the hover response with `hover-line` assertions: + +```ruby + a = 10 +# ^ hover-line: 1 Integer(10) +``` + #### Testing completion LSP tests can also assert the contents of completion responses with `completion` @@ -835,6 +886,9 @@ be slow, depending on what needs to be recompiled and updated. Some faster commands: ```bash +# Only update the `*.exp` files in `test/testdata` +tools/scripts/update_testdata_exp.sh + # Only update the `*.exp` files in `test/testdata/cfg` tools/scripts/update_testdata_exp.sh test/testdata/cfg @@ -843,9 +897,6 @@ tools/scripts/update_testdata_exp.sh test/testdata/cfg/next.rb # Only update the `*.out` files in `test/cli` bazel test //test/cli:update - -# Update the `*.exp` files in `gems/sorbet/test/hidden-method-finder` -gems/sorbet/test/hidden-method-finder/update_hidden_methods_exp.sh ``` diff --git a/sorbet_version/sorbet_version.h b/sorbet_version/sorbet_version.h index 2cabf751a4..9c424d63e0 100644 --- a/sorbet_version/sorbet_version.h +++ b/sorbet_version/sorbet_version.h @@ -9,6 +9,7 @@ extern "C" { #if !defined(NDEBUG) || defined(FORCE_DEBUG) #define DEBUG_MODE +#define TRACK_UNTYPED_BLAME_MODE #else #undef DEBUG_MODE #endif @@ -22,6 +23,12 @@ constexpr bool debug_mode = true; constexpr bool debug_mode = false; #endif +#ifdef TRACK_UNTYPED_BLAME_MODE +constexpr bool track_untyped_blame_mode = true; +#else +constexpr bool track_untyped_blame_mode = false; +#endif + #if !defined(EMSCRIPTEN) constexpr bool emscripten_build = false; #else diff --git a/test/BUILD b/test/BUILD index 7663a83026..d6bbb3f927 100644 --- a/test/BUILD +++ b/test/BUILD @@ -36,8 +36,8 @@ cc_binary( "//rewriter", "//test/helpers", "@cxxopts", - "@doctest", - "@doctest//:doctest_custom_main", + "@doctest//doctest", + "@doctest//doctest:custom_main", ], ) @@ -58,8 +58,8 @@ cc_binary( "//payload", "//test/helpers", "@cxxopts", - "@doctest", - "@doctest//:doctest_custom_main", + "@doctest//doctest", + "@doctest//doctest:custom_main", ], ) @@ -99,8 +99,8 @@ cc_binary( "//test/helpers", "@com_google_absl//absl/strings", "@cxxopts", - "@doctest", - "@doctest//:doctest_custom_main", + "@doctest//doctest", + "@doctest//doctest:custom_main", ], ) @@ -133,8 +133,8 @@ cc_binary( "//rewriter", "//test/helpers", "@cxxopts", - "@doctest", - "@doctest//:doctest_custom_main", + "@doctest//doctest", + "@doctest//doctest:custom_main", ], ) @@ -181,8 +181,8 @@ cc_test( "//parser", "//payload", "@cxxopts", - "@doctest", - "@doctest//:doctest_main", + "@doctest//doctest", + "@doctest//doctest:main", ], ) @@ -204,8 +204,8 @@ cc_test( "//local_vars", "//packager", "//parser", - "@doctest", - "@doctest//:doctest_main", + "@doctest//doctest", + "@doctest//doctest:main", ], ) @@ -224,8 +224,8 @@ cc_test( "//common", "//core", "//parser", - "@doctest", - "@doctest//:doctest_main", + "@doctest//doctest", + "@doctest//doctest:main", ], ) @@ -423,6 +423,9 @@ sh_binary( "//main:sorbet", "@sorbet_ruby_2_7//:ruby", ], + # This is to get the test to run on the compiler build job, + # so we can avoid building ruby on the test-static-sanitized job. + tags = ["compiler"], deps = [ ":logging", "@bazel_tools//tools/bash/runfiles", diff --git a/test/cli/autocorrect-extend/autocorrect-extend.rb b/test/cli/autocorrect-extend/autocorrect-extend.rb index 64985a43e2..104213a5fe 100644 --- a/test/cli/autocorrect-extend/autocorrect-extend.rb +++ b/test/cli/autocorrect-extend/autocorrect-extend.rb @@ -1,6 +1,5 @@ # typed: strict -# Don't extend T::Sig for this one, because the class wasn't defined here. def top_level; end # Should only add one extend diff --git a/test/cli/autocorrect-extend/test.out b/test/cli/autocorrect-extend/test.out index 8f81afda60..78b0e3aa89 100644 --- a/test/cli/autocorrect-extend/test.out +++ b/test/cli/autocorrect-extend/test.out @@ -1,6 +1,6 @@ # typed: strict -# Don't extend T::Sig for this one, because the class wasn't defined here. +extend T::Sig sig { returns(NilClass) } def top_level; end diff --git a/test/cli/autocorrect/test.out b/test/cli/autocorrect/test.out index 9af754f891..d41be8e4b7 100644 --- a/test/cli/autocorrect/test.out +++ b/test/cli/autocorrect/test.out @@ -285,7 +285,6 @@ T.must(foo)[0] "hi" + T.must(foo) extend T::Sig - sig {params(a: String).void} def int(a:); end int(a: T.must(foo)) @@ -298,7 +297,6 @@ T.must(foo.bar) &= "a" T.must([1].max_by {|l,r| 1})[2] extend T::Sig - sig {params(a: T.nilable(Integer)).void} def foo(a:) end diff --git a/test/cli/autocorrect_array_plus/autocorrect_array_plus.rb b/test/cli/autocorrect_array_plus/autocorrect_array_plus.rb index d09ad0ec38..ed2400d598 100644 --- a/test/cli/autocorrect_array_plus/autocorrect_array_plus.rb +++ b/test/cli/autocorrect_array_plus/autocorrect_array_plus.rb @@ -3,7 +3,8 @@ ints = T::Array[Integer].new strings = T::Array[String].new -ints + strings +x = ints + strings +T.reveal_type(x) ints + (strings) ints + ((strings)) diff --git a/test/cli/autocorrect_array_plus/test.out b/test/cli/autocorrect_array_plus/test.out index 6910258ab5..94a6226623 100644 --- a/test/cli/autocorrect_array_plus/test.out +++ b/test/cli/autocorrect_array_plus/test.out @@ -1,196 +1,23 @@ -autocorrect_array_plus.rb:6: Expected `T::Enumerable[Integer]` but found `T::Array[String]` for argument `arg0` https://srb.help/7002 - 6 |ints + strings - ^^^^^^^ - Expected `T::Enumerable[Integer]` for argument `arg0` of method `Array#+`: - https://github.com/sorbet/sorbet/tree/master/rbi/core/array.rbi#LCENSORED: - NN | arg0: T::Enumerable[Elem], - ^^^^ - Got `T::Array[String]` originating from: - autocorrect_array_plus.rb:4: - 4 |strings = T::Array[String].new - ^^^^^^^^^^^^^^^^^^^^ - Note: - If the desired behavior is to widen the type to `T::Array[T.any(Integer, String)]`, use `Array#concat` instead - Autocorrect: Done - autocorrect_array_plus.rb:6: Replaced with `.concat(strings)` - 6 |ints + strings - ^^^^^^^^^^ - -autocorrect_array_plus.rb:8: Expected `T::Enumerable[Integer]` but found `T::Array[String]` for argument `arg0` https://srb.help/7002 - 8 |ints + (strings) - ^^^^^^^ - Expected `T::Enumerable[Integer]` for argument `arg0` of method `Array#+`: - https://github.com/sorbet/sorbet/tree/master/rbi/core/array.rbi#LCENSORED: - NN | arg0: T::Enumerable[Elem], - ^^^^ - Got `T::Array[String]` originating from: - autocorrect_array_plus.rb:4: - 4 |strings = T::Array[String].new - ^^^^^^^^^^^^^^^^^^^^ - Note: - If the desired behavior is to widen the type to `T::Array[T.any(Integer, String)]`, use `Array#concat` instead - Autocorrect: Done - autocorrect_array_plus.rb:8: Replaced with `.concat(strings)` - 8 |ints + (strings) - ^^^^^^^^^^^^ - -autocorrect_array_plus.rb:9: Expected `T::Enumerable[Integer]` but found `T::Array[String]` for argument `arg0` https://srb.help/7002 - 9 |ints + ((strings)) - ^^^^^^^ - Expected `T::Enumerable[Integer]` for argument `arg0` of method `Array#+`: - https://github.com/sorbet/sorbet/tree/master/rbi/core/array.rbi#LCENSORED: - NN | arg0: T::Enumerable[Elem], - ^^^^ - Got `T::Array[String]` originating from: - autocorrect_array_plus.rb:4: - 4 |strings = T::Array[String].new - ^^^^^^^^^^^^^^^^^^^^ - Note: - If the desired behavior is to widen the type to `T::Array[T.any(Integer, String)]`, use `Array#concat` instead - Autocorrect: Done - autocorrect_array_plus.rb:9: Replaced with `.concat(strings)` - 9 |ints + ((strings)) +autocorrect_array_plus.rb:7: Revealed type: `T::Array[T.any(Integer, String)]` https://srb.help/7014 + 7 |T.reveal_type(x) + ^^^^^^^^^^^^^^^^ + Got `T::Array[T.any(Integer, String)]` originating from: + autocorrect_array_plus.rb:6: + 6 |x = ints + strings ^^^^^^^^^^^^^^ -autocorrect_array_plus.rb:10: Expected `T::Enumerable[Integer]` but found `T::Array[String]` for argument `arg0` https://srb.help/7002 - 10 |ints.+(strings) - ^^^^^^^ - Expected `T::Enumerable[Integer]` for argument `arg0` of method `Array#+`: - https://github.com/sorbet/sorbet/tree/master/rbi/core/array.rbi#LCENSORED: - NN | arg0: T::Enumerable[Elem], - ^^^^ - Got `T::Array[String]` originating from: - autocorrect_array_plus.rb:4: - 4 |strings = T::Array[String].new - ^^^^^^^^^^^^^^^^^^^^ - Note: - If the desired behavior is to widen the type to `T::Array[T.any(Integer, String)]`, use `Array#concat` instead - Autocorrect: Done - autocorrect_array_plus.rb:10: Replaced with `.concat(strings)` - 10 |ints.+(strings) - ^^^^^^^^^^^ - -autocorrect_array_plus.rb:13: Expected `T::Enumerable[Integer]` but found `T::Array[String]` for argument `arg0` https://srb.help/7002 - 13 | strings - ^^^^^^^ - Expected `T::Enumerable[Integer]` for argument `arg0` of method `Array#+`: - https://github.com/sorbet/sorbet/tree/master/rbi/core/array.rbi#LCENSORED: - NN | arg0: T::Enumerable[Elem], - ^^^^ - Got `T::Array[String]` originating from: - autocorrect_array_plus.rb:4: - 4 |strings = T::Array[String].new - ^^^^^^^^^^^^^^^^^^^^ - Note: - If the desired behavior is to widen the type to `T::Array[T.any(Integer, String)]`, use `Array#concat` instead - Autocorrect: Done - autocorrect_array_plus.rb:12: Replaced with `.concat(strings)` - 12 |ints.+( - 13 | strings - 14 |) - -autocorrect_array_plus.rb:16: Expected `T::Enumerable[Integer]` but found `T::Array[String]` for argument `arg0` https://srb.help/7002 - 16 |ints + strings - ^^^^^^^ - Expected `T::Enumerable[Integer]` for argument `arg0` of method `Array#+`: - https://github.com/sorbet/sorbet/tree/master/rbi/core/array.rbi#LCENSORED: - NN | arg0: T::Enumerable[Elem], - ^^^^ - Got `T::Array[String]` originating from: - autocorrect_array_plus.rb:4: - 4 |strings = T::Array[String].new - ^^^^^^^^^^^^^^^^^^^^ - Note: - If the desired behavior is to widen the type to `T::Array[T.any(Integer, String)]`, use `Array#concat` instead - Autocorrect: Done - autocorrect_array_plus.rb:16: Replaced with `.concat(strings)` - 16 |ints + strings - ^^^^^^^^^^ - -autocorrect_array_plus.rb:18: Expected `T::Enumerable[Integer]` but found `T::Array[String]` for argument `arg0` https://srb.help/7002 - 18 |ints += strings - ^^^^^^^ - Expected `T::Enumerable[Integer]` for argument `arg0` of method `Array#+`: - https://github.com/sorbet/sorbet/tree/master/rbi/core/array.rbi#LCENSORED: - NN | arg0: T::Enumerable[Elem], - ^^^^ - Got `T::Array[String]` originating from: - autocorrect_array_plus.rb:4: - 4 |strings = T::Array[String].new - ^^^^^^^^^^^^^^^^^^^^ - Note: - If the desired behavior is to widen the type to `T::Array[T.any(Integer, String)]`, use `Array#concat` instead - Autocorrect: Done - autocorrect_array_plus.rb:18: Replaced with `= ints.concat(strings)` - 18 |ints += strings - ^^^^^^^^^^^ - -autocorrect_array_plus.rb:19: Expected `T::Enumerable[Integer]` but found `T::Array[String]` for argument `arg0` https://srb.help/7002 - 19 |ints += strings - ^^^^^^^ - Expected `T::Enumerable[Integer]` for argument `arg0` of method `Array#+`: - https://github.com/sorbet/sorbet/tree/master/rbi/core/array.rbi#LCENSORED: - NN | arg0: T::Enumerable[Elem], - ^^^^ - Got `T::Array[String]` originating from: - autocorrect_array_plus.rb:4: - 4 |strings = T::Array[String].new - ^^^^^^^^^^^^^^^^^^^^ - Note: - If the desired behavior is to widen the type to `T::Array[T.any(Integer, String)]`, use `Array#concat` instead - Autocorrect: Done - autocorrect_array_plus.rb:19: Replaced with `= ints.concat(strings)` - 19 |ints += strings - ^^^^^^^^^^^ - -autocorrect_array_plus.rb:20: Expected `T::Enumerable[Integer]` but found `T::Array[String]` for argument `arg0` https://srb.help/7002 - 20 |ints += strings - ^^^^^^^ - Expected `T::Enumerable[Integer]` for argument `arg0` of method `Array#+`: - https://github.com/sorbet/sorbet/tree/master/rbi/core/array.rbi#LCENSORED: - NN | arg0: T::Enumerable[Elem], - ^^^^ - Got `T::Array[String]` originating from: - autocorrect_array_plus.rb:4: - 4 |strings = T::Array[String].new - ^^^^^^^^^^^^^^^^^^^^ - Note: - If the desired behavior is to widen the type to `T::Array[T.any(Integer, String)]`, use `Array#concat` instead - Autocorrect: Done - autocorrect_array_plus.rb:20: Replaced with `= ints.concat(strings)` - 20 |ints += strings - ^^^^^^^^^^^ - -autocorrect_array_plus.rb:22: Expected `T::Enumerable[{String("foo") => String("bar")}]` but found `[{}]` for argument `arg0` https://srb.help/7002 - 22 |[{"foo" => "bar"}] + [{}] - ^^^^ - Expected `T::Enumerable[{String("foo") => String("bar")}]` for argument `arg0` of method `Array#+`: - https://github.com/sorbet/sorbet/tree/master/rbi/core/array.rbi#LCENSORED: - NN | arg0: T::Enumerable[Elem], - ^^^^ - Got `[{}] (1-tuple)` originating from: - autocorrect_array_plus.rb:22: - 22 |[{"foo" => "bar"}] + [{}] - ^^^^ - Note: - If the desired behavior is to widen the type to `[{String("foo") => String("bar")}, {}]`, use `Array#concat` instead - Autocorrect: Done - autocorrect_array_plus.rb:22: Replaced with `.concat([{}])` - 22 |[{"foo" => "bar"}] + [{}] - ^^^^^^^ - -autocorrect_array_plus.rb:24: Expected `T::Enumerable[String]` but found `String("")` for argument `arg0` https://srb.help/7002 - 24 |strings + "" +autocorrect_array_plus.rb:25: Expected `T::Enumerable[T.type_parameter(:T)]` but found `String("")` for argument `arg0` https://srb.help/7002 + 25 |strings + "" ^^ - Expected `T::Enumerable[String]` for argument `arg0` of method `Array#+`: + Expected `T::Enumerable[T.type_parameter(:T)]` for argument `arg0` of method `Array#+`: https://github.com/sorbet/sorbet/tree/master/rbi/core/array.rbi#LCENSORED: - NN | arg0: T::Enumerable[Elem], + NN | arg0: T::Enumerable[T.type_parameter(:T)], ^^^^ Got `String("")` originating from: - autocorrect_array_plus.rb:24: - 24 |strings + "" + autocorrect_array_plus.rb:25: + 25 |strings + "" ^^ -Errors: 11 +Errors: 2 -------------------------------------------------------------------------- @@ -199,20 +26,23 @@ Errors: 11 ints = T::Array[Integer].new strings = T::Array[String].new -ints.concat(strings) +x = ints + strings +T.reveal_type(x) -ints.concat(strings) -ints.concat(strings) -ints.concat(strings) +ints + (strings) +ints + ((strings)) +ints.+(strings) -ints.concat(strings) +ints.+( + strings +) -ints.concat(strings) +ints + strings -ints = ints.concat(strings) -ints = ints.concat(strings) -ints = ints.concat(strings) +ints += strings +ints += strings +ints += strings -[{"foo" => "bar"}].concat([{}]) +[{"foo" => "bar"}] + [{}] strings + "" diff --git a/test/cli/autogen-autoloader/test.out b/test/cli/autogen-autoloader/test.out index 14b8d776c9..95cb7604f7 100644 --- a/test/cli/autogen-autoloader/test.out +++ b/test/cli/autogen-autoloader/test.out @@ -1,149 +1 @@ -No errors! Great job. - ---- output/Foo/Bar/Jazz.rb -# frozen_string_literal: true -# typed: true - -require 'in_class' -require 'in_method' -require 'my_gem' - -class Foo::Bar::Jazz < Foo::Bar::Quuz -end - -Opus::Require.for_autoload(Foo::Bar::Jazz, "test/cli/autogen-autoloader/example1.rb") - ---- output/Foo/Bar/Quuz.rb -# frozen_string_literal: true -# typed: true - -require 'in_class' -require 'in_method' -require 'my_gem' - -class Foo::Bar::Quuz -end - -Opus::Require.for_autoload(Foo::Bar::Quuz, "test/cli/autogen-autoloader/example1.rb") - ---- output/Foo/Dabba.rb -# frozen_string_literal: true -# typed: true - -require 'in_class' -require 'in_method' -require 'my_gem' - -Opus::Require.for_autoload(nil, "test/cli/autogen-autoloader/example1.rb", [Foo, :Dabba]) - ---- output/Foo/Errors/BaseError.rb -# frozen_string_literal: true -# typed: true - - -class Foo::Errors::BaseError < StandardError -end - -Opus::Require.for_autoload(Foo::Errors::BaseError, "test/cli/autogen-autoloader/errors.rb") - ---- output/Foo/Errors/MyError1.rb -# frozen_string_literal: true -# typed: true - - -class Foo::Errors::MyError1 < Foo::Errors::BaseError -end - -Opus::Require.for_autoload(Foo::Errors::MyError1, "test/cli/autogen-autoloader/errors.rb") - ---- output/Foo/Errors/MyError2.rb -# frozen_string_literal: true -# typed: true - - -class Foo::Errors::MyError2 < Foo::Errors::BaseError -end - -Opus::Require.for_autoload(Foo::Errors::MyError2, "test/cli/autogen-autoloader/errors.rb") - ---- output/Foo/TOP_LEVEL_CONST.rb -# frozen_string_literal: true -# typed: true - -require 'in_class' -require 'in_method' -require 'my_gem' - -Opus::Require.for_autoload(nil, "test/cli/autogen-autoloader/example1.rb", [Foo, :TOP_LEVEL_CONST]) - ---- output/Yabba/Dabba/Bar2.rb -# frozen_string_literal: true -# typed: true - - -class Yabba::Dabba::Bar2 -end - -Opus::Require.for_autoload(Yabba::Dabba::Bar2, "test/cli/autogen-autoloader/example3.rb") - ---- output/Yabba/Dabba/Jazz.rb -# frozen_string_literal: true -# typed: true - - -class Yabba::Dabba::Jazz -end - ---- output/Yabba/Dabba/Jazz/JazBaz.rb -# frozen_string_literal: true -# typed: true - - -class Yabba::Dabba::Jazz::JazBaz -end - -Opus::Require.for_autoload(Yabba::Dabba::Jazz::JazBaz, "test/cli/autogen-autoloader/example2.rb") - ---- output/Yabba/Dabba/NoBehavior.rb -# frozen_string_literal: true -# typed: true - - -class Yabba::Dabba::NoBehavior -end - -Opus::Require.for_autoload(Yabba::Dabba::NoBehavior, "test/cli/autogen-autoloader/example3.rb") - ---- output/Yabba/Dabba/Quuz.rb -# frozen_string_literal: true -# typed: true - - -class Yabba::Dabba::Quuz < AWS::String -end - -Opus::Require.for_autoload(Yabba::Dabba::Quuz, "test/cli/autogen-autoloader/example2.rb") - ---- missing output directory ---print=autogen-autoloader requires an output path to be specified - ---- in-place writes -inplace-output -inplace-output/Foo.rb - ---- strip-prefixes and root rename - - -module Foo -end - -Primus::Require.for_autoload(Foo, "autogen-autoloader/inplace.rb") - ---- with different root object -No errors! Great job. - - -module Foo -end - -Opus::Require.for_autoload(Foo, "test/cli/autogen-autoloader/inplace.rb") +Option ‘autogen-autoloader-modules’ does not exist. To see all available options pass `--help`. diff --git a/test/cli/autogen-pkg-autoloader/nested/__package.rb b/test/cli/autogen-pkg-autoloader/nested/__package.rb index 2b684d4e5e..98d8cc51b0 100644 --- a/test/cli/autogen-pkg-autoloader/nested/__package.rb +++ b/test/cli/autogen-pkg-autoloader/nested/__package.rb @@ -2,6 +2,5 @@ # frozen_string_literal: true class RootPackage::Nested < PackageSpec - autoloader_compatibility 'strict' end diff --git a/test/cli/autogen-pkg-autoloader/test.out b/test/cli/autogen-pkg-autoloader/test.out index 18f713c218..95cb7604f7 100644 --- a/test/cli/autogen-pkg-autoloader/test.out +++ b/test/cli/autogen-pkg-autoloader/test.out @@ -1,137 +1 @@ -No errors! Great job. - ---- output/RootPackage/Foo/Bar/Jazz.rb -# frozen_string_literal: true -# typed: true - -require 'in_class' -require 'in_method' -require 'my_gem' - -class RootPackage::Foo::Bar::Jazz < RootPackage::Foo::Bar::Quuz -end - -Opus::Require.for_autoload(RootPackage::Foo::Bar::Jazz, "test/cli/autogen-pkg-autoloader/foo.rb") - ---- output/RootPackage/Foo/Bar/Quuz.rb -# frozen_string_literal: true -# typed: true - -require 'in_class' -require 'in_method' -require 'my_gem' - -class RootPackage::Foo::Bar::Quuz -end - -Opus::Require.for_autoload(RootPackage::Foo::Bar::Quuz, "test/cli/autogen-pkg-autoloader/foo.rb") - ---- output/RootPackage/Foo/Dabba.rb -# frozen_string_literal: true -# typed: true - -require 'in_class' -require 'in_method' -require 'my_gem' - -Opus::Require.for_autoload(nil, "test/cli/autogen-pkg-autoloader/foo.rb", [RootPackage::Foo, :Dabba]) - ---- output/RootPackage/Foo/Errors/BaseError.rb -# frozen_string_literal: true -# typed: true - - -class RootPackage::Foo::Errors::BaseError < StandardError -end - -Opus::Require.for_autoload(RootPackage::Foo::Errors::BaseError, "test/cli/autogen-pkg-autoloader/errors.rb") - ---- output/RootPackage/Foo/Errors/MyError1.rb -# frozen_string_literal: true -# typed: true - - -class RootPackage::Foo::Errors::MyError1 < RootPackage::Foo::Errors::BaseError -end - -Opus::Require.for_autoload(RootPackage::Foo::Errors::MyError1, "test/cli/autogen-pkg-autoloader/errors.rb") - ---- output/RootPackage/Foo/Errors/MyError2.rb -# frozen_string_literal: true -# typed: true - - -class RootPackage::Foo::Errors::MyError2 < RootPackage::Foo::Errors::BaseError -end - -Opus::Require.for_autoload(RootPackage::Foo::Errors::MyError2, "test/cli/autogen-pkg-autoloader/errors.rb") - ---- output/RootPackage/Foo/TOP_LEVEL_CONST.rb -# frozen_string_literal: true -# typed: true - -require 'in_class' -require 'in_method' -require 'my_gem' - -Opus::Require.for_autoload(nil, "test/cli/autogen-pkg-autoloader/foo.rb", [RootPackage::Foo, :TOP_LEVEL_CONST]) - ---- output/RootPackage/Nested.rb -# frozen_string_literal: true -# typed: true - - -module RootPackage::Nested -end - -Opus::Require.pbal_register_package(RootPackage::Nested, 'test/cli/autogen-pkg-autoloader/nested/') - -Opus::Require.for_autoload(RootPackage::Nested, "test/cli/autogen-pkg-autoloader/nested/nested.rb") - ---- output/RootPackage/Yabba/Dabba/Bar2.rb -# frozen_string_literal: true -# typed: true - - -class RootPackage::Yabba::Dabba::Bar2 -end - -Opus::Require.for_autoload(RootPackage::Yabba::Dabba::Bar2, "test/cli/autogen-pkg-autoloader/bar2.rb") - ---- output/RootPackage/Yabba/Dabba/Jazz.rb -# frozen_string_literal: true -# typed: true - - -class RootPackage::Yabba::Dabba::Jazz -end - ---- output/RootPackage/Yabba/Dabba/Jazz/JazBaz.rb -# frozen_string_literal: true -# typed: true - - -class RootPackage::Yabba::Dabba::Jazz::JazBaz -end - -Opus::Require.for_autoload(RootPackage::Yabba::Dabba::Jazz::JazBaz, "test/cli/autogen-pkg-autoloader/bar.rb") - ---- output/RootPackage/Yabba/Dabba/NoBehavior.rb -# frozen_string_literal: true -# typed: true - - -class RootPackage::Yabba::Dabba::NoBehavior -end - -Opus::Require.for_autoload(RootPackage::Yabba::Dabba::NoBehavior, "test/cli/autogen-pkg-autoloader/bar2.rb") - ---- output/RootPackage/Yabba/Dabba/Quuz.rb -# frozen_string_literal: true -# typed: true - - -class RootPackage::Yabba::Dabba::Quuz < AWS::String -end - -Opus::Require.for_autoload(RootPackage::Yabba::Dabba::Quuz, "test/cli/autogen-pkg-autoloader/bar.rb") +Option ‘autogen-autoloader-modules’ does not exist. To see all available options pass `--help`. diff --git a/test/cli/autogen-pkg-autoloader/test.sh b/test/cli/autogen-pkg-autoloader/test.sh index 1f67a03bac..bb183aa747 100755 --- a/test/cli/autogen-pkg-autoloader/test.sh +++ b/test/cli/autogen-pkg-autoloader/test.sh @@ -13,6 +13,10 @@ cp -r test/cli/autogen-pkg-autoloader "$tmp/test/cli" cd "$tmp" || exit 1 mkdir output +dir_to_delete="output/RootPackage/Nested" +inner_dir_to_delete="${dir_to_delete}/Inner" +mkdir -p $inner_dir_to_delete +touch "$inner_dir_to_delete/__file_to_delete.rb" "$cwd/main/sorbet" --silence-dev-message --stop-after=namer \ --stripe-packages \ @@ -21,7 +25,6 @@ mkdir output --autogen-autoloader-exclude-require=byebug \ --autogen-autoloader-ignore=scripts/ \ --autogen-autoloader-preamble "$preamble" \ - --autogen-autoloader-pbal-namespaces RootPackage \ test/cli/autogen-pkg-autoloader/{foo,bar,bar2,errors,__package}.rb \ test/cli/autogen-pkg-autoloader/nested/*.rb \ test/cli/autogen-pkg-autoloader/scripts/baz.rb 2>&1 @@ -31,4 +34,10 @@ for file in $(find output -type f | sort | grep -v "_mtime_stamp"); do cat "$file" done +if test -d $dir_to_delete; then + echo "ERROR: $dir_to_delete exists" +else + echo "$dir_to_delete correctly deleted" +fi + rm -rf "$tmp" diff --git a/test/cli/dash-e/test.out b/test/cli/dash-e/test.out index 1cbeb4366b..1079db4fa1 100644 --- a/test/cli/dash-e/test.out +++ b/test/cli/dash-e/test.out @@ -13,7 +13,8 @@ class :: < ::Object () 2 |def foo; end ^^^^^^^ Autocorrect: Use `-a` to autocorrect - -e:2: Insert `sig { returns(NilClass) }` + -e:2: Insert `extend T::Sig + sig { returns(NilClass) }` 2 |def foo; end ^ Errors: 1 diff --git a/test/cli/dedup_loc/test.out b/test/cli/dedup_loc/test.out index 2f4f41e0fa..49b115608a 100644 --- a/test/cli/dedup_loc/test.out +++ b/test/cli/dedup_loc/test.out @@ -4,7 +4,7 @@ test/cli/dedup_loc/dedup_loc.rb:5: Argument does not have asserted type `NilClas Got `Exception` originating from: test/cli/dedup_loc/dedup_loc.rb:4: 4 |rescue Exception => e - 5 | T.assert_type!(e, NilClass) + ^^^^^^ test/cli/dedup_loc/dedup_loc.rb:4: 4 |rescue Exception => e ^^^^^^^^^ diff --git a/test/cli/errors/test.out b/test/cli/errors/test.out index a998f3234b..e003f942f2 100644 --- a/test/cli/errors/test.out +++ b/test/cli/errors/test.out @@ -9,12 +9,12 @@ test/cli/errors/errors.rb:5: Unable to resolve constant `MyConstantWithTypo` htt .. | MyConstantWithNoTypo = nil ^^^^^^^^^^^^^^^^^^^^ -test/cli/errors/errors.rb:15: Expected `String` but found `Integer` for argument `arg0` https://srb.help/7002 +test/cli/errors/errors.rb:15: Expected `T.any(T::Class[T.anything], Exception, String)` but found `Integer` for argument `arg0` https://srb.help/7002 .. | raise arg # raise is defined by stdlib ^^^ - Expected `String` for argument `arg0` of method `Kernel#raise (overload.1)`: + Expected `T.any(T::Class[T.anything], Exception, String)` for argument `arg0` of method `Kernel#raise (overload.1)`: https://github.com/sorbet/sorbet/tree/master/rbi/core/kernel.rbi#LCENSORED: - NN | arg0: String, + NN | arg0: T.any(T::Class[T.anything], Exception, String), ^^^^ Got `Integer` originating from: test/cli/errors/errors.rb:13: @@ -66,12 +66,12 @@ test/cli/errors/errors.rb:5: Unable to resolve constant `MyConstantWithTypo` htt .. | MyConstantWithNoTypo = nil ^^^^^^^^^^^^^^^^^^^^ -test/cli/errors/errors.rb:15: Expected `String` but found `Integer` for argument `arg0` https://srb.help/7002 +test/cli/errors/errors.rb:15: Expected `T.any(T::Class[T.anything], Exception, String)` but found `Integer` for argument `arg0` https://srb.help/7002 .. | raise arg # raise is defined by stdlib ^^^ - Expected `String` for argument `arg0` of method `Kernel#raise (overload.1)`: + Expected `T.any(T::Class[T.anything], Exception, String)` for argument `arg0` of method `Kernel#raise (overload.1)`: https://github.com/sorbet/sorbet/tree/master/rbi/core/kernel.rbi#LCENSORED: - NN | arg0: String, + NN | arg0: T.any(T::Class[T.anything], Exception, String), ^^^^ Got `Integer` originating from: test/cli/errors/errors.rb:13: @@ -123,12 +123,12 @@ test/cli/errors/errors.rb:5: Unable to resolve constant `MyConstantWithTypo` htt .. | MyConstantWithNoTypo = nil ^^^^^^^^^^^^^^^^^^^^ -test/cli/errors/errors.rb:15: Expected `String` but found `Integer` for argument `arg0` https://srb.help/7002 +test/cli/errors/errors.rb:15: Expected `T.any(T::Class[T.anything], Exception, String)` but found `Integer` for argument `arg0` https://srb.help/7002 .. | raise arg # raise is defined by stdlib ^^^ - Expected `String` for argument `arg0` of method `Kernel#raise (overload.1)`: + Expected `T.any(T::Class[T.anything], Exception, String)` for argument `arg0` of method `Kernel#raise (overload.1)`: https://github.com/sorbet/sorbet/tree/master/rbi/core/kernel.rbi#LCENSORED: - NN | arg0: String, + NN | arg0: T.any(T::Class[T.anything], Exception, String), ^^^^ Got `Integer` originating from: test/cli/errors/errors.rb:13: diff --git a/test/cli/expected-got/test.out b/test/cli/expected-got/test.out index 98bb3d66a3..805afe3f38 100644 --- a/test/cli/expected-got/test.out +++ b/test/cli/expected-got/test.out @@ -68,10 +68,9 @@ test/cli/expected-got/expected-got.rb:30: `T.must` called on `Integer(0)`, which 30 |T.must(0) ^^^^^^^^^ -test/cli/expected-got/expected-got.rb:36: Expected `Integer` but found `String("")` for block result type https://srb.help/7005 - 36 |takes_int_block do +test/cli/expected-got/expected-got.rb:37: Expected `Integer` but found `String("")` for block result type https://srb.help/7005 37 | '' - 38 |end + ^^ Expected `Integer` for block result type: test/cli/expected-got/expected-got.rb:32: 32 |sig {params(blk: T.proc.returns(Integer)).void} diff --git a/test/cli/flow-sensitivity-hint/test.out b/test/cli/flow-sensitivity-hint/test.out index 86167dc8c1..f66a8d76cf 100644 --- a/test/cli/flow-sensitivity-hint/test.out +++ b/test/cli/flow-sensitivity-hint/test.out @@ -118,10 +118,10 @@ test/cli/flow-sensitivity-hint/test.rb:46: Method `even?` does not exist on `Nil 46 | looks_like_var_but_is_method.even? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -test/cli/flow-sensitivity-hint/test.rb:58: Method `sqrt` does not exist on `Class` https://srb.help/7003 +test/cli/flow-sensitivity-hint/test.rb:58: Method `sqrt` does not exist on `T::Class[T.anything]` https://srb.help/7003 58 | returns_class.sqrt(4) ^^^^ - Got `Class` originating from: + Got `T::Class[T.anything]` originating from: test/cli/flow-sensitivity-hint/test.rb:58: 58 | returns_class.sqrt(4) ^^^^^^^^^^^^^ diff --git a/test/cli/lsp-common-case-exit/test.out b/test/cli/lsp-common-case-exit/test.out index afdef58f71..df173edf11 100644 --- a/test/cli/lsp-common-case-exit/test.out +++ b/test/cli/lsp-common-case-exit/test.out @@ -1,5 +1,5 @@ -Content-Length: 597 +Content-Length: 639 -{"jsonrpc":"2.0","id":0,"requestMethod":"initialize","result":{"capabilities":{"textDocumentSync":1,"hoverProvider":true,"completionProvider":{"triggerCharacters":[".",":","@","#"]},"definitionProvider":true,"typeDefinitionProvider":true,"implementationProvider":true,"referencesProvider":true,"documentHighlightProvider":false,"documentSymbolProvider":false,"workspaceSymbolProvider":true,"codeActionProvider":{"codeActionKinds":["quickfix","source.fixAll.sorbet","refactor.extract"]},"documentFormattingProvider":false,"renameProvider":{"prepareProvider":true},"sorbetShowSymbolProvider":true}}}Content-Length: 65 +{"jsonrpc":"2.0","id":0,"requestMethod":"initialize","result":{"capabilities":{"textDocumentSync":1,"hoverProvider":true,"completionProvider":{"triggerCharacters":[".",":","@","#"]},"definitionProvider":true,"typeDefinitionProvider":true,"implementationProvider":true,"referencesProvider":true,"documentHighlightProvider":false,"documentSymbolProvider":false,"workspaceSymbolProvider":true,"codeActionProvider":{"codeActionKinds":["quickfix","source.fixAll.sorbet","refactor.extract","refactor.rewrite"],"resolveProvider":true},"documentFormattingProvider":false,"renameProvider":{"prepareProvider":true},"sorbetShowSymbolProvider":true}}}Content-Length: 65 {"jsonrpc":"2.0","id":1,"requestMethod":"shutdown","result":null} \ No newline at end of file diff --git a/test/cli/minimize-rbi/test.out b/test/cli/minimize-rbi/test.out index 884a221ae9..57ca669578 100644 --- a/test/cli/minimize-rbi/test.out +++ b/test/cli/minimize-rbi/test.out @@ -16,13 +16,11 @@ class :: < ::Object () method ::# () @ test/cli/minimize-rbi/minimize-rbi.rb:5 argument @ Loc {file=test/cli/minimize-rbi/minimize-rbi.rb start=??? end=???} module ::ModuleCommonToBoth < ::Sorbet::Private::Static::ImplicitModuleSuperclass () @ test/cli/minimize-rbi/minimize-rbi.rb:12 - class ::[] < ::Module () @ test/cli/minimize-rbi/minimize-rbi.rb:12 - type-member(+) :::: -> T.attached_class (of ModuleCommonToBoth) @ test/cli/minimize-rbi/minimize-rbi.rb:12 + class :: < ::Module () @ test/cli/minimize-rbi/minimize-rbi.rb:12 method ::# () @ test/cli/minimize-rbi/minimize-rbi.rb:12 argument @ Loc {file=test/cli/minimize-rbi/minimize-rbi.rb start=??? end=???} module ::ModuleOnlyInFirst < ::Sorbet::Private::Static::ImplicitModuleSuperclass () @ test/cli/minimize-rbi/minimize-rbi.rb:11 - class ::[] < ::Module () @ test/cli/minimize-rbi/minimize-rbi.rb:11 - type-member(+) :::: -> T.attached_class (of ModuleOnlyInFirst) @ test/cli/minimize-rbi/minimize-rbi.rb:11 + class :: < ::Module () @ test/cli/minimize-rbi/minimize-rbi.rb:11 method ::# () @ test/cli/minimize-rbi/minimize-rbi.rb:11 argument @ Loc {file=test/cli/minimize-rbi/minimize-rbi.rb start=??? end=???} class ::OnlyInFirst < ::Object () @ test/cli/minimize-rbi/minimize-rbi.rb:3 @@ -45,13 +43,11 @@ class :: < ::Object () method ::# () @ test/cli/minimize-rbi/unknown.rbi:3 argument @ Loc {file=test/cli/minimize-rbi/unknown.rbi start=??? end=???} module ::ModuleCommonToBoth < ::Sorbet::Private::Static::ImplicitModuleSuperclass () @ test/cli/minimize-rbi/unknown.rbi:22 - class ::[] < ::Module () @ test/cli/minimize-rbi/unknown.rbi:22 - type-member(+) :::: -> T.attached_class (of ModuleCommonToBoth) @ test/cli/minimize-rbi/unknown.rbi:22 + class :: < ::Module () @ test/cli/minimize-rbi/unknown.rbi:22 method ::# () @ test/cli/minimize-rbi/unknown.rbi:22 argument @ Loc {file=test/cli/minimize-rbi/unknown.rbi start=??? end=???} module ::ModuleOnlyInSecond < ::Sorbet::Private::Static::ImplicitModuleSuperclass () @ test/cli/minimize-rbi/unknown.rbi:23 - class ::[] < ::Module () @ test/cli/minimize-rbi/unknown.rbi:23 - type-member(+) :::: -> T.attached_class (of ModuleOnlyInSecond) @ test/cli/minimize-rbi/unknown.rbi:23 + class :: < ::Module () @ test/cli/minimize-rbi/unknown.rbi:23 method ::# () @ test/cli/minimize-rbi/unknown.rbi:23 argument @ Loc {file=test/cli/minimize-rbi/unknown.rbi start=??? end=???} class ::OnlyInSecond < ::Object (ModuleCommonToBoth) @ test/cli/minimize-rbi/unknown.rbi:9 diff --git a/test/cli/package-autocorrect-missing-import/test.out b/test/cli/package-autocorrect-missing-import/test.out index 1c2178f7f4..7a1dcec77e 100644 --- a/test/cli/package-autocorrect-missing-import/test.out +++ b/test/cli/package-autocorrect-missing-import/test.out @@ -8,6 +8,13 @@ foo_class.rb:15: Unable to resolve constant `MyClass` https://srb.help/5002 https://github.com/sorbet/sorbet/tree/master/rbi/core/class.rbi#LCENSORED: `Class` defined here NN |class Class < Module ^^^^^^^^^^^^^^^^^^^^ + Autocorrect: Done + foo_class.rb:15: Replaced with `T::Class` + 15 | Foo::Bar::MyClass::SUBCLASSES # resolves via root + ^^^^^^^^^^^^^^^^^ + https://github.com/sorbet/sorbet/tree/master/rbi/sorbet/t.rbi#LCENSORED: `T::Class` defined here + NN |module T::Class + ^^^^^^^^^^^^^^^ Autocorrect: Done foo_class.rb:15: Replaced with `Digest::Class` 15 | Foo::Bar::MyClass::SUBCLASSES # resolves via root diff --git a/test/cli/package-disallow-enum-value-exports/my_package/__package.rb b/test/cli/package-disallow-enum-value-exports/my_package/__package.rb new file mode 100644 index 0000000000..b65ae1e602 --- /dev/null +++ b/test/cli/package-disallow-enum-value-exports/my_package/__package.rb @@ -0,0 +1,5 @@ +# typed: strict + +class MyPackage < PackageSpec + export MyPackage::A::Val1 # error +end diff --git a/test/cli/package-disallow-enum-value-exports/my_package/a.rb b/test/cli/package-disallow-enum-value-exports/my_package/a.rb new file mode 100644 index 0000000000..760e77a650 --- /dev/null +++ b/test/cli/package-disallow-enum-value-exports/my_package/a.rb @@ -0,0 +1,10 @@ +# typed: strict + +module MyPackage + class A < T::Enum + enums do + Val1 = new + Val2 = new + end + end +end diff --git a/test/cli/package-disallow-enum-value-exports/other_package/__package.rb b/test/cli/package-disallow-enum-value-exports/other_package/__package.rb new file mode 100644 index 0000000000..e28b618a00 --- /dev/null +++ b/test/cli/package-disallow-enum-value-exports/other_package/__package.rb @@ -0,0 +1,6 @@ +# typed: strict + +class OtherPackage < PackageSpec + import MyPackage +end + diff --git a/test/cli/package-disallow-enum-value-exports/other_package/b.rb b/test/cli/package-disallow-enum-value-exports/other_package/b.rb new file mode 100644 index 0000000000..c8e80cb6e1 --- /dev/null +++ b/test/cli/package-disallow-enum-value-exports/other_package/b.rb @@ -0,0 +1,8 @@ +# typed: strict + +module OtherPackage + module B + X = MyPackage::A::Val2 + end +end + diff --git a/test/cli/package-disallow-enum-value-exports/test.out b/test/cli/package-disallow-enum-value-exports/test.out new file mode 100644 index 0000000000..ef0f9785d8 --- /dev/null +++ b/test/cli/package-disallow-enum-value-exports/test.out @@ -0,0 +1,22 @@ +my_package/__package.rb:4: Cannot export enum value `MyPackage::A::Val1`. Instead, export the entire enum `MyPackage::A` https://srb.help/3721 + 4 | export MyPackage::A::Val1 # error + ^^^^^^^^^^^^^^^^^^^^^^^^^ + my_package/a.rb:6: Defined here + 6 | Val1 = new + ^^^^ + Autocorrect: Use `-a` to autocorrect + my_package/__package.rb:4: Replace with `export MyPackage::A` + 4 | export MyPackage::A::Val1 # error + ^^^^^^^^^^^^^^^^^^^^^^^^^ + +other_package/b.rb:5: `MyPackage::A::Val2` resolves but is not exported from `MyPackage` https://srb.help/3717 + 5 | X = MyPackage::A::Val2 + ^^^^^^^^^^^^^^^^^^ + my_package/a.rb:7: Defined here + 7 | Val2 = new + ^^^^ + Autocorrect: Use `-a` to autocorrect + my_package/__package.rb:4: Insert `export MyPackage::A` + 4 | export MyPackage::A::Val1 # error + ^ +Errors: 2 diff --git a/test/cli/package-disallow-enum-value-exports/test.sh b/test/cli/package-disallow-enum-value-exports/test.sh new file mode 100755 index 0000000000..01c1514c5e --- /dev/null +++ b/test/cli/package-disallow-enum-value-exports/test.sh @@ -0,0 +1,5 @@ +cd test/cli/package-disallow-enum-value-exports || exit 1 + +../../../main/sorbet --silence-dev-message --stripe-packages --max-threads=0 . 2>&1 + + diff --git a/test/cli/package-error-unresolved-export/test.out b/test/cli/package-error-unresolved-export/test.out index 71116acdad..3c5553173e 100644 --- a/test/cli/package-error-unresolved-export/test.out +++ b/test/cli/package-error-unresolved-export/test.out @@ -5,13 +5,6 @@ __package.rb:7: Unable to resolve constant `WildlyMisspelled` https://srb.help/5 __package.rb:8: Unable to resolve constant `NameWithTipo` https://srb.help/5002 8 | export Foo::BasePkg::NameWithTipo ^^^^^^^^^^^^^^^^^^^^^^^^^^ - Did you mean `Foo::Other::NameWithTipo`? Use `-a` to autocorrect - __package.rb:8: Replace with `Foo::Other::NameWithTipo` - 8 | export Foo::BasePkg::NameWithTipo - ^^^^^^^^^^^^^^^^^^^^^^^^^^ - other/other.rb:4: `Foo::Other::NameWithTipo` defined here - 4 | class NameWithTipo; end - ^^^^^^^^^^^^^^^^^^ Did you mean `Foo::BasePkg::NameWithTypo`? Use `-a` to autocorrect __package.rb:8: Replace with `Foo::BasePkg::NameWithTypo` 8 | export Foo::BasePkg::NameWithTipo diff --git a/test/cli/package-export-suggestion-boundary/example/__package.rb b/test/cli/package-export-suggestion-boundary/example/__package.rb new file mode 100644 index 0000000000..26e19feea5 --- /dev/null +++ b/test/cli/package-export-suggestion-boundary/example/__package.rb @@ -0,0 +1,8 @@ +# typed: strict + +class Some::Example < PackageSpec + export Some::Example::A + export Test::Some::Example::A + export Some::Exmpl + export So +end diff --git a/test/cli/package-export-suggestion-boundary/example/b.rb b/test/cli/package-export-suggestion-boundary/example/b.rb new file mode 100644 index 0000000000..57d426c9e6 --- /dev/null +++ b/test/cli/package-export-suggestion-boundary/example/b.rb @@ -0,0 +1,6 @@ +# typed: strict + +module Some::Example + class B + end +end diff --git a/test/cli/package-export-suggestion-boundary/example/test/b.rb b/test/cli/package-export-suggestion-boundary/example/test/b.rb new file mode 100644 index 0000000000..88c00314c2 --- /dev/null +++ b/test/cli/package-export-suggestion-boundary/example/test/b.rb @@ -0,0 +1,7 @@ +# typed: strict + +module Test::Some::Example + class B + end +end + diff --git a/test/cli/package-export-suggestion-boundary/example_2/__package.rb b/test/cli/package-export-suggestion-boundary/example_2/__package.rb new file mode 100644 index 0000000000..1142e86d2d --- /dev/null +++ b/test/cli/package-export-suggestion-boundary/example_2/__package.rb @@ -0,0 +1,4 @@ +# typed: strict + +class Some::Example2 < PackageSpec +end diff --git a/test/cli/package-export-suggestion-boundary/example_2/a.rb b/test/cli/package-export-suggestion-boundary/example_2/a.rb new file mode 100644 index 0000000000..464219206c --- /dev/null +++ b/test/cli/package-export-suggestion-boundary/example_2/a.rb @@ -0,0 +1,6 @@ +# typed: strict + +module Some::Example2 + class A + end +end diff --git a/test/cli/package-export-suggestion-boundary/example_2/test/a.rb b/test/cli/package-export-suggestion-boundary/example_2/test/a.rb new file mode 100644 index 0000000000..f7cc2188bd --- /dev/null +++ b/test/cli/package-export-suggestion-boundary/example_2/test/a.rb @@ -0,0 +1,8 @@ +# typed: strict + +module Test::Some::Example2 + class A + end +end + + diff --git a/test/cli/package-export-suggestion-boundary/test.out b/test/cli/package-export-suggestion-boundary/test.out new file mode 100644 index 0000000000..531e09add4 --- /dev/null +++ b/test/cli/package-export-suggestion-boundary/test.out @@ -0,0 +1,37 @@ +example/__package.rb:4: Unable to resolve constant `A` https://srb.help/5002 + 4 | export Some::Example::A + ^^^^^^^^^^^^^^^^ + Did you mean `Some::Example::B`? Use `-a` to autocorrect + example/__package.rb:4: Replace with `Some::Example::B` + 4 | export Some::Example::A + ^^^^^^^^^^^^^^^^ + example/b.rb:4: `Some::Example::B` defined here + 4 | class B + ^^^^^^^ + +example/__package.rb:5: Unable to resolve constant `A` https://srb.help/5002 + 5 | export Test::Some::Example::A + ^^^^^^^^^^^^^^^^^^^^^^ + Did you mean `Test::Some::Example::B`? Use `-a` to autocorrect + example/__package.rb:5: Replace with `Test::Some::Example::B` + 5 | export Test::Some::Example::A + ^^^^^^^^^^^^^^^^^^^^^^ + example/test/b.rb:4: `Test::Some::Example::B` defined here + 4 | class B + ^^^^^^^ + +example/__package.rb:6: Unable to resolve constant `Exmpl` https://srb.help/5002 + 6 | export Some::Exmpl + ^^^^^^^^^^^ + Did you mean `Some::Example`? Use `-a` to autocorrect + example/__package.rb:6: Replace with `Some::Example` + 6 | export Some::Exmpl + ^^^^^^^^^^^ + example/b.rb:3: `Some::Example` defined here + 3 |module Some::Example + ^^^^^^^^^^^^^^^^^^^^ + +example/__package.rb:7: Unable to resolve constant `So` https://srb.help/5002 + 7 | export So + ^^ +Errors: 4 diff --git a/test/cli/package-export-suggestion-boundary/test.sh b/test/cli/package-export-suggestion-boundary/test.sh new file mode 100755 index 0000000000..ca6f433759 --- /dev/null +++ b/test/cli/package-export-suggestion-boundary/test.sh @@ -0,0 +1,3 @@ +cd test/cli/package-export-suggestion-boundary || exit 1 + +../../../main/sorbet --silence-dev-message --stripe-packages --max-threads=0 . 2>&1 diff --git a/test/cli/package-file-table/bar/__package.rb b/test/cli/package-file-table/bar/__package.rb new file mode 100644 index 0000000000..959a95bd09 --- /dev/null +++ b/test/cli/package-file-table/bar/__package.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true +# typed: strict +# enable-packager: true + +class Project::Bar < PackageSpec + import Project::Foo + + export Project::Bar::BarClass + export Project::Bar::BarMethods +end diff --git a/test/cli/package-file-table/bar/bar.rb b/test/cli/package-file-table/bar/bar.rb new file mode 100644 index 0000000000..31670a9110 --- /dev/null +++ b/test/cli/package-file-table/bar/bar.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +# typed: strict + +module Project::Bar + class BarClass + extend T::Sig + sig { params(value: Integer).void } + def initialize(value) + @value = T.let(value, Integer) + end + end + + class UnexportedClass; end +end diff --git a/test/cli/package-file-table/bar/bar_methods.rb b/test/cli/package-file-table/bar/bar_methods.rb new file mode 100644 index 0000000000..61d2e61d3f --- /dev/null +++ b/test/cli/package-file-table/bar/bar_methods.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true +# typed: strict + +module Project::Bar + module BarMethods + extend T::Sig + + sig {returns(Project::Foo::FooClass)} + def self.build_foo + # Construct an imported class. + Project::Foo::FooClass.new(10) + end + + sig {returns(BarClass)} + def self.build_bar + # Call an imported method. + Project::Foo::FooMethods.build_bar + end + end +end diff --git a/test/cli/package-file-table/foo/__package.rb b/test/cli/package-file-table/foo/__package.rb new file mode 100644 index 0000000000..c4ddc21553 --- /dev/null +++ b/test/cli/package-file-table/foo/__package.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true +# typed: strict + +class Project::Foo < PackageSpec + import Project::Bar + + export Project::Foo::FooClass + export Project::Foo::FooMethods +end diff --git a/test/cli/package-file-table/foo/foo.rb b/test/cli/package-file-table/foo/foo.rb new file mode 100644 index 0000000000..5ac0b5b222 --- /dev/null +++ b/test/cli/package-file-table/foo/foo.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# typed: strict + +module Project::Foo + class FooClass + extend T::Sig + sig { params(value: Integer).void } + def initialize(value) + @value = T.let(value, Integer) + end + + Project::Bar::BardClass + Project::Bar::UnexportedClass + end +end diff --git a/test/cli/package-file-table/foo/foo_methods.rb b/test/cli/package-file-table/foo/foo_methods.rb new file mode 100644 index 0000000000..985e8f34bb --- /dev/null +++ b/test/cli/package-file-table/foo/foo_methods.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true +# typed: strict + +module Project::Foo + module FooMethods + extend T::Sig + + sig {returns(Project::Bar::BarClass)} + def self.build_bar + # Construct an imported class. + Project::Bar::BarClass.new(10) + end + + sig {returns(FooClass)} + def self.build_foo + # Call an imported method. + Project::Bar::BarMethods.build_foo + end + end +end diff --git a/test/cli/package-file-table/test.out b/test/cli/package-file-table/test.out new file mode 100644 index 0000000000..90cf98e533 --- /dev/null +++ b/test/cli/package-file-table/test.out @@ -0,0 +1,68 @@ +foo/foo.rb:13: Unable to resolve constant `BardClass` https://srb.help/5002 + 13 | Project::Bar::BardClass + ^^^^^^^^^^^^^^^^^^^^^^^ + Did you mean `Project::Bar::BarClass`? Use `-a` to autocorrect + foo/foo.rb:13: Replace with `Project::Bar::BarClass` + 13 | Project::Bar::BardClass + ^^^^^^^^^^^^^^^^^^^^^^^ + bar/bar.rb:6: `Project::Bar::BarClass` defined here + 6 | class BarClass + ^^^^^^^^^^^^^^ + +foo/foo.rb:14: `Project::Bar::UnexportedClass` resolves but is not exported from `Project::Bar` https://srb.help/3717 + 14 | Project::Bar::UnexportedClass + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + bar/bar.rb:14: Defined here + 14 | class UnexportedClass; end + ^^^^^^^^^^^^^^^^^^^^^ + Autocorrect: Use `-a` to autocorrect + bar/__package.rb:9: Insert `export Project::Bar::UnexportedClass` + 9 | export Project::Bar::BarMethods + ^ +Errors: 2 +{ + "files": [ + { + "path": "./bar/__package.rb", + "sigil": "Strict", + "strict": "Strict", + "min_error_level": "Max", + "pkg": "Project::Bar" + }, + { + "path": "./bar/bar.rb", + "sigil": "Strict", + "strict": "Strict", + "min_error_level": "Max", + "pkg": "Project::Bar" + }, + { + "path": "./bar/bar_methods.rb", + "sigil": "Strict", + "strict": "Strict", + "min_error_level": "Max", + "pkg": "Project::Bar" + }, + { + "path": "./foo/__package.rb", + "sigil": "Strict", + "strict": "Strict", + "min_error_level": "Max", + "pkg": "Project::Foo" + }, + { + "path": "./foo/foo.rb", + "sigil": "Strict", + "strict": "Strict", + "min_error_level": "False", + "pkg": "Project::Foo" + }, + { + "path": "./foo/foo_methods.rb", + "sigil": "Strict", + "strict": "Strict", + "min_error_level": "Max", + "pkg": "Project::Foo" + } + ] +} diff --git a/test/cli/package-file-table/test.sh b/test/cli/package-file-table/test.sh new file mode 100755 index 0000000000..b1531de128 --- /dev/null +++ b/test/cli/package-file-table/test.sh @@ -0,0 +1,3 @@ +cd test/cli/package-file-table || exit 1 + +../../../main/sorbet --print=file-table-json --silence-dev-message --stripe-packages --max-threads=0 . 2>&1 diff --git a/test/cli/package-implicit-parent-namespace-export/my_package/__package.rb b/test/cli/package-implicit-parent-namespace-export/my_package/__package.rb new file mode 100644 index 0000000000..fb3f3bd182 --- /dev/null +++ b/test/cli/package-implicit-parent-namespace-export/my_package/__package.rb @@ -0,0 +1,8 @@ +# typed: strict + +class MyPackage < PackageSpec + export MyPackage::ParentNamespace::Const1 + export MyPackage::ParentNamespace::Const2 + export MyPackage::BehaviorDefiningParentNamespace::Const1 +end + diff --git a/test/cli/package-implicit-parent-namespace-export/my_package/constants.rb b/test/cli/package-implicit-parent-namespace-export/my_package/constants.rb new file mode 100644 index 0000000000..93467faa64 --- /dev/null +++ b/test/cli/package-implicit-parent-namespace-export/my_package/constants.rb @@ -0,0 +1,19 @@ +# typed: strict + +module MyPackage + module ParentNamespace + Const1 = 1 + Const2 = 2 + PrivateConst3 = 3 + end + + module BehaviorDefiningParentNamespace + extend T::Sig + + sig {void} + def behavior + end + + Const1 = 4 + end +end diff --git a/test/cli/package-implicit-parent-namespace-export/other_package/__package.rb b/test/cli/package-implicit-parent-namespace-export/other_package/__package.rb new file mode 100644 index 0000000000..e28b618a00 --- /dev/null +++ b/test/cli/package-implicit-parent-namespace-export/other_package/__package.rb @@ -0,0 +1,6 @@ +# typed: strict + +class OtherPackage < PackageSpec + import MyPackage +end + diff --git a/test/cli/package-implicit-parent-namespace-export/other_package/reference.rb b/test/cli/package-implicit-parent-namespace-export/other_package/reference.rb new file mode 100644 index 0000000000..8fdfcdbdbb --- /dev/null +++ b/test/cli/package-implicit-parent-namespace-export/other_package/reference.rb @@ -0,0 +1,13 @@ +# typed: strict + +module OtherPackage + MPP = MyPackage::ParentNamespace # Allowed due to implicit parent namespace exporting + + MPP::Const1 + MPP::Const2 + MPP::PrivateConst3 # Should be an ERROR! Private constant. + + BPP = MyPackage::BehaviorDefiningParentNamespace # should be an ERROR! Not allowed since namespace defines behavior. + + BPP::Const1 # This is ok since MyPackage::BehaviorDefiningParentNamespace::Const1 is explicitly exported :) +end diff --git a/test/cli/package-implicit-parent-namespace-export/test.out b/test/cli/package-implicit-parent-namespace-export/test.out new file mode 100644 index 0000000000..db3e1c7cf5 --- /dev/null +++ b/test/cli/package-implicit-parent-namespace-export/test.out @@ -0,0 +1,22 @@ +other_package/reference.rb:8: `MyPackage::ParentNamespace::PrivateConst3` resolves but is not exported from `MyPackage` https://srb.help/3717 + 8 | MPP::PrivateConst3 # Should be an ERROR! Private constant. + ^^^^^^^^^^^^^^^^^^ + my_package/constants.rb:7: Defined here + 7 | PrivateConst3 = 3 + ^^^^^^^^^^^^^ + Autocorrect: Use `-a` to autocorrect + my_package/__package.rb:6: Insert `export MyPackage::ParentNamespace::PrivateConst3` + 6 | export MyPackage::BehaviorDefiningParentNamespace::Const1 + ^ + +other_package/reference.rb:10: `MyPackage::BehaviorDefiningParentNamespace` resolves but is not exported from `MyPackage` https://srb.help/3717 + 10 | BPP = MyPackage::BehaviorDefiningParentNamespace # should be an ERROR! Not allowed since namespace defines behavior. + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + my_package/constants.rb:10: Defined here + 10 | module BehaviorDefiningParentNamespace + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Autocorrect: Use `-a` to autocorrect + my_package/__package.rb:6: Insert `export MyPackage::BehaviorDefiningParentNamespace` + 6 | export MyPackage::BehaviorDefiningParentNamespace::Const1 + ^ +Errors: 2 diff --git a/test/cli/package-implicit-parent-namespace-export/test.sh b/test/cli/package-implicit-parent-namespace-export/test.sh new file mode 100755 index 0000000000..29c108cb8e --- /dev/null +++ b/test/cli/package-implicit-parent-namespace-export/test.sh @@ -0,0 +1,3 @@ +cd test/cli/package-implicit-parent-namespace-export || exit 1 + +../../../main/sorbet --silence-dev-message --stripe-packages --max-threads=0 . 2>&1 diff --git a/test/cli/package-prefix-enforcement/test.out b/test/cli/package-prefix-enforcement/test.out index 57a40f0ed2..00f722ff98 100644 --- a/test/cli/package-prefix-enforcement/test.out +++ b/test/cli/package-prefix-enforcement/test.out @@ -5,23 +5,44 @@ nested/nested.rb:5: File belongs to package `Root::Nested` but defines a constan 3 |class Root::Nested < PackageSpec ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -nested/nested.rb:40: Class or method behavior may not be defined outside of the enclosing package namespace `Root::Nested` https://srb.help/3713 +nested/nested.rb:40: This file must only define behavior in enclosing package `Root::Nested` https://srb.help/3713 40 | sig {returns(NilClass)} ^^^^^^^^^^^^^^^^^^^^^^^ - Note: - Attempting to define class or method behavior in package namespace `Root` + nested/nested.rb:36: Defining behavior in `Root` instead: + 36 |module Root + ^^^^ + nested/__package.rb:3: Enclosing package `Root::Nested` declared here + 3 |class Root::Nested < PackageSpec + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + __package.rb:3: Package `Root` declared here + 3 |class Root < PackageSpec + ^^^^^^^^^^^^^^^^^^^^^^^^ -nested/nested.rb:41: Class or method behavior may not be defined outside of the enclosing package namespace `Root::Nested` https://srb.help/3713 +nested/nested.rb:41: This file must only define behavior in enclosing package `Root::Nested` https://srb.help/3713 41 | def self.method ^^^^^^^^^^^^^^^ - Note: - Attempting to define class or method behavior in package namespace `Root` + nested/nested.rb:36: Defining behavior in `Root` instead: + 36 |module Root + ^^^^ + nested/__package.rb:3: Enclosing package `Root::Nested` declared here + 3 |class Root::Nested < PackageSpec + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + __package.rb:3: Package `Root` declared here + 3 |class Root < PackageSpec + ^^^^^^^^^^^^^^^^^^^^^^^^ -nested/nested.rb:37: Class or method behavior may not be defined outside of the enclosing package namespace `Root::Nested` https://srb.help/3713 +nested/nested.rb:37: This file must only define behavior in enclosing package `Root::Nested` https://srb.help/3713 37 | extend T::Sig ^^^^^^^^^^^^^ - Note: - Attempting to define class or method behavior in package namespace `Root` + nested/nested.rb:36: Defining behavior in `Root` instead: + 36 |module Root + ^^^^ + nested/__package.rb:3: Enclosing package `Root::Nested` declared here + 3 |class Root::Nested < PackageSpec + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + __package.rb:3: Package `Root` declared here + 3 |class Root < PackageSpec + ^^^^^^^^^^^^^^^^^^^^^^^^ nested/nested.rb:38: File belongs to package `Root::Nested` but defines a constant that does not match this namespace https://srb.help/3713 38 | NOT_IN_PACKAGE = T.let(1, Integer) diff --git a/test/cli/package-skip-import-visibility-check-for/__package.rb b/test/cli/package-skip-import-visibility-check-for/__package.rb new file mode 100644 index 0000000000..387e0aa83d --- /dev/null +++ b/test/cli/package-skip-import-visibility-check-for/__package.rb @@ -0,0 +1,6 @@ +# typed: strict + +class Project::Root < PackageSpec + import Project::A +end + diff --git a/test/cli/package-skip-import-visibility-check-for/a/__package.rb b/test/cli/package-skip-import-visibility-check-for/a/__package.rb new file mode 100644 index 0000000000..6fef00f89b --- /dev/null +++ b/test/cli/package-skip-import-visibility-check-for/a/__package.rb @@ -0,0 +1,5 @@ +# typed: strict + +class Project::A < PackageSpec + visible_to Project::B +end diff --git a/test/cli/package-skip-import-visibility-check-for/b/__package.rb b/test/cli/package-skip-import-visibility-check-for/b/__package.rb new file mode 100644 index 0000000000..adbe5ec47f --- /dev/null +++ b/test/cli/package-skip-import-visibility-check-for/b/__package.rb @@ -0,0 +1,5 @@ +# typed: strict + +class Project::B < PackageSpec + import Project::A +end diff --git a/test/cli/package-skip-import-visibility-check-for/test.out b/test/cli/package-skip-import-visibility-check-for/test.out new file mode 100644 index 0000000000..f6b138871e --- /dev/null +++ b/test/cli/package-skip-import-visibility-check-for/test.out @@ -0,0 +1 @@ +No errors! Great job. diff --git a/test/cli/package-skip-import-visibility-check-for/test.sh b/test/cli/package-skip-import-visibility-check-for/test.sh new file mode 100755 index 0000000000..61eb08e03b --- /dev/null +++ b/test/cli/package-skip-import-visibility-check-for/test.sh @@ -0,0 +1,5 @@ +cd test/cli/package-skip-import-visibility-check-for || exit 1 + +../../../main/sorbet --silence-dev-message --stripe-packages --skip-package-import-visibility-check-for=Project::Root --max-threads=0 . 2>&1 + + diff --git a/test/cli/package-special-allowed-dsls/test.out b/test/cli/package-special-allowed-dsls/test.out index e0385cfe4e..a5c6b1a3b8 100644 --- a/test/cli/package-special-allowed-dsls/test.out +++ b/test/cli/package-special-allowed-dsls/test.out @@ -2,7 +2,7 @@ baz/__package.rb:4: Argument to `autoloader_compatibility` must be a string lite 4 | autoloader_compatibility :invalid ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -baz/__package.rb:5: Argument to `autoloader_compatibility` must be either 'strict' or 'legacy' https://srb.help/3706 +baz/__package.rb:5: Argument to `autoloader_compatibility` can only be 'legacy' https://srb.help/3706 5 | autoloader_compatibility 'something' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,4 +13,10 @@ baz/__package.rb:6: Argument to `autoloader_compatibility` must be a string lite baz/__package.rb:6: Replace with `"legacy"` 6 | autoloader_compatibility :legacy ^^^^^^^ -Errors: 3 + +foo/__package.rb:7: The 'strict' argument has been deprecated as an argument to `autoloader_compatibility` https://srb.help/3706 + 7 | autoloader_compatibility 'strict' + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Note: + If you wish to mark your package as strictly path-based-autoloading compatible, do not provide an autoloader_compatibility annotation +Errors: 4 diff --git a/test/cli/package-special-allowed-dsls/test.sh b/test/cli/package-special-allowed-dsls/test.sh index 8dadd25dc1..ec6a65fb86 100755 --- a/test/cli/package-special-allowed-dsls/test.sh +++ b/test/cli/package-special-allowed-dsls/test.sh @@ -1,4 +1,4 @@ cd test/cli/package-special-allowed-dsls || exit 0 -../../../main/sorbet --silence-dev-message --stripe-packages . 2>&1 +../../../main/sorbet --silence-dev-message --stripe-packages --max-threads=0 . 2>&1 diff --git a/test/cli/packager_did_you_mean/__package.rb b/test/cli/packager_did_you_mean/__package.rb new file mode 100644 index 0000000000..8618bf6c4d --- /dev/null +++ b/test/cli/packager_did_you_mean/__package.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true +# typed: strict +# enable-packager: true + +class ProjectWithLongDiscernableName::Foo < PackageSpec + export ProjectWithLongDiscernableName::Foo::Example +end diff --git a/test/cli/packager_did_you_mean/test.out b/test/cli/packager_did_you_mean/test.out new file mode 100644 index 0000000000..9df402f9c7 --- /dev/null +++ b/test/cli/packager_did_you_mean/test.out @@ -0,0 +1,4 @@ +__package.rb:6: Unable to resolve constant `ProjectWithLongDiscernableName` https://srb.help/5002 + 6 | export ProjectWithLongDiscernableName::Foo::Example + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Errors: 1 diff --git a/test/cli/packager_did_you_mean/test.sh b/test/cli/packager_did_you_mean/test.sh new file mode 100755 index 0000000000..4d0e59727f --- /dev/null +++ b/test/cli/packager_did_you_mean/test.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -euo pipefail + +cd test/cli/packager_did_you_mean + +if ../../../main/sorbet --silence-dev-message --stripe-packages --max-threads=0 . 2>&1 ; then + echo "Expected to fail, but passed!" + exit 1 +fi diff --git a/test/cli/packager_export_self/test.out b/test/cli/packager_export_self/test.out index 6b23294711..210fad491b 100644 --- a/test/cli/packager_export_self/test.out +++ b/test/cli/packager_export_self/test.out @@ -5,13 +5,6 @@ test/cli/packager_export_self/__package.rb:3: Invalid expression in package: Arg test/cli/packager_export_self/__package.rb:3: Unable to resolve constant `Opus` https://srb.help/5002 3 | export_for_test Opus::Deploy ^^^^ - Did you mean `Opus`? Use `-a` to autocorrect - test/cli/packager_export_self/__package.rb:3: Replace with `Opus` - 3 | export_for_test Opus::Deploy - ^^^^ - test/cli/packager_export_self/__package.rb:2: `Opus` defined here - 2 |class Opus::Deploy < PackageSpec - ^^^^ test/cli/packager_export_self/__package.rb:3: Method `export_for_test` does not exist on `T.class_of(Opus::Deploy)` https://srb.help/7003 3 | export_for_test Opus::Deploy diff --git a/test/cli/print_generics/test.out b/test/cli/print_generics/test.out index 39b2faa1b7..378385996c 100644 --- a/test/cli/print_generics/test.out +++ b/test/cli/print_generics/test.out @@ -2,7 +2,8 @@ test/cli/print_generics/print_generics.rb:5: The method `foo` does not have a `s 5 |def foo(x) ^^^^^^^^^^ Autocorrect: Use `-a` to autocorrect - test/cli/print_generics/print_generics.rb:5: Insert `sig { params(x: T.untyped).returns(T::Array[Integer]) }` + test/cli/print_generics/print_generics.rb:5: Insert `extend T::Sig + sig { params(x: T.untyped).returns(T::Array[Integer]) }` 5 |def foo(x) ^ @@ -10,7 +11,8 @@ test/cli/print_generics/print_generics.rb:9: The method `bar` does not have a `s 9 |def bar(x) ^^^^^^^^^^ Autocorrect: Use `-a` to autocorrect - test/cli/print_generics/print_generics.rb:9: Insert `sig { params(x: T.untyped).returns(T::Hash[Symbol, Integer]) }` + test/cli/print_generics/print_generics.rb:9: Insert `extend T::Sig + sig { params(x: T.untyped).returns(T::Hash[Symbol, Integer]) }` 9 |def bar(x) ^ @@ -18,7 +20,8 @@ test/cli/print_generics/print_generics.rb:13: The method `qux` does not have a ` 13 |def qux(x) ^^^^^^^^^^ Autocorrect: Use `-a` to autocorrect - test/cli/print_generics/print_generics.rb:13: Insert `sig { params(x: T.untyped).returns(T::Enumerable[Integer]) }` + test/cli/print_generics/print_generics.rb:13: Insert `extend T::Sig + sig { params(x: T.untyped).returns(T::Enumerable[Integer]) }` 13 |def qux(x) ^ @@ -26,7 +29,8 @@ test/cli/print_generics/print_generics.rb:17: The method `wub` does not have a ` 17 |def wub(x) ^^^^^^^^^^ Autocorrect: Use `-a` to autocorrect - test/cli/print_generics/print_generics.rb:17: Insert `sig { params(x: T.untyped).returns(T::Range[Integer]) }` + test/cli/print_generics/print_generics.rb:17: Insert `extend T::Sig + sig { params(x: T.untyped).returns(T::Range[Integer]) }` 17 |def wub(x) ^ @@ -34,7 +38,8 @@ test/cli/print_generics/print_generics.rb:21: The method `zan` does not have a ` 21 |def zan(x) ^^^^^^^^^^ Autocorrect: Use `-a` to autocorrect - test/cli/print_generics/print_generics.rb:21: Insert `sig { params(x: T.untyped).returns(T::Set[Integer]) }` + test/cli/print_generics/print_generics.rb:21: Insert `extend T::Sig + sig { params(x: T.untyped).returns(T::Set[Integer]) }` 21 |def zan(x) ^ @@ -42,7 +47,8 @@ test/cli/print_generics/print_generics.rb:25: The method `gaz` does not have a ` 25 |def gaz(x) ^^^^^^^^^^ Autocorrect: Use `-a` to autocorrect - test/cli/print_generics/print_generics.rb:25: Insert `sig { params(x: T.untyped).returns(T::Enumerator[Integer]) }` + test/cli/print_generics/print_generics.rb:25: Insert `extend T::Sig + sig { params(x: T.untyped).returns(T::Enumerator[Integer]) }` 25 |def gaz(x) ^ Errors: 6 diff --git a/test/cli/suggest-class-new-not-singleton/test.out b/test/cli/suggest-class-new-not-singleton/test.out index 9bada71e45..95d316aad7 100644 --- a/test/cli/suggest-class-new-not-singleton/test.out +++ b/test/cli/suggest-class-new-not-singleton/test.out @@ -17,12 +17,8 @@ suggest-class-new-not-singleton.rb:4: Call to method `new` on `T.class_of(Intege suggest-class-new-not-singleton.rb:5: Call to method `new` on `T.class_of(T::Array)` mistakes a type for a value https://srb.help/7030 5 |T.class_of(T::Array).new ^^^ - Autocorrect: Done - suggest-class-new-not-singleton.rb:5: Replaced with `T::Array` - 5 |T.class_of(T::Array).new - ^^^^^^^^^^^^^^^^^^^^ -suggest-class-new-not-singleton.rb:6: Call to method `new` on `T.class_of(Array)` mistakes a type for a value https://srb.help/7030 +suggest-class-new-not-singleton.rb:6: Call to method `new` on `T.class_of(Array)[T::Array[T.untyped]]` mistakes a type for a value https://srb.help/7030 6 |T.class_of(T::Array[String]).new ^^^ Autocorrect: Done @@ -45,7 +41,7 @@ Errors: 5 String.new Integer.new -T::Array.new +T.class_of(T::Array).new Array.new x = T.class_of(String) String.new diff --git a/test/cli/suggest-sig-literal/test.out b/test/cli/suggest-sig-literal/test.out index f8372e1611..68722e47b7 100644 --- a/test/cli/suggest-sig-literal/test.out +++ b/test/cli/suggest-sig-literal/test.out @@ -2,7 +2,8 @@ suggest-sig-literal.rb:2: The method `index_for_live` does not have a `sig` http 2 |def index_for_live(fields) ^^^^^^^^^^^^^^^^^^^^^^^^^^ Autocorrect: Done - suggest-sig-literal.rb:2: Inserted `sig { params(fields: T.untyped).returns(T::Array[T::Array[T.any(Integer, Symbol)]]) }` + suggest-sig-literal.rb:2: Inserted `extend T::Sig + sig { params(fields: T.untyped).returns(T::Array[T.untyped]) }` 2 |def index_for_live(fields) ^ Errors: 1 @@ -10,7 +11,8 @@ Errors: 1 -------------------------------------------------------------------------- # typed: strict -sig { params(fields: T.untyped).returns(T::Array[T::Array[T.any(Integer, Symbol)]]) } +extend T::Sig +sig { params(fields: T.untyped).returns(T::Array[T.untyped]) } def index_for_live(fields) [[:deleted_at, 1]] + fields end diff --git a/test/cli/suggest-sig/test.out b/test/cli/suggest-sig/test.out index c347d2b5f6..8a14ea8891 100644 --- a/test/cli/suggest-sig/test.out +++ b/test/cli/suggest-sig/test.out @@ -36,7 +36,8 @@ suggest-sig.rb:5: The method `hazTwoArgs` does not have a `sig` https://srb.help 5 |def hazTwoArgs(a, b); 1; end; ^^^^^^^^^^^^^^^^^^^^ Autocorrect: Done - suggest-sig.rb:5: Inserted `sig { params(a: T.untyped, b: T.untyped).returns(Integer) }` + suggest-sig.rb:5: Inserted `extend T::Sig + sig { params(a: T.untyped, b: T.untyped).returns(Integer) }` 5 |def hazTwoArgs(a, b); 1; end; ^ @@ -48,7 +49,8 @@ suggest-sig.rb:7: The method `baz` does not have a `sig` https://srb.help/7017 7 |def baz ^^^^^^^ Autocorrect: Done - suggest-sig.rb:7: Inserted `sig { returns(T.any(T::Array[T.untyped], String)) }` + suggest-sig.rb:7: Inserted `extend T::Sig + sig { returns(T.any(T::Array[T.untyped], String)) }` 7 |def baz ^ @@ -56,7 +58,8 @@ suggest-sig.rb:18: The method `bla` does not have a `sig` https://srb.help/7017 18 |def bla; give_me_void; end ^^^^^^^ Autocorrect: Done - suggest-sig.rb:18: Inserted `sig { void }` + suggest-sig.rb:18: Inserted `extend T::Sig + sig { void }` 18 |def bla; give_me_void; end ^ @@ -68,7 +71,8 @@ suggest-sig.rb:20: The method `bbq` does not have a `sig` https://srb.help/7017 20 |def bbq ^^^^^^^ Autocorrect: Done - suggest-sig.rb:20: Inserted `sig { void }` + suggest-sig.rb:20: Inserted `extend T::Sig + sig { void }` 20 |def bbq ^ @@ -80,7 +84,8 @@ suggest-sig.rb:30: The method `give_me_literal` does not have a `sig` https://sr 30 |def give_me_literal; 1; end; ^^^^^^^^^^^^^^^^^^^ Autocorrect: Done - suggest-sig.rb:30: Inserted `sig { returns(Integer) }` + suggest-sig.rb:30: Inserted `extend T::Sig + sig { returns(Integer) }` 30 |def give_me_literal; 1; end; ^ @@ -88,7 +93,8 @@ suggest-sig.rb:32: The method `give_me_literal_nested` does not have a `sig` htt 32 |def give_me_literal_nested; [[1]]; end; ^^^^^^^^^^^^^^^^^^^^^^^^^^ Autocorrect: Done - suggest-sig.rb:32: Inserted `sig { returns(T::Array[T::Array[Integer]]) }` + suggest-sig.rb:32: Inserted `extend T::Sig + sig { returns(T::Array[T::Array[Integer]]) }` 32 |def give_me_literal_nested; [[1]]; end; ^ @@ -96,7 +102,8 @@ suggest-sig.rb:34: The method `root_private` does not have a `sig` https://srb.h 34 |private def root_private; end ^^^^^^^^^^^^^^^^ Autocorrect: Done - suggest-sig.rb:34: Inserted `sig { returns(NilClass) }` + suggest-sig.rb:34: Inserted `extend T::Sig + sig { returns(NilClass) }` 34 |private def root_private; end ^ @@ -104,7 +111,8 @@ suggest-sig.rb:36: The method `root_protected` does not have a `sig` https://srb 36 |protected def root_protected; end ^^^^^^^^^^^^^^^^^^ Autocorrect: Done - suggest-sig.rb:36: Inserted `sig { returns(NilClass) }` + suggest-sig.rb:36: Inserted `extend T::Sig + sig { returns(NilClass) }` 36 |protected def root_protected; end ^ @@ -112,7 +120,8 @@ suggest-sig.rb:46: The method `foo` does not have a `sig` https://srb.help/7017 46 |def foo(a) ^^^^^^^^^^ Autocorrect: Done - suggest-sig.rb:46: Inserted `sig { params(a: Integer).returns(Integer) }` + suggest-sig.rb:46: Inserted `extend T::Sig + sig { params(a: Integer).returns(Integer) }` 46 |def foo(a) ^ @@ -120,7 +129,8 @@ suggest-sig.rb:56: The method `fooCond` does not have a `sig` https://srb.help/7 56 |def fooCond(a, cond) ^^^^^^^^^^^^^^^^^^^^ Autocorrect: Done - suggest-sig.rb:56: Inserted `sig { params(a: T.any(Integer, String), cond: T.untyped).void }` + suggest-sig.rb:56: Inserted `extend T::Sig + sig { params(a: T.any(Integer, String), cond: T.untyped).void }` 56 |def fooCond(a, cond) ^ @@ -128,7 +138,8 @@ suggest-sig.rb:64: The method `fooWhile` does not have a `sig` https://srb.help/ 64 |def fooWhile(a, cond1, cond2) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Autocorrect: Done - suggest-sig.rb:64: Inserted `sig { params(a: T.any(Integer, String), cond1: T.untyped, cond2: T.untyped).returns(NilClass) }` + suggest-sig.rb:64: Inserted `extend T::Sig + sig { params(a: T.any(Integer, String), cond1: T.untyped, cond2: T.untyped).returns(NilClass) }` 64 |def fooWhile(a, cond1, cond2) ^ @@ -136,7 +147,8 @@ suggest-sig.rb:74: The method `takesBlock` does not have a `sig` https://srb.hel 74 |def takesBlock ^^^^^^^^^^^^^^ Autocorrect: Done - suggest-sig.rb:74: Inserted `sig { returns(Integer) }` + suggest-sig.rb:74: Inserted `extend T::Sig + sig { returns(Integer) }` 74 |def takesBlock ^ @@ -144,7 +156,8 @@ suggest-sig.rb:79: The method `list_ints_or_empty_list` does not have a `sig` ht 79 |def list_ints_or_empty_list ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Autocorrect: Done - suggest-sig.rb:79: Inserted `sig { returns(T::Array[T.untyped]) }` + suggest-sig.rb:79: Inserted `extend T::Sig + sig { returns(T::Array[T.untyped]) }` 79 |def list_ints_or_empty_list ^ @@ -188,7 +201,8 @@ suggest-sig.rb:84: The method `dead` does not have a `sig` https://srb.help/7017 84 |def dead(x) ^^^^^^^^^^^ Autocorrect: Done - suggest-sig.rb:84: Inserted `sig { params(x: Integer).void }` + suggest-sig.rb:84: Inserted `extend T::Sig + sig { params(x: Integer).void }` 84 |def dead(x) ^ @@ -196,7 +210,8 @@ suggest-sig.rb:92: The method `with_block` does not have a `sig` https://srb.hel 92 |def with_block ^^^^^^^^^^^^^^ Autocorrect: Done - suggest-sig.rb:92: Inserted `sig { returns(NilClass) }` + suggest-sig.rb:92: Inserted `extend T::Sig + sig { returns(NilClass) }` 92 |def with_block ^ @@ -212,7 +227,8 @@ suggest-sig.rb:118: The method `cantRun` does not have a `sig` https://srb.help/ 118 |def cantRun(a) ^^^^^^^^^^^^^^ Autocorrect: Done - suggest-sig.rb:118: Inserted `sig { params(a: T.untyped).returns(Integer) }` + suggest-sig.rb:118: Inserted `extend T::Sig + sig { params(a: T.untyped).returns(Integer) }` 118 |def cantRun(a) ^ @@ -220,7 +236,8 @@ suggest-sig.rb:155: The method `explicitly_named_block_parameter` does not have 155 |def explicitly_named_block_parameter(&blk) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Autocorrect: Done - suggest-sig.rb:155: Inserted `sig { params(blk: T.untyped).returns(Integer) }` + suggest-sig.rb:155: Inserted `extend T::Sig + sig { params(blk: T.untyped).returns(Integer) }` 155 |def explicitly_named_block_parameter(&blk) ^ @@ -376,9 +393,11 @@ Errors: 47 extend T::Sig +extend T::Sig sig { params(a: T.untyped, b: T.untyped).returns(Integer) } def hazTwoArgs(a, b); 1; end; +extend T::Sig sig { returns(T.any(T::Array[T.untyped], String)) } def baz if someCondition @@ -391,9 +410,11 @@ end sig {void} def give_me_void; end +extend T::Sig sig { void } def bla; give_me_void; end +extend T::Sig sig { void } def bbq if someCondition @@ -405,15 +426,19 @@ end def idk(a); a / a + a * a; end +extend T::Sig sig { returns(Integer) } def give_me_literal; 1; end; +extend T::Sig sig { returns(T::Array[T::Array[Integer]]) } def give_me_literal_nested; [[1]]; end; +extend T::Sig sig { returns(NilClass) } private def root_private; end +extend T::Sig sig { returns(NilClass) } protected def root_protected; end @@ -427,6 +452,7 @@ class A protected def a_protected; end end +extend T::Sig sig { params(a: Integer).returns(Integer) } def foo(a) 1 + a @@ -438,6 +464,7 @@ def takesInt(a); end; sig {params(a: String).void} def takesString(a); end; +extend T::Sig sig { params(a: T.any(Integer, String), cond: T.untyped).void } def fooCond(a, cond) if cond @@ -447,6 +474,7 @@ def fooCond(a, cond) end end +extend T::Sig sig { params(a: T.any(Integer, String), cond1: T.untyped, cond2: T.untyped).returns(NilClass) } def fooWhile(a, cond1, cond2) while cond2 @@ -458,18 +486,21 @@ def fooWhile(a, cond1, cond2) end end +extend T::Sig sig { returns(Integer) } def takesBlock yield 1 2 end +extend T::Sig sig { returns(T::Array[T.untyped]) } def list_ints_or_empty_list x = T.let(1, T.nilable(Integer)) x.nil? ? [x] : [] end +extend T::Sig sig { params(x: Integer).void } def dead(x) if true || qux || blah @@ -479,6 +510,7 @@ def dead(x) end end +extend T::Sig sig { returns(NilClass) } def with_block yield @@ -508,6 +540,7 @@ class TestCarash end end +extend T::Sig sig { params(a: T.untyped).returns(Integer) } def cantRun(a) takesInt(a) @@ -546,6 +579,7 @@ class Abstract def abstract_foo(a); end end +extend T::Sig sig { params(blk: T.untyped).returns(Integer) } def explicitly_named_block_parameter(&blk) 42 diff --git a/test/cli/symbol-table/test.out b/test/cli/symbol-table/test.out index 6fcb586e14..63507fb381 100644 --- a/test/cli/symbol-table/test.out +++ b/test/cli/symbol-table/test.out @@ -7,12 +7,9 @@ class :: < ::Object () module ::Net::SSH < ::Sorbet::Private::Static::ImplicitModuleSuperclass () @ test/cli/symbol-table/symbol-table.rb:2 module ::Net::SSH::Authentication < ::Sorbet::Private::Static::ImplicitModuleSuperclass () @ test/cli/symbol-table/symbol-table.rb:2 module ::Net::SSH::Authentication::CustomModule < ::Sorbet::Private::Static::ImplicitModuleSuperclass () @ test/cli/symbol-table/symbol-table.rb:2 - class ::Net::SSH::Authentication::[] < ::Module () @ test/cli/symbol-table/symbol-table.rb:2 - type-member(+) ::Net::SSH::Authentication:::: -> T.attached_class (of Net::SSH::Authentication::CustomModule) @ test/cli/symbol-table/symbol-table.rb:2 + class ::Net::SSH::Authentication:: < ::Module () @ test/cli/symbol-table/symbol-table.rb:2 method ::Net::SSH::Authentication::# () @ test/cli/symbol-table/symbol-table.rb:2 argument @ Loc {file=test/cli/symbol-table/symbol-table.rb start=??? end=???} - class ::Net::SSH::[] < ::Module () @ test/cli/symbol-table/symbol-table.rb:2 - type-member(+) ::Net::SSH:::: -> T.attached_class (of Net::SSH::Authentication) @ test/cli/symbol-table/symbol-table.rb:2 - class ::Net::[] < ::Module () @ test/cli/symbol-table/symbol-table.rb:2 - type-member(+) ::Net:::: -> T.attached_class (of Net::SSH) @ test/cli/symbol-table/symbol-table.rb:2 + class ::Net::SSH:: < ::Module () @ test/cli/symbol-table/symbol-table.rb:2 + class ::Net:: < ::Module () @ test/cli/symbol-table/symbol-table.rb:2 diff --git a/test/cli/track-untyped/a.rb b/test/cli/track-untyped/a.rb new file mode 100644 index 0000000000..6e4319b75a --- /dev/null +++ b/test/cli/track-untyped/a.rb @@ -0,0 +1,13 @@ +# typed: strong +extend T::Sig + +sig { params(arg0: T.untyped).returns(Integer) } +def example_untyped(arg0) + result = arg0.foo + + if result + puts(result) + end + + result +end diff --git a/test/cli/track-untyped/b.rb b/test/cli/track-untyped/b.rb new file mode 100644 index 0000000000..9ab9de9a48 --- /dev/null +++ b/test/cli/track-untyped/b.rb @@ -0,0 +1,4 @@ +# typed: strong +extend T::Sig + +T.unsafe(nil).foo diff --git a/test/cli/track-untyped/c.rb b/test/cli/track-untyped/c.rb new file mode 100644 index 0000000000..8efdefe33f --- /dev/null +++ b/test/cli/track-untyped/c.rb @@ -0,0 +1,13 @@ +# typed: strong +extend T::Sig + +sig { params(arg0: Integer).returns(T::Boolean) } +def example_typed(arg0) + result = arg0.even? + + if result + puts(result) + end + + result +end diff --git a/test/cli/track-untyped/test.out b/test/cli/track-untyped/test.out new file mode 100644 index 0000000000..b145017657 --- /dev/null +++ b/test/cli/track-untyped/test.out @@ -0,0 +1,84 @@ +a.rb:6: Call to method `foo` on `T.untyped` https://srb.help/7018 + 6 | result = arg0.foo + ^^^ + Got `T.untyped` originating from: + a.rb:5: + 5 |def example_untyped(arg0) + ^^^^ + Note: + Support for `typed: strong` is minimal. Consider using `typed: strict` instead. + +a.rb:8: Conditional branch on `T.untyped` https://srb.help/7018 + 8 | if result + ^^^^^^ + Got `T.untyped` originating from: + a.rb:6: + 6 | result = arg0.foo + ^^^^^^^^ + Note: + Support for `typed: strong` is minimal. Consider using `typed: strict` instead. + +a.rb:9: Argument passed to parameter `arg0` is `T.untyped` https://srb.help/7018 + 9 | puts(result) + ^^^^^^ + Expected `BasicObject` for argument `arg0` of method `Kernel#puts`: + https://github.com/sorbet/sorbet/tree/master/rbi/core/kernel.rbi#L1903: + 1903 | arg0: BasicObject, + ^^^^ + Got `T.untyped` originating from: + a.rb:6: + 6 | result = arg0.foo + ^^^^^^^^ + a.rb:8: + 8 | if result + ^^^^^^ + Note: + Support for `typed: strong` is minimal. Consider using `typed: strict` instead. + +a.rb:12: Value returned from method is `T.untyped` https://srb.help/7018 + 12 | result + ^^^^^^ + Got `T.untyped` originating from: + a.rb:6: + 6 | result = arg0.foo + ^^^^^^^^ + a.rb:8: + 8 | if result + ^^^^^^ + Note: + Support for `typed: strong` is minimal. Consider using `typed: strict` instead. + +b.rb:4: Call to method `foo` on `T.untyped` https://srb.help/7018 + 4 |T.unsafe(nil).foo + ^^^ + Got `T.untyped` originating from: + b.rb:4: + 4 |T.unsafe(nil).foo + ^^^^^^^^^^^^^ + Note: + Support for `typed: strong` is minimal. Consider using `typed: strict` instead. +Errors: 5 +{ + "files": [ + { + "path": "test/cli/track-untyped/a.rb", + "sigil": "Strong", + "strict": "Strong", + "min_error_level": "Strong", + "untyped_usages": 4 + }, + { + "path": "test/cli/track-untyped/b.rb", + "sigil": "Strong", + "strict": "Strong", + "min_error_level": "Strong", + "untyped_usages": 1 + }, + { + "path": "test/cli/track-untyped/c.rb", + "sigil": "Strong", + "strict": "Strong", + "min_error_level": "Max" + } + ] +} diff --git a/test/cli/track-untyped/test.sh b/test/cli/track-untyped/test.sh new file mode 100755 index 0000000000..46fb8e7f98 --- /dev/null +++ b/test/cli/track-untyped/test.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -euo pipefail + +main/sorbet --silence-dev-message --track-untyped --print=file-table-json --max-threads=0 test/cli/track-untyped 2>&1 diff --git a/test/error-check-test.cc b/test/error-check-test.cc index c53fde5bf5..e2b8c900e1 100644 --- a/test/error-check-test.cc +++ b/test/error-check-test.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" // has to go first as it violates our requirements #include "ast/ast.h" diff --git a/test/fuzz/fuzz_dash_e.cc b/test/fuzz/fuzz_dash_e.cc index 36b89a2757..28a07dd7a9 100644 --- a/test/fuzz/fuzz_dash_e.cc +++ b/test/fuzz/fuzz_dash_e.cc @@ -70,7 +70,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { } indexed = realmain::pipeline::index(*gs, inputFiles, *opts, *workers, kvstore); - indexed = move(realmain::pipeline::resolve(gs, move(indexed), *opts, *workers).result()); - realmain::pipeline::typecheck(gs, move(indexed), *opts, *workers); + auto foundHashes = nullptr; + indexed = move(realmain::pipeline::resolve(gs, move(indexed), *opts, *workers, foundHashes).result()); + realmain::pipeline::typecheck(*gs, move(indexed), *opts, *workers); return 0; } diff --git a/test/hello-test.cc b/test/hello-test.cc index 9777ddc3b1..53109fb6aa 100644 --- a/test/hello-test.cc +++ b/test/hello-test.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" #include // has to go first as it violates our requirements diff --git a/test/helpers/BUILD b/test/helpers/BUILD index 7ac40e2218..d19c945299 100644 --- a/test/helpers/BUILD +++ b/test/helpers/BUILD @@ -15,7 +15,7 @@ cc_library( deps = [ "//core", "//main/lsp", - "@doctest", + "@doctest//doctest", "@dtl", ], ) diff --git a/test/helpers/CounterStateDatabase.cc b/test/helpers/CounterStateDatabase.cc index e81a5ebb7a..3824404cf6 100644 --- a/test/helpers/CounterStateDatabase.cc +++ b/test/helpers/CounterStateDatabase.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" // ^ Violates linting rules, so include first. #include "test/helpers/CounterStateDatabase.h" @@ -66,7 +66,7 @@ CounterStateDatabase::getTimings(ConstExprStr counter, vectortimings) { auto timing_tags_size = timing.tags == nullptr ? 0 : timing.tags->size(); if (strncmp(timing.measure, counter.str, counter.size + 1) == 0 && timing_tags_size >= tags.size()) { - UnorderedMap timingTags; + absl::flat_hash_map timingTags; if (timing.tags != nullptr) { for (const auto &tag : *timing.tags) { timingTags[tag.first] = tag.second; diff --git a/test/helpers/CounterStateDatabase.h b/test/helpers/CounterStateDatabase.h index 7bffebb327..c7a73a4219 100644 --- a/test/helpers/CounterStateDatabase.h +++ b/test/helpers/CounterStateDatabase.h @@ -1,8 +1,8 @@ #ifndef TEST_LSP_COUNTERSTATEDATABASE_H #define TEST_LSP_COUNTERSTATEDATABASE_H -#include "common/Counters.h" -#include "common/Counters_impl.h" +#include "common/counters/Counters.h" +#include "common/counters/Counters_impl.h" namespace sorbet::test::lsp { class CounterStateDatabase final { diff --git a/test/helpers/expectations.cc b/test/helpers/expectations.cc index 302f3d3549..008270fb31 100644 --- a/test/helpers/expectations.cc +++ b/test/helpers/expectations.cc @@ -1,11 +1,11 @@ -#include "doctest.h" +#include "doctest/doctest.h" // Include first as it uses poisoned things #include "absl/strings/match.h" #include "absl/strings/str_split.h" #include "common/FileOps.h" #include "common/concurrency/WorkerPool.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "dtl/dtl.hpp" #include "test/helpers/expectations.h" #include diff --git a/test/helpers/lsp.cc b/test/helpers/lsp.cc index 9f393d69c7..d51b191bdb 100644 --- a/test/helpers/lsp.cc +++ b/test/helpers/lsp.cc @@ -1,11 +1,11 @@ -#include "doctest.h" +#include "doctest/doctest.h" // has to go first as it violates requirements #include "absl/strings/ascii.h" #include "absl/strings/match.h" #include "absl/strings/str_replace.h" #include "common/common.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "main/lsp/LSPConfiguration.h" #include "main/lsp/requests/initialize.h" #include "test/helpers/lsp.h" @@ -538,7 +538,10 @@ namespace { } // namespace -string applyEdit(string_view source, const core::File &file, const Range &range, string_view newText) { +// reindent should probably be `true` for snippets (e.g., completion items) and false for things that are not snippets. +// At some point we might want to try to approximate what VS Code does more closely, but I've never +// been able to find a concise description of how they decide to reindent a snippet and when. +string applyEdit(string_view source, const core::File &file, const Range &range, string_view newText, bool reindent) { auto beginLine = static_cast(range.start->line + 1); auto beginCol = static_cast(range.start->character + 1); auto beginOffset = core::Loc::pos2Offset(file, {beginLine, beginCol}).value(); @@ -553,7 +556,11 @@ string applyEdit(string_view source, const core::File &file, const Range &range, auto indentAfterNewline = absl::StrCat("\n", source.substr(lineStartOffset, firstNonWhitespace)); string actualEditedFileContents = string(source); - auto indented = absl::StrReplaceAll(stripTrailingAsciiBlank(newText), {{"\n", indentAfterNewline}}); + // Only strip trailing whitespace if it will end up at the end of a line + auto maybeStripped = (actualEditedFileContents.size() > endOffset && actualEditedFileContents[endOffset] == '\n') + ? stripTrailingAsciiBlank(newText) + : newText; + auto indented = reindent ? absl::StrReplaceAll(maybeStripped, {{"\n", indentAfterNewline}}) : string(maybeStripped); actualEditedFileContents.replace(beginOffset, endOffset - beginOffset, indented); return actualEditedFileContents; diff --git a/test/helpers/lsp.h b/test/helpers/lsp.h index c0fc197dd3..537d341a54 100644 --- a/test/helpers/lsp.h +++ b/test/helpers/lsp.h @@ -89,7 +89,8 @@ std::vector> getLSPResponsesFor(LSPWrapper &wrapper, std::vector> getLSPResponsesFor(LSPWrapper &wrapper, std::vector> messages); -std::string applyEdit(std::string_view source, const core::File &file, const Range &range, std::string_view newText); +std::string applyEdit(std::string_view source, const core::File &file, const Range &range, std::string_view newText, + bool reindent); } // namespace sorbet::test #endif // TEST_HELPERS_LSP_H diff --git a/test/helpers/position_assertions.cc b/test/helpers/position_assertions.cc index 08c3e630c2..cd59a41ac6 100644 --- a/test/helpers/position_assertions.cc +++ b/test/helpers/position_assertions.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" // ^ Include first because it violates linting rules. #include "absl/strings/match.h" @@ -6,8 +6,8 @@ #include "absl/strings/str_split.h" #include "common/FileOps.h" #include "common/concurrency/WorkerPool.h" -#include "common/formatting.h" -#include "common/sort.h" +#include "common/sort/sort.h" +#include "common/strings/formatting.h" #include "main/lsp/LSPConfiguration.h" #include "test/helpers/lsp.h" #include "test/helpers/position_assertions.h" @@ -19,6 +19,215 @@ using namespace std; namespace sorbet::test { namespace { +/** + * prettyPrintComment("foo.bar", {start: {character: 4}, end: {character: 7}}, "error: bar not defined") -> + * foo.bar + * ^^^ error: bar not defined + */ +string prettyPrintRangeComment(string_view sourceLine, const Range &range, string_view comment) { + int numLeadingSpaces = range.start->character; + if (numLeadingSpaces < 0) { + FAIL_CHECK(fmt::format("Invalid range: {} < 0", range.start->character)); + return ""; + } + string sourceLineNumber = fmt::format("{}", range.start->line + 1); + { + INFO("Multi-line ranges are not supported at this time."); + CHECK_EQ(range.start->line, range.end->line); + } + if (range.start->line != range.end->line) { + return string(comment); + } + + int numCarets = range.end->character - range.start->character; + if (numCarets == RangeAssertion::END_OF_LINE_POS) { + // Caret the entire line. + numCarets = sourceLine.length(); + } + + return fmt::format("{}: {}\n {}{} {}", sourceLineNumber, sourceLine, + string(numLeadingSpaces + sourceLineNumber.length() + 1, ' '), string(numCarets, '^'), comment); +} + +template bool isDuplicateDiagnostic(string_view filename, T *assertion, const Diagnostic &d) { + return assertion && assertion->matchesDuplicateErrors && assertion->matches(filename, *d.range) == 0 && + d.message.find(assertion->message) != string::npos; +} + +template +void reportMissingError(const string &filename, const T &assertion, string_view sourceLine, string_view errorPrefix, + bool missingDuplicate = false) { + auto coreMessage = missingDuplicate ? "Error was not duplicated" : "Did not find expected error"; + auto messagePostfix = missingDuplicate ? "\nYou can fix this error by changing the assertion to `error:`." : ""; + ADD_FAIL_CHECK_AT(filename.c_str(), assertion.range->start->line + 1, + fmt::format("{}{}:\n{}{}", errorPrefix, coreMessage, + prettyPrintRangeComment(sourceLine, *assertion.range, assertion.toString()), + messagePostfix)); +} + +void reportUnexpectedError(const string &filename, const Diagnostic &diagnostic, string_view sourceLine, + string_view errorPrefix) { + ADD_FAIL_CHECK_AT( + filename.c_str(), diagnostic.range->start->line + 1, + fmt::format( + "{}Found unexpected error:\n{}\nNote: If there is already an assertion for this error, then this is a " + "duplicate error. Change the assertion to `# error-with-dupes: ` if the duplicate is " + "expected.", + errorPrefix, + prettyPrintRangeComment( + sourceLine, *diagnostic.range, + fmt::format(diagnostic.severity == DiagnosticSeverity::Information ? "untyped: {}" : "error: {}", + diagnostic.message)))); +} +string getSourceLine(const UnorderedMap> &sourceFileContents, const string &filename, + int line) { + if (absl::StartsWith(filename, core::File::URL_PREFIX)) { + return ""; + } + + auto it = sourceFileContents.find(filename); + if (it == sourceFileContents.end()) { + FAIL_CHECK(fmt::format("Unable to find referenced source file `{}`", filename)); + return ""; + } + + auto &file = it->second; + if (line >= file->lineCount()) { + ADD_FAIL_CHECK_AT(filename.c_str(), line + 1, "Invalid line number for range."); + return ""; + } else { + // Note: line is a 0-indexed line number, but file uses 1-indexed line numbers. + auto lineView = file->getLine(line + 1); + return string(lineView); + } +} + +template +bool checkAllInner(const sorbet::UnorderedMap> &files, + vector> errorAssertions, + map>> &filenamesAndDiagnostics, string errorPrefix) { + // Sort input error assertions so they are in (filename, line, column) order. + fast_sort(errorAssertions, sorbet::test::RangeAssertion::compareByRange); + + auto assertionsIt = errorAssertions.begin(); + + bool success = true; + + // Due to map's default sort order, this loop iterates over diagnostics in filename order. + for (auto &filenameAndDiagnostics : filenamesAndDiagnostics) { + auto &filename = filenameAndDiagnostics.first; + auto &diagnostics = filenameAndDiagnostics.second; + + // Sort diagnostics within file in range, message order. + // This explicit sort, combined w/ the map's implicit sort order, ensures that this loop iterates over + // diagnostics in (filename, range, message) order -- matching the sort order of errorAssertions. + fast_sort(diagnostics, [](const unique_ptr &a, const unique_ptr &b) -> bool { + const int rangeCmp = a->range->cmp(*b->range); + if (rangeCmp != 0) { + return rangeCmp < 0; + } + return a->message.compare(b->message) < 0; + }); + + auto diagnosticsIt = diagnostics.begin(); + T *lastAssertion = nullptr; + bool lastAssertionMatchedDuplicate = false; + + while (diagnosticsIt != diagnostics.end() && assertionsIt != errorAssertions.end()) { + // See if the ranges match. + auto &diagnostic = *diagnosticsIt; + auto &assertion = *assertionsIt; + + if (diagnostic->severity.value_or(T::severity) != T::severity) { + diagnosticsIt++; + continue; + } + + if (isDuplicateDiagnostic(filename, lastAssertion, *diagnostic)) { + diagnosticsIt++; + lastAssertionMatchedDuplicate = true; + continue; + } else { + if (lastAssertion && lastAssertion->matchesDuplicateErrors && !lastAssertionMatchedDuplicate) { + reportMissingError(lastAssertion->filename, *lastAssertion, + getSourceLine(files, lastAssertion->filename, lastAssertion->range->start->line), + errorPrefix, true); + success = false; + } + lastAssertionMatchedDuplicate = false; + lastAssertion = nullptr; + } + + const int cmp = assertion->matches(filename, *diagnostic->range); + if (cmp > 0) { + // Diagnostic comes *before* this assertion, so we don't + // have an assertion that matches the diagnostic. + reportUnexpectedError(filename, *diagnostic, + getSourceLine(files, filename, diagnostic->range->start->line), errorPrefix); + // We've 'consumed' the diagnostic -- nothing matches it. + diagnosticsIt++; + success = false; + } else if (cmp < 0) { + // Diagnostic comes *after* this assertion + // We don't have a diagnostic that matches the assertion. + reportMissingError(assertion->filename, *assertion, + getSourceLine(files, assertion->filename, assertion->range->start->line), + errorPrefix); + // We've 'consumed' this error assertion -- nothing matches it. + assertionsIt++; + success = false; + } else { + // Ranges match, so check the assertion. + success = assertion->check(*diagnostic, + getSourceLine(files, assertion->filename, assertion->range->start->line), + errorPrefix) && + success; + // We've 'consumed' the diagnostic and assertion. + // Save assertion in case it matches multiple diagnostics. + lastAssertion = assertion.get(); + diagnosticsIt++; + assertionsIt++; + } + } + + while (diagnosticsIt != diagnostics.end()) { + // We had more diagnostics than error assertions. + auto &diagnostic = *diagnosticsIt; + + if (diagnostic->severity.value_or(T::severity) != T::severity) { + diagnosticsIt++; + continue; + } + + if (isDuplicateDiagnostic(filename, lastAssertion, *diagnostic)) { + lastAssertionMatchedDuplicate = true; + } else { + reportUnexpectedError(filename, *diagnostic, + getSourceLine(files, filename, diagnostic->range->start->line), errorPrefix); + success = false; + + if (lastAssertion && lastAssertion->matchesDuplicateErrors && !lastAssertionMatchedDuplicate) { + reportMissingError(lastAssertion->filename, *lastAssertion, + getSourceLine(files, lastAssertion->filename, lastAssertion->range->start->line), + errorPrefix, true); + } + lastAssertion = nullptr; + lastAssertionMatchedDuplicate = false; + } + diagnosticsIt++; + } + } + + while (assertionsIt != errorAssertions.end()) { + // Had more error assertions than diagnostics + reportMissingError((*assertionsIt)->filename, **assertionsIt, + getSourceLine(files, (*assertionsIt)->filename, (*assertionsIt)->range->start->line), + errorPrefix); + success = false; + assertionsIt++; + } + return success; +} // Matches ' # ^^^^^ label: dafhdsjfkhdsljkfh*&#&*%' // and ' # label: foobar'. @@ -30,6 +239,7 @@ const regex whitespaceRegex("^[ ]*$"); const UnorderedMap< string, function(string_view, unique_ptr &, int, string_view, string_view)>> assertionConstructors = { + {"untyped", UntypedAssertion::make}, {"error", ErrorAssertion::make}, {"error-with-dupes", ErrorAssertion::make}, {"usage", UsageAssertion::make}, @@ -38,9 +248,11 @@ const UnorderedMap< {"def", DefAssertion::make}, {"type", TypeAssertion::make}, {"type-def", TypeDefAssertion::make}, + {"highlight-untyped-values", BooleanPropertyAssertion::make}, {"disable-fast-path", BooleanPropertyAssertion::make}, {"disable-stress-incremental", BooleanPropertyAssertion::make}, {"stripe-mode", BooleanPropertyAssertion::make}, + {"check-out-of-order-constant-references", BooleanPropertyAssertion::make}, {"enable-packager", BooleanPropertyAssertion::make}, {"enable-experimental-requires-ancestor", BooleanPropertyAssertion::make}, {"experimental-ruby3-keyword-args", BooleanPropertyAssertion::make}, @@ -51,6 +263,7 @@ const UnorderedMap< {"assert-fast-path", FastPathAssertion::make}, {"assert-slow-path", BooleanPropertyAssertion::make}, {"hover", HoverAssertion::make}, + {"hover-line", HoverLineAssertion::make}, {"completion", CompletionAssertion::make}, {"apply-completion", ApplyCompletionAssertion::make}, {"apply-code-action", ApplyCodeActionAssertion::make}, @@ -59,6 +272,7 @@ const UnorderedMap< {"apply-rename", ApplyRenameAssertion::make}, {"extra-package-files-directory-prefix-underscore", StringPropertyAssertion::make}, {"extra-package-files-directory-prefix-slash", StringPropertyAssertion::make}, + {"skip-package-import-visibility-check-for", StringPropertyAssertion::make}, {"implementation", ImplementationAssertion::make}, {"find-implementation", FindImplementationAssertion::make}, {"show-symbol", ShowSymbolAssertion::make}, @@ -87,36 +301,6 @@ bool rangeIsSubset(const Range &a, const Range &b) { return b.start->character >= a.start->character && b.end->character <= a.end->character; } -/** - * prettyPrintComment("foo.bar", {start: {character: 4}, end: {character: 7}}, "error: bar not defined") -> - * foo.bar - * ^^^ error: bar not defined - */ -string prettyPrintRangeComment(string_view sourceLine, const Range &range, string_view comment) { - int numLeadingSpaces = range.start->character; - if (numLeadingSpaces < 0) { - FAIL_CHECK(fmt::format("Invalid range: {} < 0", range.start->character)); - return ""; - } - string sourceLineNumber = fmt::format("{}", range.start->line + 1); - { - INFO("Multi-line ranges are not supported at this time."); - CHECK_EQ(range.start->line, range.end->line); - } - if (range.start->line != range.end->line) { - return string(comment); - } - - int numCarets = range.end->character - range.start->character; - if (numCarets == RangeAssertion::END_OF_LINE_POS) { - // Caret the entire line. - numCarets = sourceLine.length(); - } - - return fmt::format("{}: {}\n {}{} {}", sourceLineNumber, sourceLine, - string(numLeadingSpaces + sourceLineNumber.length() + 1, ' '), string(numCarets, '^'), comment); -} - string_view getLine(const LSPConfiguration &config, const UnorderedMap> &sourceFileContents, const Location &loc) { auto filename = uriToFilePath(config, loc.uri); @@ -298,6 +482,43 @@ bool ErrorAssertion::check(const Diagnostic &diagnostic, string_view sourceLine, return true; } +UntypedAssertion::UntypedAssertion(string_view filename, unique_ptr &range, int assertionLine, + string_view message) + : RangeAssertion(filename, range, assertionLine), message(message) {} + +shared_ptr UntypedAssertion::make(string_view filename, unique_ptr &range, int assertionLine, + string_view assertionContents, string_view assertionType) { + return make_shared(filename, range, assertionLine, assertionContents); +} + +string UntypedAssertion::toString() const { + return fmt::format("{}: {}", "untyped", message); +} + +bool UntypedAssertion::checkAll(const UnorderedMap> &files, + vector> errorAssertions, + map>> &filenamesAndDiagnostics, + string errorPrefix) { + return checkAllInner(files, errorAssertions, filenamesAndDiagnostics, errorPrefix); +} + +bool UntypedAssertion::check(const Diagnostic &diagnostic, string_view sourceLine, string_view errorPrefix) { + // The error message must contain `message`. + if (diagnostic.severity != DiagnosticSeverity::Information || diagnostic.message.find(message) == string::npos) { + ADD_FAIL_CHECK_AT( + filename.c_str(), range->start->line + 1, + fmt::format( + "{}Expected information diagnostic of form:\n{}\nFound diagnostic:\n{}", errorPrefix, + prettyPrintRangeComment(sourceLine, *range, toString()), + prettyPrintRangeComment( + sourceLine, *diagnostic.range, + fmt::format(diagnostic.severity == DiagnosticSeverity::Information ? "untyped: {}" : "error: {}", + diagnostic.message)))); + return false; + } + return true; +} + unique_ptr RangeAssertion::makeRange(int sourceLine, int startChar, int endChar) { return make_unique(make_unique(sourceLine, startChar), make_unique(sourceLine, endChar)); } @@ -313,6 +534,17 @@ RangeAssertion::getErrorAssertions(const vector> &ass return rv; } +vector> +RangeAssertion::getUntypedAssertions(const vector> &assertions) { + vector> rv; + for (auto assertion : assertions) { + if (auto assertionOfType = dynamic_pointer_cast(assertion)) { + rv.push_back(assertionOfType); + } + } + return rv; +} + vector> parseAssertionsForFile(const shared_ptr &file) { vector> assertions; @@ -858,170 +1090,11 @@ string TypeAssertion::toString() const { return fmt::format("type: {}", symbol); } -void reportMissingError(const string &filename, const ErrorAssertion &assertion, string_view sourceLine, - string_view errorPrefix, bool missingDuplicate = false) { - auto coreMessage = missingDuplicate ? "Error was not duplicated" : "Did not find expected error"; - auto messagePostfix = missingDuplicate ? "\nYou can fix this error by changing the assertion to `error:`." : ""; - ADD_FAIL_CHECK_AT(filename.c_str(), assertion.range->start->line + 1, - fmt::format("{}{}:\n{}{}", errorPrefix, coreMessage, - prettyPrintRangeComment(sourceLine, *assertion.range, assertion.toString()), - messagePostfix)); -} - -void reportUnexpectedError(const string &filename, const Diagnostic &diagnostic, string_view sourceLine, - string_view errorPrefix) { - ADD_FAIL_CHECK_AT( - filename.c_str(), diagnostic.range->start->line + 1, - fmt::format( - "{}Found unexpected error:\n{}\nNote: If there is already an assertion for this error, then this is a " - "duplicate error. Change the assertion to `# error-with-dupes: ` if the duplicate is " - "expected.", - errorPrefix, - prettyPrintRangeComment(sourceLine, *diagnostic.range, fmt::format("error: {}", diagnostic.message)))); -} - -string getSourceLine(const UnorderedMap> &sourceFileContents, const string &filename, - int line) { - if (absl::StartsWith(filename, core::File::URL_PREFIX)) { - return ""; - } - - auto it = sourceFileContents.find(filename); - if (it == sourceFileContents.end()) { - FAIL_CHECK(fmt::format("Unable to find referenced source file `{}`", filename)); - return ""; - } - - auto &file = it->second; - if (line >= file->lineCount()) { - ADD_FAIL_CHECK_AT(filename.c_str(), line + 1, "Invalid line number for range."); - return ""; - } else { - // Note: line is a 0-indexed line number, but file uses 1-indexed line numbers. - auto lineView = file->getLine(line + 1); - return string(lineView); - } -} - -bool isDuplicateDiagnostic(string_view filename, ErrorAssertion *assertion, const Diagnostic &d) { - return assertion && assertion->matchesDuplicateErrors && assertion->matches(filename, *d.range) == 0 && - d.message.find(assertion->message) != string::npos; -} - bool ErrorAssertion::checkAll(const UnorderedMap> &files, vector> errorAssertions, map>> &filenamesAndDiagnostics, string errorPrefix) { - // Sort input error assertions so they are in (filename, line, column) order. - fast_sort(errorAssertions, RangeAssertion::compareByRange); - - auto assertionsIt = errorAssertions.begin(); - - bool success = true; - - // Due to map's default sort order, this loop iterates over diagnostics in filename order. - for (auto &filenameAndDiagnostics : filenamesAndDiagnostics) { - auto &filename = filenameAndDiagnostics.first; - auto &diagnostics = filenameAndDiagnostics.second; - - // Sort diagnostics within file in range, message order. - // This explicit sort, combined w/ the map's implicit sort order, ensures that this loop iterates over - // diagnostics in (filename, range, message) order -- matching the sort order of errorAssertions. - fast_sort(diagnostics, [](const unique_ptr &a, const unique_ptr &b) -> bool { - const int rangeCmp = a->range->cmp(*b->range); - if (rangeCmp != 0) { - return rangeCmp < 0; - } - return a->message.compare(b->message) < 0; - }); - - auto diagnosticsIt = diagnostics.begin(); - ErrorAssertion *lastAssertion = nullptr; - bool lastAssertionMatchedDuplicate = false; - - while (diagnosticsIt != diagnostics.end() && assertionsIt != errorAssertions.end()) { - // See if the ranges match. - auto &diagnostic = *diagnosticsIt; - auto &assertion = *assertionsIt; - - if (isDuplicateDiagnostic(filename, lastAssertion, *diagnostic)) { - diagnosticsIt++; - lastAssertionMatchedDuplicate = true; - continue; - } else { - if (lastAssertion && lastAssertion->matchesDuplicateErrors && !lastAssertionMatchedDuplicate) { - reportMissingError(lastAssertion->filename, *lastAssertion, - getSourceLine(files, lastAssertion->filename, lastAssertion->range->start->line), - errorPrefix, true); - success = false; - } - lastAssertionMatchedDuplicate = false; - lastAssertion = nullptr; - } - - const int cmp = assertion->matches(filename, *diagnostic->range); - if (cmp > 0) { - // Diagnostic comes *before* this assertion, so we don't - // have an assertion that matches the diagnostic. - reportUnexpectedError(filename, *diagnostic, - getSourceLine(files, filename, diagnostic->range->start->line), errorPrefix); - // We've 'consumed' the diagnostic -- nothing matches it. - diagnosticsIt++; - success = false; - } else if (cmp < 0) { - // Diagnostic comes *after* this assertion - // We don't have a diagnostic that matches the assertion. - reportMissingError(assertion->filename, *assertion, - getSourceLine(files, assertion->filename, assertion->range->start->line), - errorPrefix); - // We've 'consumed' this error assertion -- nothing matches it. - assertionsIt++; - success = false; - } else { - // Ranges match, so check the assertion. - success = assertion->check(*diagnostic, - getSourceLine(files, assertion->filename, assertion->range->start->line), - errorPrefix) && - success; - // We've 'consumed' the diagnostic and assertion. - // Save assertion in case it matches multiple diagnostics. - lastAssertion = assertion.get(); - diagnosticsIt++; - assertionsIt++; - } - } - - while (diagnosticsIt != diagnostics.end()) { - // We had more diagnostics than error assertions. - auto &diagnostic = *diagnosticsIt; - if (isDuplicateDiagnostic(filename, lastAssertion, *diagnostic)) { - lastAssertionMatchedDuplicate = true; - } else { - reportUnexpectedError(filename, *diagnostic, - getSourceLine(files, filename, diagnostic->range->start->line), errorPrefix); - success = false; - - if (lastAssertion && lastAssertion->matchesDuplicateErrors && !lastAssertionMatchedDuplicate) { - reportMissingError(lastAssertion->filename, *lastAssertion, - getSourceLine(files, lastAssertion->filename, lastAssertion->range->start->line), - errorPrefix, true); - } - lastAssertion = nullptr; - lastAssertionMatchedDuplicate = false; - } - diagnosticsIt++; - } - } - - while (assertionsIt != errorAssertions.end()) { - // Had more error assertions than diagnostics - reportMissingError((*assertionsIt)->filename, **assertionsIt, - getSourceLine(files, (*assertionsIt)->filename, (*assertionsIt)->range->start->line), - errorPrefix); - success = false; - assertionsIt++; - } - return success; + return checkAllInner(files, errorAssertions, filenamesAndDiagnostics, errorPrefix); } shared_ptr BooleanPropertyAssertion::make(string_view filename, unique_ptr &range, @@ -1232,6 +1305,76 @@ string HoverAssertion::toString() const { return fmt::format("hover: {}", message); } +shared_ptr HoverLineAssertion::make(string_view filename, unique_ptr &range, + int assertionLine, string_view assertionContents, + string_view assertionType) { + static const regex multilineRegex(R"(^([0-9]+) (.+)$)"); + + smatch matches; + string assertionContentsString = string(assertionContents); + + if (regex_search(assertionContentsString, matches, multilineRegex)) { + auto lineno = stoi(matches[1].str()); + auto contents = matches[2].str(); + return make_shared(filename, range, assertionLine, lineno, contents); + } + + ADD_FAIL_CHECK_AT( + string(filename).c_str(), assertionLine + 1, + fmt::format("Improperly formatted hover-line assertion. Expected ' '. Found '{}'", + assertionContents, filename)); + + return nullptr; +} +HoverLineAssertion::HoverLineAssertion(string_view filename, unique_ptr &range, int assertionLine, int lineno, + string_view message) + : RangeAssertion(filename, range, assertionLine), lineno(lineno), message(string(message)) {} + +void HoverLineAssertion::checkAll(const vector> &assertions, + const UnorderedMap> &sourceFileContents, + LSPWrapper &wrapper, int &nextId, string errorPrefix) { + for (auto assertion : assertions) { + if (auto assertionOfType = dynamic_pointer_cast(assertion)) { + assertionOfType->check(sourceFileContents, wrapper, nextId, errorPrefix); + } + } +} + +void HoverLineAssertion::check(const UnorderedMap> &sourceFileContents, + LSPWrapper &wrapper, int &nextId, string errorPrefix) { + const auto &config = wrapper.config(); + auto uri = filePathToUri(config, filename); + auto pos = make_unique(make_unique(uri), range->start->copy()); + auto id = nextId++; + auto msg = make_unique(make_unique("2.0", id, LSPMethod::TextDocumentHover, move(pos))); + auto responses = getLSPResponsesFor(wrapper, move(msg)); + REQUIRE_EQ(responses.size(), 1); + auto &responseMsg = responses.at(0); + REQUIRE(responseMsg->isResponse()); + auto &response = responseMsg->asResponse(); + REQUIRE_MESSAGE(response.result.has_value(), response.error.value()->message); + auto &hoverResponse = get>>(*response.result); + auto hoverContents = hoverToString(hoverResponse); + vector hoverLines = absl::StrSplit(hoverContents, "\n"); + + REQUIRE_LE(1, this->lineno); + REQUIRE_LE(this->lineno, hoverLines.size()); + + // Match a full line. Makes it possible to disambiguate `String` and `T.nilable(String)`. + if (hoverLines[this->lineno - 1] != this->message) { + auto sourceLine = getSourceLine(sourceFileContents, filename, range->start->line); + ADD_FAIL_CHECK_AT(filename.c_str(), range->start->line + 1, + fmt::format("{}Expected line {} of hover contents:\n{}\nComplete hover contents:\n{}", + errorPrefix, this->lineno, + prettyPrintRangeComment(sourceLine, *range, toString()), + prettyPrintRangeComment(sourceLine, *range, hoverContents))); + } +} + +string HoverLineAssertion::toString() const { + return fmt::format("hover-line: {} {}", lineno, message); +} + shared_ptr CompletionAssertion::make(string_view filename, unique_ptr &range, int assertionLine, string_view assertionContents, string_view assertionType) { @@ -1362,7 +1505,8 @@ void ApplyCompletionAssertion::check(const UnorderedMaptextEdit, nullopt); auto &textEdit = completionItem->textEdit.value(); - auto actualEditedFileContents = applyEdit(file->source(), *file, *textEdit->range, textEdit->newText); + auto reindent = true; + auto actualEditedFileContents = applyEdit(file->source(), *file, *textEdit->range, textEdit->newText, reindent); { CHECK_EQ_DIFF(expectedEditedFileContents, actualEditedFileContents, @@ -1518,7 +1662,8 @@ void ApplyRenameAssertion::check(const UnorderedMaprange, edit->newText); + auto reindent = false; + actualEditedFileContents = applyEdit(actualEditedFileContents, file, *edit->range, edit->newText, reindent); } actualEditedFiles[sourceFilePath] = actualEditedFileContents; } @@ -1639,7 +1784,8 @@ void ApplyCodeActionAssertion::check(const UnorderedMapedits) { - actualEditedFileContents = applyEdit(actualEditedFileContents, *file, *e->range, e->newText); + auto reindent = false; + actualEditedFileContents = applyEdit(actualEditedFileContents, *file, *e->range, e->newText, reindent); } assertResults(expectedUpdatedFilePath, expectedEditedFileContents, actualEditedFileContents); } @@ -1667,7 +1813,8 @@ void ApplyCodeActionAssertion::checkAll( ? accumulatedOriginalEditedContents[actualEditedFileContents] : actualEditedFileContents; - auto newSource = applyEdit(oldSource, *file, *e->range, e->newText); + auto reindent = false; + auto newSource = applyEdit(oldSource, *file, *e->range, e->newText, reindent); accumulatedOriginalEditedContents.insert_or_assign(actualEditedFileContents, newSource); } } diff --git a/test/helpers/position_assertions.h b/test/helpers/position_assertions.h index dde65b5260..07043316b3 100644 --- a/test/helpers/position_assertions.h +++ b/test/helpers/position_assertions.h @@ -10,6 +10,7 @@ namespace sorbet::test { using namespace sorbet::realmain::lsp; class ErrorAssertion; +class UntypedAssertion; /** * An assertion that is relevant to a specific set of characters on a line. @@ -42,6 +43,9 @@ class RangeAssertion { static std::vector> getErrorAssertions(const std::vector> &assertions); + static std::vector> + getUntypedAssertions(const std::vector> &assertions); + const std::string filename; const std::unique_ptr range; // Used to produce intelligent error messages when assertion comments are malformed/invalid @@ -83,6 +87,7 @@ class ErrorAssertion final : public RangeAssertion { const std::string message; const bool matchesDuplicateErrors; + static constexpr DiagnosticSeverity severity = DiagnosticSeverity::Error; ErrorAssertion(std::string_view filename, std::unique_ptr &range, int assertionLine, std::string_view message, bool matchesDuplicateErrors); @@ -92,6 +97,31 @@ class ErrorAssertion final : public RangeAssertion { bool check(const Diagnostic &diagnostic, std::string_view sourceLine, std::string_view errorPrefix); }; +class UntypedAssertion final : public RangeAssertion { +public: + static std::shared_ptr make(std::string_view filename, std::unique_ptr &range, + int assertionLine, std::string_view assertionContents, + std::string_view assertionType); + + UntypedAssertion(std::string_view filename, std::unique_ptr &range, int assertionLine, + std::string_view message); + + std::string toString() const override; + const std::string message; + + // this exists solely to allow us to reuse ErrorAssertion's checkAll + // for UntypedAssertion. It should *always* be false. + static constexpr bool matchesDuplicateErrors = false; + static constexpr DiagnosticSeverity severity = DiagnosticSeverity::Information; + + static bool checkAll(const UnorderedMap> &files, + std::vector> errorAssertions, + std::map>> &filenamesAndDiagnostics, + std::string errorPrefix = ""); + + bool check(const Diagnostic &diagnostic, std::string_view sourceLine, std::string_view errorPrefix); +}; + // # ^^^ def: symbol class DefAssertion final : public RangeAssertion { public: @@ -276,6 +306,29 @@ class HoverAssertion final : public RangeAssertion { std::string toString() const override; }; +// # ^ hover-line: 1 foo +class HoverLineAssertion final : public RangeAssertion { +public: + static std::shared_ptr make(std::string_view filename, std::unique_ptr &range, + int assertionLine, std::string_view assertionContents, + std::string_view assertionType); + /** Checks all HoverLineAssertions within the assertion vector. Skips over non-hover assertions.*/ + static void checkAll(const std::vector> &assertions, + const UnorderedMap> &sourceFileContents, + LSPWrapper &wrapper, int &nextId, std::string errorPrefix = ""); + + HoverLineAssertion(std::string_view filename, std::unique_ptr &range, int assertionLine, int lineno, + std::string_view message); + + const int lineno; + const std::string message; + + void check(const UnorderedMap> &sourceFileContents, LSPWrapper &wrapper, + int &nextId, std::string errorPrefix = ""); + + std::string toString() const override; +}; + // # ^ completion: foo class CompletionAssertion final : public RangeAssertion { public: diff --git a/test/lsp/ProtocolTest.cc b/test/lsp/ProtocolTest.cc index a19ed0cbd6..8b32c71657 100644 --- a/test/lsp/ProtocolTest.cc +++ b/test/lsp/ProtocolTest.cc @@ -34,7 +34,6 @@ void ProtocolTest::resetState(std::shared_ptr opts) } opts->cacheDir = cacheDir; } - opts->lspExperimentalFastPathEnabled = true; if (useMultithreading) { lspWrapper = MultiThreadedLSPWrapper::create(rootPath, opts); diff --git a/test/lsp/ProtocolTest.h b/test/lsp/ProtocolTest.h index 746f7ccbe6..d21c78d22f 100644 --- a/test/lsp/ProtocolTest.h +++ b/test/lsp/ProtocolTest.h @@ -1,10 +1,10 @@ #ifndef TEST_LSP_PROTOCOLTEST_H #define TEST_LSP_PROTOCOLTEST_H -#include "doctest.h" +#include "doctest/doctest.h" // ^ Violates linting rules, so include first. -#include "common/Counters.h" -#include "common/Counters_impl.h" +#include "common/counters/Counters.h" +#include "common/counters/Counters_impl.h" #include "main/lsp/json_types.h" #include "main/lsp/wrapper.h" #include "test/helpers/CounterStateDatabase.h" diff --git a/test/lsp/alias-incremental/alias-incremental.rec b/test/lsp/alias-incremental/alias-incremental.rec index ca546b427d..c670c1f57d 100644 --- a/test/lsp/alias-incremental/alias-incremental.rec +++ b/test/lsp/alias-incremental/alias-incremental.rec @@ -1,5 +1,5 @@ Read: {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":19779,"rootPath":"/Users/dmitry/stripe/pay-server","rootUri":"file:///Users/dmitry/stripe/pay-server","capabilities":{"workspace":{"applyEdit":true,"workspaceEdit":{"documentChanges":true},"didChangeConfiguration":{"dynamicRegistration":true},"didChangeWatchedFiles":{"dynamicRegistration":true},"symbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]}},"executeCommand":{"dynamicRegistration":true},"configuration":true,"workspaceFolders":true},"textDocument":{"publishDiagnostics":{"relatedInformation":true},"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"contextSupport":true,"completionItem":{"snippetSupport":true,"commitCharactersSupport":true,"documentationFormat":["markdown","plaintext"]},"completionItemKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]}},"hover":{"dynamicRegistration":true,"contentFormat":["markdown","plaintext"]},"signatureHelp":{"dynamicRegistration":true,"signatureInformation":{"documentationFormat":["markdown","plaintext"]}},"definition":{"dynamicRegistration":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]}},"codeAction":{"dynamicRegistration":true},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true},"documentLink":{"dynamicRegistration":true},"typeDefinition":{"dynamicRegistration":true},"implementation":{"dynamicRegistration":true},"colorProvider":{"dynamicRegistration":true}}},"trace":"off","workspaceFolders":[{"uri":"file:///Users/dmitry/stripe/pay-server","name":"pay-server"}]}} -Write: {"jsonrpc":"2.0","id":0,"requestMethod":"initialize","result":{"capabilities":{"textDocumentSync":1,"hoverProvider":true,"completionProvider":{"triggerCharacters":[".",":","@","#"]},"signatureHelpProvider":{"triggerCharacters":["(",","]},"definitionProvider":true,"typeDefinitionProvider":true,"implementationProvider":true,"referencesProvider":true,"documentHighlightProvider":true,"documentSymbolProvider":true,"workspaceSymbolProvider":true,"codeActionProvider":{"codeActionKinds":["quickfix","source.fixAll.sorbet","refactor.extract"]},"documentFormattingProvider":false,"renameProvider":{"prepareProvider":true},"sorbetShowSymbolProvider":true}}} +Write: {"jsonrpc":"2.0","id":0,"requestMethod":"initialize","result":{"capabilities":{"textDocumentSync":1,"hoverProvider":true,"completionProvider":{"triggerCharacters":[".",":","@","#"]},"signatureHelpProvider":{"triggerCharacters":["(",","]},"definitionProvider":true,"typeDefinitionProvider":true,"implementationProvider":true,"referencesProvider":true,"documentHighlightProvider":true,"documentSymbolProvider":true,"workspaceSymbolProvider":true,"codeActionProvider":{"codeActionKinds":["quickfix","source.fixAll.sorbet","refactor.extract","refactor.rewrite"],"resolveProvider":true},"documentFormattingProvider":false,"renameProvider":{"prepareProvider":true},"sorbetShowSymbolProvider":true}}} Read: {"jsonrpc":"2.0","method":"initialized","params":{}} Read: {"jsonrpc":"2.0","method":"workspace/didChangeConfiguration","params":{"settings":{"ruby-typer":{}}}} Read: {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///Users/dmitry/stripe/pay-server/cibot/lib/cibot/bla.rb","languageId":"ruby","version":1,"text":"class Bla\n S = Bla\n def foo\n 123\n end\nend"}}} diff --git a/test/lsp/cache_protocol_test_corpus.cc b/test/lsp/cache_protocol_test_corpus.cc index 8b4aaa4e73..b23bc2e3ed 100644 --- a/test/lsp/cache_protocol_test_corpus.cc +++ b/test/lsp/cache_protocol_test_corpus.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" // has to go first as it violates our requirements #include "absl/strings/match.h" #include "absl/strings/str_replace.h" diff --git a/test/lsp/incremental-lsp-changes/incremental-lsp-changes.rec b/test/lsp/incremental-lsp-changes/incremental-lsp-changes.rec index 98311efe98..b5cabe5b16 100644 --- a/test/lsp/incremental-lsp-changes/incremental-lsp-changes.rec +++ b/test/lsp/incremental-lsp-changes/incremental-lsp-changes.rec @@ -1,5 +1,5 @@ Read: {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":32630,"rootPath":"/Users/dmitry/stripe/pay-server","rootUri":"file:///Users/dmitry/stripe/pay-server","capabilities":{"workspace":{"applyEdit":true,"workspaceEdit":{"documentChanges":true},"didChangeConfiguration":{"dynamicRegistration":true},"didChangeWatchedFiles":{"dynamicRegistration":true},"symbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]}},"executeCommand":{"dynamicRegistration":true},"configuration":true,"workspaceFolders":true},"textDocument":{"publishDiagnostics":{"relatedInformation":true},"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"contextSupport":true,"completionItem":{"snippetSupport":true,"commitCharactersSupport":true,"documentationFormat":["markdown","plaintext"],"deprecatedSupport":true,"preselectSupport":true},"completionItemKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]}},"hover":{"dynamicRegistration":true,"contentFormat":["markdown","plaintext"]},"signatureHelp":{"dynamicRegistration":true,"signatureInformation":{"documentationFormat":["markdown","plaintext"]}},"definition":{"dynamicRegistration":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"hierarchicalDocumentSymbolSupport":true},"codeAction":{"dynamicRegistration":true,"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["","quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}}},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true},"documentLink":{"dynamicRegistration":true},"typeDefinition":{"dynamicRegistration":true},"implementation":{"dynamicRegistration":true},"colorProvider":{"dynamicRegistration":true},"foldingRange":{"dynamicRegistration":true,"rangeLimit":5000,"lineFoldingOnly":true}}},"trace":"off","workspaceFolders":[{"uri":"file:///Users/dmitry/stripe/pay-server","name":"pay-server"}]}} -Write: {"jsonrpc":"2.0","id":0,"requestMethod":"initialize","result":{"capabilities":{"textDocumentSync":1,"hoverProvider":true,"completionProvider":{"triggerCharacters":[".",":","@","#"]},"signatureHelpProvider":{"triggerCharacters":["(",","]},"definitionProvider":true,"typeDefinitionProvider":true,"implementationProvider":true,"referencesProvider":true,"documentHighlightProvider":true,"documentSymbolProvider":true,"workspaceSymbolProvider":true,"codeActionProvider":{"codeActionKinds":["quickfix","source.fixAll.sorbet","refactor.extract"]},"documentFormattingProvider":false,"renameProvider":{"prepareProvider":true},"sorbetShowSymbolProvider":true}}} +Write: {"jsonrpc":"2.0","id":0,"requestMethod":"initialize","result":{"capabilities":{"textDocumentSync":1,"hoverProvider":true,"completionProvider":{"triggerCharacters":[".",":","@","#"]},"signatureHelpProvider":{"triggerCharacters":["(",","]},"definitionProvider":true,"typeDefinitionProvider":true,"implementationProvider":true,"referencesProvider":true,"documentHighlightProvider":true,"documentSymbolProvider":true,"workspaceSymbolProvider":true,"codeActionProvider":{"codeActionKinds":["quickfix","source.fixAll.sorbet","refactor.extract","refactor.rewrite"],"resolveProvider":true},"documentFormattingProvider":false,"renameProvider":{"prepareProvider":true},"sorbetShowSymbolProvider":true}}} Read: {"jsonrpc":"2.0","method":"initialized","params":{}} Read: {"jsonrpc":"2.0","method":"workspace/didChangeConfiguration","params":{"settings":{"ruby-typer":{}}}} Read: {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///Users/dmitry/stripe/pay-server/api/lib/compatibility/change/foo.rb","languageId":"ruby","version":1,"text":"# typed: true\nclass Foo\n def bar\n Foo\n end\nend"}}} diff --git a/test/lsp/lsp_test.bzl b/test/lsp/lsp_test.bzl index 0fc7bf7a84..117378b214 100644 --- a/test/lsp/lsp_test.bzl +++ b/test/lsp/lsp_test.bzl @@ -74,7 +74,7 @@ def protocol_tests(cc_files): "//main/lsp", "//payload", "//test/helpers", - "@doctest", - "@doctest//:doctest_main", + "@doctest//doctest", + "@doctest//doctest:main", ], ) diff --git a/test/lsp/multithreaded_protocol_test_corpus.cc b/test/lsp/multithreaded_protocol_test_corpus.cc index f62b5aa8e0..56fa5e2ed6 100644 --- a/test/lsp/multithreaded_protocol_test_corpus.cc +++ b/test/lsp/multithreaded_protocol_test_corpus.cc @@ -1,8 +1,8 @@ -#include "doctest.h" +#include "doctest/doctest.h" // ^ Violates linting rules, so include first. #include "absl/strings/match.h" #include "common/common.h" -#include "common/sort.h" +#include "common/sort/sort.h" #include "test/helpers/lsp.h" #include "test/lsp/ProtocolTest.h" diff --git a/test/lsp/no-trailing-newline/no-trailing-newline.rec b/test/lsp/no-trailing-newline/no-trailing-newline.rec index 5cd588f814..bb0ba44007 100644 --- a/test/lsp/no-trailing-newline/no-trailing-newline.rec +++ b/test/lsp/no-trailing-newline/no-trailing-newline.rec @@ -1,5 +1,5 @@ Read: {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"rootPath":null,"rootUri":null,"capabilities":{"workspace":{"applyEdit":true,"workspaceEdit":{"documentChanges":true},"didChangeConfiguration":{"dynamicRegistration":true},"didChangeWatchedFiles":{"dynamicRegistration":true},"symbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]}},"executeCommand":{"dynamicRegistration":true},"workspaceFolders":true},"textDocument":{"publishDiagnostics":{"relatedInformation":true},"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"contextSupport":true,"completionItem":{"snippetSupport":true,"commitCharactersSupport":true,"documentationFormat":["markdown","plaintext"],"deprecatedSupport":true,"preselectSupport":true},"completionItemKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]}},"hover":{"dynamicRegistration":true,"contentFormat":["markdown","plaintext"]},"signatureHelp":{"dynamicRegistration":true,"signatureInformation":{"documentationFormat":["markdown","plaintext"]}},"definition":{"dynamicRegistration":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"hierarchicalDocumentSymbolSupport":true},"codeAction":{"dynamicRegistration":true,"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["","quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}}},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true},"documentLink":{"dynamicRegistration":true},"typeDefinition":{"dynamicRegistration":true},"implementation":{"dynamicRegistration":true},"colorProvider":{"dynamicRegistration":true},"foldingRange":{"dynamicRegistration":true,"rangeLimit":5000,"lineFoldingOnly":true}}},"trace":"off","workspaceFolders":null}} -Write: {"jsonrpc":"2.0","id":0,"requestMethod":"initialize","result":{"capabilities":{"textDocumentSync":1,"hoverProvider":true,"completionProvider":{"triggerCharacters":[".",":","@","#"]},"signatureHelpProvider":{"triggerCharacters":["(",","]},"definitionProvider":true,"typeDefinitionProvider":true,"implementationProvider":true,"referencesProvider":true,"documentHighlightProvider":true,"documentSymbolProvider":true,"workspaceSymbolProvider":true,"codeActionProvider":{"codeActionKinds":["quickfix","source.fixAll.sorbet","refactor.extract"]},"documentFormattingProvider":false,"renameProvider":{"prepareProvider":true},"sorbetShowSymbolProvider":true}}} +Write: {"jsonrpc":"2.0","id":0,"requestMethod":"initialize","result":{"capabilities":{"textDocumentSync":1,"hoverProvider":true,"completionProvider":{"triggerCharacters":[".",":","@","#"]},"signatureHelpProvider":{"triggerCharacters":["(",","]},"definitionProvider":true,"typeDefinitionProvider":true,"implementationProvider":true,"referencesProvider":true,"documentHighlightProvider":true,"documentSymbolProvider":true,"workspaceSymbolProvider":true,"codeActionProvider":{"codeActionKinds":["quickfix","source.fixAll.sorbet","refactor.extract","refactor.rewrite"],"resolveProvider":true},"documentFormattingProvider":false,"renameProvider":{"prepareProvider":true},"sorbetShowSymbolProvider":true}}} Read: {"jsonrpc":"2.0","method":"initialized","params":{}} Read: {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"inmemory://model/default","languageId":"ruby","version":1,"text":"# typed: true\nfoo"}}} Write: {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"inmemory://model/default","diagnostics":[{"range":{"start":{"line":1,"character":0},"end":{"line":1,"character":3}},"severity":1,"code":7003,"codeDescription":{"href":"https://srb.help/7003"},"message":"Method `foo` does not exist on `T.class_of()`","relatedInformation":[]}]}} diff --git a/test/lsp/protocol_test_corpus.cc b/test/lsp/protocol_test_corpus.cc index 9e604e1e6d..ec755a89ff 100644 --- a/test/lsp/protocol_test_corpus.cc +++ b/test/lsp/protocol_test_corpus.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" // ^ Violates linting rules, so include first. #include "ProtocolTest.h" #include "absl/strings/match.h" @@ -566,6 +566,26 @@ TEST_CASE_FIXTURE(ProtocolTest, "DoesNotCrashOnNonWorkspaceURIs") { getLSPResponsesFor(*lspWrapper, make_unique(move(didOpenNotif))); } +// Tests that Sorbet does not crash when attempting to format a file URI outside of the workspace. +TEST_CASE_FIXTURE(ProtocolTest, "DoesNotCrashOnFormattingNonWorkspaceURIs") { + auto initOptions = make_unique(); + initOptions->supportsSorbetURIs = true; + + // Manually invoke to customize rootURI and rootPath. + auto initializeResponses = sorbet::test::initializeLSP( + "/Users/jvilk/stripe/areallybigfoldername", "file://Users/jvilk/stripe/areallybigfoldername", *lspWrapper, + nextId, false, false, make_optional(move(initOptions))); + + auto fileUri = "file:///Users/jvilk/Desktop/test.rb"; + auto documentFormattingParams = make_unique( + make_unique(string(fileUri)), make_unique(0, 0)); + auto resp = send(LSPMessage(make_unique("2.0", nextId++, LSPMethod::TextDocumentFormatting, + move(documentFormattingParams)))); + // Just assert that this doesn't crash -- it's really a no-op + INFO("Expected only a single response to the formatting request."); + REQUIRE_EQ(resp.size(), 1); +} + // Tests that Sorbet reports metrics about the request's response status for certain requests TEST_CASE_FIXTURE(ProtocolTest, "RequestReportsEmptyResultsMetrics") { assertDiagnostics(initializeLSP(), {}); diff --git a/test/lsp/watchman_test_corpus.cc b/test/lsp/watchman_test_corpus.cc index ca1c0b0d2b..760c245964 100644 --- a/test/lsp/watchman_test_corpus.cc +++ b/test/lsp/watchman_test_corpus.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" // ^ Violates linting rules, so include first. #include "ProtocolTest.h" #include "common/common.h" diff --git a/test/lsp/workspaceSymbol/workspaceSymbol.rec b/test/lsp/workspaceSymbol/workspaceSymbol.rec index daf021e2bb..dbabf36ad1 100644 --- a/test/lsp/workspaceSymbol/workspaceSymbol.rec +++ b/test/lsp/workspaceSymbol/workspaceSymbol.rec @@ -1,5 +1,5 @@ Read: {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":9921,"rootPath":"/Users/sctu/stripe/pay-server","rootUri":"file:///Users/sctu/stripe/pay-server","capabilities":{"workspace":{"applyEdit":true,"workspaceEdit":{"documentChanges":true},"didChangeConfiguration":{"dynamicRegistration":true},"didChangeWatchedFiles":{"dynamicRegistration":true},"symbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]}},"executeCommand":{"dynamicRegistration":true},"configuration":true,"workspaceFolders":true},"textDocument":{"publishDiagnostics":{"relatedInformation":true},"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"contextSupport":true,"completionItem":{"snippetSupport":true,"commitCharactersSupport":true,"documentationFormat":["markdown","plaintext"]},"completionItemKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]}},"hover":{"dynamicRegistration":true,"contentFormat":["markdown","plaintext"]},"signatureHelp":{"dynamicRegistration":true,"signatureInformation":{"documentationFormat":["markdown","plaintext"]}},"definition":{"dynamicRegistration":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]}},"codeAction":{"dynamicRegistration":true},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true},"documentLink":{"dynamicRegistration":true},"typeDefinition":{"dynamicRegistration":true},"implementation":{"dynamicRegistration":true},"colorProvider":{"dynamicRegistration":true}}},"trace":"off","workspaceFolders":[{"uri":"file:///Users/sctu/stripe/pay-server","name":"pay-server"}]}} -Write: {"jsonrpc":"2.0","id":0,"requestMethod":"initialize","result":{"capabilities":{"textDocumentSync":1,"hoverProvider":true,"completionProvider":{"triggerCharacters":[".",":","@","#"]},"signatureHelpProvider":{"triggerCharacters":["(",","]},"definitionProvider":true,"typeDefinitionProvider":true,"implementationProvider":true,"referencesProvider":true,"documentHighlightProvider":true,"documentSymbolProvider":true,"workspaceSymbolProvider":true,"codeActionProvider":{"codeActionKinds":["quickfix","source.fixAll.sorbet","refactor.extract"]},"documentFormattingProvider":false,"renameProvider":{"prepareProvider":true},"sorbetShowSymbolProvider":true}}} +Write: {"jsonrpc":"2.0","id":0,"requestMethod":"initialize","result":{"capabilities":{"textDocumentSync":1,"hoverProvider":true,"completionProvider":{"triggerCharacters":[".",":","@","#"]},"signatureHelpProvider":{"triggerCharacters":["(",","]},"definitionProvider":true,"typeDefinitionProvider":true,"implementationProvider":true,"referencesProvider":true,"documentHighlightProvider":true,"documentSymbolProvider":true,"workspaceSymbolProvider":true,"codeActionProvider":{"codeActionKinds":["quickfix","source.fixAll.sorbet","refactor.extract","refactor.rewrite"],"resolveProvider":true},"documentFormattingProvider":false,"renameProvider":{"prepareProvider":true},"sorbetShowSymbolProvider":true}}} Read: {"jsonrpc":"2.0","method":"initialized","params":{}} Read: {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///Users/sctu/stripe/pay-server/foo.rb","languageId":"ruby","version":1,"text":"# frozen_string_literal: true\n# typed: true\n\n\n\nclass Opus::Foo; extend T::Sig\n sig {returns(String)}\n def uniquebar\n \"bar\"\n end\n\n sig {returns(String)}\n def baz\n uniquebar + \"baz\"\n end\nend\n"}}} Read: {"jsonrpc":"2.0","id":1,"method":"workspace/symbol","params":{"query":"uniquebar"}} diff --git a/test/lsp_test_runner.cc b/test/lsp_test_runner.cc index 729d2ab75f..2d9ebc9439 100644 --- a/test/lsp_test_runner.cc +++ b/test/lsp_test_runner.cc @@ -1,12 +1,12 @@ -#include "doctest.h" +#include "doctest/doctest.h" #include // has to go first as it violates our requirements #include "absl/strings/match.h" #include "common/FileOps.h" #include "common/common.h" -#include "common/formatting.h" -#include "common/sort.h" +#include "common/sort/sort.h" +#include "common/strings/formatting.h" #include "common/web_tracer_framework/tracing.h" #include "main/lsp/LSPConfiguration.h" #include "test/helpers/expectations.h" @@ -547,9 +547,12 @@ TEST_CASE("LSPTest") { opts->ruby3KeywordArgs = BooleanPropertyAssertion::getValue("experimental-ruby3-keyword-args", assertions).value_or(false); opts->stripeMode = BooleanPropertyAssertion::getValue("stripe-mode", assertions).value_or(false); + opts->outOfOrderReferenceChecksEnabled = + BooleanPropertyAssertion::getValue("check-out-of-order-constant-references", assertions).value_or(false); opts->requiresAncestorEnabled = BooleanPropertyAssertion::getValue("enable-experimental-requires-ancestor", assertions).value_or(false); opts->stripePackages = BooleanPropertyAssertion::getValue("enable-packager", assertions).value_or(false); + if (opts->stripePackages) { auto extraDirUnderscore = StringPropertyAssertion::getValue("extra-package-files-directory-prefix-underscore", assertions); @@ -562,10 +565,15 @@ TEST_CASE("LSPTest") { opts->extraPackageFilesDirectorySlashPrefixes.emplace_back(extraDirSlash.value()); } opts->secondaryTestPackageNamespaces.emplace_back("Critic"); + auto skipImportVisibility = + StringPropertyAssertion::getValue("skip-package-import-visibility-check-for", assertions); + if (skipImportVisibility.has_value()) { + opts->skipPackageImportVisibilityCheckFor.emplace_back(skipImportVisibility.value()); + } + opts->secondaryTestPackageNamespaces.emplace_back("Critic"); } opts->disableWatchman = true; opts->rubyfmtPath = "test/testdata/lsp/rubyfmt-stub/rubyfmt"; - opts->lspExperimentalFastPathEnabled = true; // Set to a number that is reasonable large for tests, but small enough that we can have a test to handle // this edge case. If you change this number, `fast_path/{too_many_files,not_enough_files,initialize}` will @@ -597,6 +605,8 @@ TEST_CASE("LSPTest") { string rootUri = fmt::format("file://{}", rootPath); auto sorbetInitOptions = make_unique(); sorbetInitOptions->enableTypecheckInfo = true; + sorbetInitOptions->highlightUntyped = + BooleanPropertyAssertion::getValue("highlight-untyped-values", assertions).value_or(false); auto initializedResponses = initializeLSP(rootPath, rootUri, *lspWrapper, nextId, true, shouldUseCodeActionResolve, move(sorbetInitOptions)); INFO("Should not receive any response to 'initialized' message."); @@ -645,8 +655,14 @@ TEST_CASE("LSPTest") { } auto responses = getLSPResponsesFor(*lspWrapper, move(updates)); updateDiagnostics(config, testFileUris, responses, diagnostics); - slowPathPassed = ErrorAssertion::checkAll( + bool errorAssertionsPassed = ErrorAssertion::checkAll( test.sourceFileContents, RangeAssertion::getErrorAssertions(assertions), diagnostics, errorPrefixes[i]); + + bool untypedAssertionsPassed = + UntypedAssertion::checkAll(test.sourceFileContents, RangeAssertion::getUntypedAssertions(assertions), + diagnostics, errorPrefixes[i]); + + slowPathPassed = errorAssertionsPassed && untypedAssertionsPassed; } } @@ -822,6 +838,9 @@ TEST_CASE("LSPTest") { // Hover assertions HoverAssertion::checkAll(assertions, test.sourceFileContents, *lspWrapper, nextId); + // Hover multiline assertions + HoverLineAssertion::checkAll(assertions, test.sourceFileContents, *lspWrapper, nextId); + // sorbet/showSymbol assertions ShowSymbolAssertion::checkAll(assertions, test.sourceFileContents, *lspWrapper, nextId); @@ -921,6 +940,9 @@ TEST_CASE("LSPTest") { // Check any new HoverAssertions in the updates. HoverAssertion::checkAll(assertions, updatesAndContents, *lspWrapper, nextId); + + // Check and new HoverMultilneAsserions assertions + HoverLineAssertion::checkAll(assertions, test.sourceFileContents, *lspWrapper, nextId); } } diff --git a/test/parser_test_runner.cc b/test/parser_test_runner.cc index ee5256830e..f3373da8e2 100644 --- a/test/parser_test_runner.cc +++ b/test/parser_test_runner.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" #include // has to go first as it violates our requirements @@ -15,8 +15,8 @@ #include "class_flatten/class_flatten.h" #include "common/FileOps.h" #include "common/common.h" -#include "common/formatting.h" -#include "common/sort.h" +#include "common/sort/sort.h" +#include "common/strings/formatting.h" #include "common/web_tracer_framework/tracing.h" #include "core/Error.h" #include "core/ErrorCollector.h" diff --git a/test/pipeline_test.bzl b/test/pipeline_test.bzl index 64a99334c6..1e6190b263 100644 --- a/test/pipeline_test.bzl +++ b/test/pipeline_test.bzl @@ -100,6 +100,9 @@ def single_package_rbi_test(name, rb_files): name = name, rb_files = rb_files, size = "small", + # This is to get the test to run on the compiler build job, + # so we can avoid building ruby on the test-static-sanitized job. + tags = ["compiler"], ) _TEST_RUNNERS = { diff --git a/test/pipeline_test_runner.cc b/test/pipeline_test_runner.cc index 79eaf5634f..16bb7c5d0c 100644 --- a/test/pipeline_test_runner.cc +++ b/test/pipeline_test_runner.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" #include // has to go first as it violates our requirements @@ -15,14 +15,15 @@ #include "class_flatten/class_flatten.h" #include "common/FileOps.h" #include "common/common.h" -#include "common/formatting.h" -#include "common/sort.h" +#include "common/sort/sort.h" +#include "common/strings/formatting.h" #include "common/web_tracer_framework/tracing.h" #include "core/Error.h" #include "core/ErrorCollector.h" #include "core/ErrorQueue.h" #include "core/Unfreeze.h" #include "core/errors/namer.h" +#include "core/errors/resolver.h" #include "core/serialize/serialize.h" #include "definition_validator/validator.h" #include "infer/infer.h" @@ -201,7 +202,6 @@ TEST_CASE("PerPhaseTest") { // NOLINT } gs->censorForSnapshotTests = true; - gs->lspExperimentalFastPathEnabled = true; auto workers = WorkerPool::create(0, gs->tracer()); auto assertions = RangeAssertion::parseAssertions(test.sourceFileContents); @@ -215,6 +215,10 @@ TEST_CASE("PerPhaseTest") { // NOLINT gs->suppressErrorClass(core::errors::Namer::MultipleBehaviorDefs.code); } + if (!BooleanPropertyAssertion::getValue("check-out-of-order-constant-references", assertions).value_or(false)) { + gs->suppressErrorClass(core::errors::Resolver::OutOfOrderConstantAccess.code); + } + if (BooleanPropertyAssertion::getValue("no-stdlib", assertions).value_or(false)) { gs->initEmpty(); } else { @@ -295,32 +299,28 @@ TEST_CASE("PerPhaseTest") { // NOLINT ast::ParsedFile localNamed; - if (!test.expectations.contains("autogen")) { - // Rewriter - ast::ParsedFile rewriten; - { - core::UnfreezeNameTable nameTableAccess(*gs); // enters original strings + // Rewriter + ast::ParsedFile rewriten; + { + core::UnfreezeNameTable nameTableAccess(*gs); // enters original strings - core::MutableContext ctx(*gs, core::Symbols::root(), desugared.file); - rewriten = testSerialize( - *gs, ast::ParsedFile{rewriter::Rewriter::run(ctx, move(desugared.tree)), desugared.file}); - } + core::MutableContext ctx(*gs, core::Symbols::root(), desugared.file); + bool previous = gs->runningUnderAutogen; + gs->runningUnderAutogen = test.expectations.contains("autogen"); + rewriten = + testSerialize(*gs, ast::ParsedFile{rewriter::Rewriter::run(ctx, move(desugared.tree)), desugared.file}); + gs->runningUnderAutogen = previous; + } - handler.addObserved(*gs, "rewrite-tree", [&]() { return rewriten.tree.toString(*gs); }); - handler.addObserved(*gs, "rewrite-tree-raw", [&]() { return rewriten.tree.showRaw(*gs); }); + handler.addObserved(*gs, "rewrite-tree", [&]() { return rewriten.tree.toString(*gs); }); + handler.addObserved(*gs, "rewrite-tree-raw", [&]() { return rewriten.tree.showRaw(*gs); }); - core::MutableContext ctx(*gs, core::Symbols::root(), desugared.file); - localNamed = testSerialize(*gs, local_vars::LocalVars::run(ctx, move(rewriten))); + core::MutableContext ctx(*gs, core::Symbols::root(), desugared.file); + localNamed = testSerialize(*gs, local_vars::LocalVars::run(ctx, move(rewriten))); + + handler.addObserved(*gs, "index-tree", [&]() { return localNamed.tree.toString(*gs); }); + handler.addObserved(*gs, "index-tree-raw", [&]() { return localNamed.tree.showRaw(*gs); }); - handler.addObserved(*gs, "index-tree", [&]() { return localNamed.tree.toString(*gs); }); - handler.addObserved(*gs, "index-tree-raw", [&]() { return localNamed.tree.showRaw(*gs); }); - } else { - core::MutableContext ctx(*gs, core::Symbols::root(), desugared.file); - localNamed = testSerialize(*gs, local_vars::LocalVars::run(ctx, move(desugared))); - if (test.expectations.contains("rewrite-tree-raw") || test.expectations.contains("rewrite-tree")) { - FAIL_CHECK("Running Rewriter passes with autogen isn't supported"); - } - } trees.emplace_back(move(localNamed)); } @@ -331,6 +331,7 @@ TEST_CASE("PerPhaseTest") { // NOLINT vector extraPackageFilesDirectorySlashPrefixes; vector secondaryTestPackageNamespaces = {"Critic"}; vector skipRBIExportEnforcementDirs; + vector skipImportVisibilityCheckFor; auto extraDirUnderscore = StringPropertyAssertion::getValue("extra-package-files-directory-prefix-underscore", assertions); @@ -344,11 +345,18 @@ TEST_CASE("PerPhaseTest") { // NOLINT extraPackageFilesDirectorySlashPrefixes.emplace_back(extraDirSlash.value()); } + auto skipImportVisibility = + StringPropertyAssertion::getValue("skip-package-import-visibility-check-for", assertions); + if (skipImportVisibility.has_value()) { + skipImportVisibilityCheckFor.emplace_back(skipImportVisibility.value()); + } + { core::UnfreezeNameTable packageNS(*gs); core::packages::UnfreezePackages unfreezeToEnterPackagerOptionsPackageDB = gs->unfreezePackages(); gs->setPackagerOptions(secondaryTestPackageNamespaces, extraPackageFilesDirectoryUnderscorePrefixes, - extraPackageFilesDirectorySlashPrefixes, {}, "PACKAGE_ERROR_HINT"); + extraPackageFilesDirectorySlashPrefixes, {}, skipImportVisibilityCheckFor, + "PACKAGE_ERROR_HINT"); } // Packager runs over all trees. @@ -752,6 +760,7 @@ TEST_CASE("PerPhaseTest") { // NOLINT } } + bool ranIncremantalNamer = false; { // namer for (auto &tree : trees) { @@ -765,6 +774,7 @@ TEST_CASE("PerPhaseTest") { // NOLINT // Here, to complement those tests, we just run Namer::run (not Namer::runIncremental) // to stress the codepath where Namer is not tasked with deleting anything when run for // the fast path. + ENFORCE(!ranIncremantalNamer); vTmp = move(namer::Namer::run(*gs, move(vTmp), *workers, &foundHashes).result()); tree = testSerialize(*gs, move(vTmp[0])); @@ -774,7 +784,7 @@ TEST_CASE("PerPhaseTest") { // NOLINT } // resolver - trees = move(resolver::Resolver::runIncremental(*gs, move(trees)).result()); + trees = move(resolver::Resolver::runIncremental(*gs, move(trees), ranIncremantalNamer).result()); if (enablePackager) { trees = packager::VisibilityChecker::run(*gs, *workers, move(trees)); diff --git a/test/pkg_autocorrects_test.cc b/test/pkg_autocorrects_test.cc index 6e4d5f99dc..579fcb4756 100644 --- a/test/pkg_autocorrects_test.cc +++ b/test/pkg_autocorrects_test.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" #include "ast/ast.h" #include "ast/desugar/Desugar.h" diff --git a/test/scip/testdata/classes.snapshot.rb b/test/scip/testdata/classes.snapshot.rb index 567a618e38..46b437b3de 100644 --- a/test/scip/testdata/classes.snapshot.rb +++ b/test/scip/testdata/classes.snapshot.rb @@ -37,7 +37,7 @@ def local_class() localClass = Class.new # ^^^^^^^^^^ definition local 1~#552113551 # ^^^^^ reference [..] Class# -# ^^^ reference [..] Class#new(). +# ^^^ reference [..] ``#new(). # Technically, this is not supported by Sorbet (https://srb.help/3001), # but make sure we don't crash or do something weird. def localClass.myMethod() diff --git a/test/scip/testdata/hoverdocs.snapshot.rb b/test/scip/testdata/hoverdocs.snapshot.rb index d4a37815fc..bd6ec65d4f 100644 --- a/test/scip/testdata/hoverdocs.snapshot.rb +++ b/test/scip/testdata/hoverdocs.snapshot.rb @@ -224,7 +224,6 @@ def f1 #^^^ reference [..] Sorbet#Private#``#sig(). # ^^^^^^^ reference [..] T#Private#Methods#DeclBuilder#returns(). # ^ reference [..] T# -# ^^^^^^^^^^ reference [..] Sorbet#Private#Static# def f2 # ^^ definition [..] Object#f2(). # documentation @@ -255,7 +254,6 @@ def f3 # undocumented global function #^^^ reference [..] Sorbet#Private#``#sig(). # ^^^^^^^ reference [..] T#Private#Methods#DeclBuilder#returns(). # ^ reference [..] T# -# ^^^^^^^^^^ reference [..] Sorbet#Private#Static# def f4 # another undocumented global function # ^^ definition [..] Object#f4(). # documentation diff --git a/test/scip/testdata/rescue.snapshot.rb b/test/scip/testdata/rescue.snapshot.rb index bcd306a0fa..32d4416ca9 100644 --- a/test/scip/testdata/rescue.snapshot.rb +++ b/test/scip/testdata/rescue.snapshot.rb @@ -21,18 +21,16 @@ def f raise 'This exception will be rescued!' # ^^^^^ reference [..] Kernel#raise(). rescue MyError => e1 -# ^^^^^^^ reference local 1~#3809224601 # ^^^^^^^ reference [..] MyError# # ^^ definition local 1~#3809224601 handle(e1) # ^^^^^^ reference [..] Object#handle(). # ^^ reference local 1~#3809224601 rescue StandardError => e2 -# ^^^^^^^^^^^^^ reference local 2~#3809224601 # ^^^^^^^^^^^^^ reference [..] StandardError# -# ^^ definition local 2~#3809224601 +# ^^ definition local 3~#3809224601 handle(e2) # ^^^^^^ reference [..] Object#handle(). -# ^^ reference local 2~#3809224601 +# ^^ reference local 3~#3809224601 end end diff --git a/test/scip/testdata/singleton.snapshot.rb b/test/scip/testdata/singleton.snapshot.rb index 4459e21c0c..62d2ee46d1 100644 --- a/test/scip/testdata/singleton.snapshot.rb +++ b/test/scip/testdata/singleton.snapshot.rb @@ -4,7 +4,6 @@ class A # ^ definition [..] A# include Singleton # ^^^^^^^ reference [..] Module#include(). -# ^^^^^^^^^^^^^^^^^ definition [..] ``#instance(). # ^^^^^^^^^ reference [..] Singleton# end @@ -17,7 +16,6 @@ class C # ^ definition [..] C# include Singleton # ^^^^^^^ reference [..] Module#include(). -# ^^^^^^^^^^^^^^^^^ definition [..] ``#instance(). # ^^^^^^^^^ reference [..] Singleton# extend T::Helpers # ^^^^^^ reference [..] Kernel#extend(). @@ -28,9 +26,9 @@ def f # ^ definition [..] Object#f(). return [A.instance, B.instance, C.instance] # ^ reference [..] A# -# ^^^^^^^^ reference [..] ``#instance(). +# ^^^^^^^^ reference [..] Singleton#SingletonClassMethods#instance(). # ^ reference [..] B# -# ^^^^^^^^ reference [..] ``#instance(). +# ^^^^^^^^ reference [..] Singleton#SingletonClassMethods#instance(). # ^ reference [..] C# -# ^^^^^^^^ reference [..] ``#instance(). +# ^^^^^^^^ reference [..] Singleton#SingletonClassMethods#instance(). end diff --git a/test/scip_test_runner.cc b/test/scip_test_runner.cc index baf9db49b2..e39834bcd8 100644 --- a/test/scip_test_runner.cc +++ b/test/scip_test_runner.cc @@ -1,4 +1,4 @@ -#include "doctest.h" +#include "doctest/doctest.h" #include "proto/SCIP.pb.h" #include // has to go first as it violates our requirements @@ -26,8 +26,8 @@ #include "class_flatten/class_flatten.h" #include "common/FileOps.h" #include "common/common.h" -#include "common/formatting.h" -#include "common/sort.h" +#include "common/sort/sort.h" +#include "common/strings/formatting.h" #include "common/web_tracer_framework/tracing.h" #include "core/Error.h" #include "core/ErrorCollector.h" diff --git a/test/testdata/autogen/generator.rb.autogen.exp b/test/testdata/autogen/generator.rb.autogen.exp index 3be5a5b94d..1bdbd83d1f 100644 --- a/test/testdata/autogen/generator.rb.autogen.exp +++ b/test/testdata/autogen/generator.rb.autogen.exp @@ -36,33 +36,33 @@ requires: [] loc=test/testdata/autogen/generator.rb:4 is_defining_ref=0 [ref id=2] + scope=[Gener] + name=[HydraResource] + nesting=[[Gener]] + resolved=[Gener HydraResource] + loc=test/testdata/autogen/generator.rb:10 + is_defining_ref=0 +[ref id=3] scope=[Gener] name=[HydraResource] nesting=[[Gener]] resolved=[Gener HydraResource] loc=test/testdata/autogen/generator.rb:6 is_defining_ref=1 -[ref id=3] +[ref id=4] scope=[Gener] name=[T] nesting=[[Gener]] resolved=[T] loc=test/testdata/autogen/generator.rb:6 is_defining_ref=0 -[ref id=4] +[ref id=5] scope=[Gener] name=[DOES_NOT_EXIST] nesting=[[Gener]] resolved=[] loc=test/testdata/autogen/generator.rb:6 is_defining_ref=0 -[ref id=5] - scope=[Gener] - name=[HydraResource] - nesting=[[Gener]] - resolved=[Gener HydraResource] - loc=test/testdata/autogen/generator.rb:10 - is_defining_ref=0 [ref id=6] scope=[Gener] name=[C] diff --git a/test/testdata/autogen/has_attached_class.rb b/test/testdata/autogen/has_attached_class.rb new file mode 100644 index 0000000000..9ef0261199 --- /dev/null +++ b/test/testdata/autogen/has_attached_class.rb @@ -0,0 +1,12 @@ +# typed: true + +module HasAttachedClassExample + extend T::Sig + extend T::Generic + + abstract! + has_attached_class! + + sig {abstract.returns(T.attached_class)} + def example; end +end diff --git a/test/testdata/autogen/has_attached_class.rb.autogen.exp b/test/testdata/autogen/has_attached_class.rb.autogen.exp new file mode 100644 index 0000000000..c3b01f3056 --- /dev/null +++ b/test/testdata/autogen/has_attached_class.rb.autogen.exp @@ -0,0 +1,41 @@ +# ParsedFile: test/testdata/autogen/has_attached_class.rb +requires: [] +## defs: +[def id=0] + type=module + defines_behavior=0 + is_empty=0 +[def id=1] + type=module + defines_behavior=1 + is_empty=0 + defining_ref=[HasAttachedClassExample] +## refs: +[ref id=0] + scope=[] + name=[HasAttachedClassExample] + nesting=[] + resolved=[HasAttachedClassExample] + loc=test/testdata/autogen/has_attached_class.rb:3 + is_defining_ref=1 +[ref id=1] + scope=[HasAttachedClassExample] + name=[T Sig] + nesting=[[HasAttachedClassExample]] + resolved=[T Sig] + loc=test/testdata/autogen/has_attached_class.rb:4 + is_defining_ref=0 +[ref id=2] + scope=[HasAttachedClassExample] + name=[T Generic] + nesting=[[HasAttachedClassExample]] + resolved=[T Generic] + loc=test/testdata/autogen/has_attached_class.rb:5 + is_defining_ref=0 +[ref id=3] + scope=[HasAttachedClassExample] + name=[T] + nesting=[[HasAttachedClassExample]] + resolved=[T] + loc=test/testdata/autogen/has_attached_class.rb:10 + is_defining_ref=0 diff --git a/test/testdata/cfg/array.rb.cfg-text.exp b/test/testdata/cfg/array.rb.cfg-text.exp index 77413ae933..44d0b7b2e1 100644 --- a/test/testdata/cfg/array.rb.cfg-text.exp +++ b/test/testdata/cfg/array.rb.cfg-text.exp @@ -97,9 +97,9 @@ bb2[rubyRegionId=1, firstDead=-1](: T.class_of(TestArray), $7: Sorbet::Private::Static::Void, $8: T.class_of(TestArray)): $3: Sorbet::Private::Static::Void = Solve<$7, sig> : T.class_of(TestArray) = $8 - $17: T.class_of(Sorbet::Private::Static) = alias - $19: Sorbet::Private::Static::Void = $17: T.class_of(Sorbet::Private::Static).sig(: T.class_of(TestArray)) - $20: T.class_of(TestArray) = + $16: T.class_of(Sorbet::Private::Static) = alias + $18: Sorbet::Private::Static::Void = $16: T.class_of(Sorbet::Private::Static).sig(: T.class_of(TestArray)) + $19: T.class_of(TestArray) = -> bb6 # backedges @@ -107,37 +107,37 @@ bb3[rubyRegionId=0, firstDead=-1]($7: Sorbet::Private::Stat bb5[rubyRegionId=1, firstDead=4](: T.class_of(TestArray), $7: Sorbet::Private::Static::Void, $8: T.class_of(TestArray)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $13: T.class_of(Integer) = alias - $10: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.returns($13: T.class_of(Integer)) - $14: T.noreturn = blockreturn $10: T::Private::Methods::DeclBuilder + $12: T.class_of(Integer) = alias + $9: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.returns($12: T.class_of(Integer)) + $13: T.noreturn = blockreturn $9: T::Private::Methods::DeclBuilder -> bb2 # backedges # - bb3(rubyRegionId=0) # - bb9(rubyRegionId=2) -bb6[rubyRegionId=2, firstDead=-1](: T.class_of(TestArray), $19: Sorbet::Private::Static::Void, $20: T.class_of(TestArray)): +bb6[rubyRegionId=2, firstDead=-1](: T.class_of(TestArray), $18: Sorbet::Private::Static::Void, $19: T.class_of(TestArray)): # outerLoops: 1 -> (NilClass ? bb9 : bb7) # backedges # - bb6(rubyRegionId=2) -bb7[rubyRegionId=0, firstDead=6]($19: Sorbet::Private::Static::Void, $20: T.class_of(TestArray)): - $15: Sorbet::Private::Static::Void = Solve<$19, sig> - : T.class_of(TestArray) = $20 - $30: T.class_of(T::Sig) = alias - $32: T.class_of(T) = alias - $27: T.class_of(TestArray) = : T.class_of(TestArray).extend($30: T.class_of(T::Sig)) +bb7[rubyRegionId=0, firstDead=6]($18: Sorbet::Private::Static::Void, $19: T.class_of(TestArray)): + $14: Sorbet::Private::Static::Void = Solve<$18, sig> + : T.class_of(TestArray) = $19 + $28: T.class_of(T::Sig) = alias + $30: T.class_of(T) = alias + $25: T.class_of(TestArray) = : T.class_of(TestArray).extend($28: T.class_of(T::Sig)) : T.noreturn = return $2: NilClass -> bb1 # backedges # - bb6(rubyRegionId=2) -bb9[rubyRegionId=2, firstDead=4](: T.class_of(TestArray), $19: Sorbet::Private::Static::Void, $20: T.class_of(TestArray)): +bb9[rubyRegionId=2, firstDead=4](: T.class_of(TestArray), $18: Sorbet::Private::Static::Void, $19: T.class_of(TestArray)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $25: T.class_of(String) = alias - $22: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.returns($25: T.class_of(String)) - $26: T.noreturn = blockreturn $22: T::Private::Methods::DeclBuilder + $23: T.class_of(String) = alias + $20: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.returns($23: T.class_of(String)) + $24: T.noreturn = blockreturn $20: T::Private::Methods::DeclBuilder -> bb6 } diff --git a/test/testdata/cfg/block_in_deadcode.rb.cfg-text.exp b/test/testdata/cfg/block_in_deadcode.rb.cfg-text.exp index 717f34e3a0..71b297f1f8 100644 --- a/test/testdata/cfg/block_in_deadcode.rb.cfg-text.exp +++ b/test/testdata/cfg/block_in_deadcode.rb.cfg-text.exp @@ -9,8 +9,8 @@ bb0[rubyRegionId=0, firstDead=-1](): # - bb3(rubyRegionId=0) # - bb5(rubyRegionId=1) bb1[rubyRegionId=0, firstDead=-1](): - $10 = - $11 = $10.inner() + $9 = + $10 = $9.inner() -> bb1 # backedges @@ -32,7 +32,7 @@ bb3[rubyRegionId=0, firstDead=3](: Object, $4: Sorbet bb5[rubyRegionId=1, firstDead=2](: Object): # outerLoops: 1 : Object = loadSelf(outer) - $8: T.noreturn = return $9: NilClass + $7: T.noreturn = return $8: NilClass -> bb1 } diff --git a/test/testdata/cfg/break.rb.cfg-text.exp b/test/testdata/cfg/break.rb.cfg-text.exp index 362551cb1f..cec3cebdca 100644 --- a/test/testdata/cfg/break.rb.cfg-text.exp +++ b/test/testdata/cfg/break.rb.cfg-text.exp @@ -93,13 +93,13 @@ bb2[rubyRegionId=1, firstDead=-1](: T.class_of(), $7: Sorbet::Private::Static::Void, $8: T.class_of()): $3: Sorbet::Private::Static::Void = Solve<$7, sig> : T.class_of() = $8 - $30: T.class_of(T::Sig) = alias - $32: T.class_of(T) = alias - $27: T.class_of() = : T.class_of().extend($30: T.class_of(T::Sig)) - $36: T.untyped = : T.class_of().foo() - $34: NilClass = : T.class_of().puts($36: T.untyped) - $41: Sorbet::Private::Static::Void = : T.class_of().bar() - $42: T.class_of() = + $29: T.class_of(T::Sig) = alias + $31: T.class_of(T) = alias + $26: T.class_of() = : T.class_of().extend($29: T.class_of(T::Sig)) + $35: T.untyped = : T.class_of().foo() + $33: NilClass = : T.class_of().puts($35: T.untyped) + $40: Sorbet::Private::Static::Void = : T.class_of().bar() + $41: T.class_of() = -> bb6 # backedges @@ -107,122 +107,122 @@ bb3[rubyRegionId=0, firstDead=-1]($7: Sorbet::Private::Stat bb5[rubyRegionId=1, firstDead=13](: T.class_of(), $7: Sorbet::Private::Static::Void, $8: T.class_of()): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $13: Symbol(:blk) = :blk - $18: T.class_of(T) = alias - $16: T.class_of(T.proc) = $18: T.class_of(T).proc() - $19: Symbol(:x) = :x - $21: T.class_of(Integer) = alias - $15: Runtime object representing type: T.proc.params(arg0: Integer).returns() = $16: T.class_of(T.proc).params($19: Symbol(:x), $21: T.class_of(Integer)) - $23: T.class_of(String) = alias - $14: Runtime object representing type: T.proc.params(arg0: Integer).returns(String) = $15: Runtime object representing type: T.proc.params(arg0: Integer).returns().returns($23: T.class_of(String)) - $11: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($13: Symbol(:blk), $14: Runtime object representing type: T.proc.params(arg0: Integer).returns(String)) - $25: T.class_of(String) = alias - $10: T::Private::Methods::DeclBuilder = $11: T::Private::Methods::DeclBuilder.returns($25: T.class_of(String)) - $26: T.noreturn = blockreturn $10: T::Private::Methods::DeclBuilder + $12: Symbol(:blk) = :blk + $17: T.class_of(T) = alias + $15: T.class_of(T.proc) = $17: T.class_of(T).proc() + $18: Symbol(:x) = :x + $20: T.class_of(Integer) = alias + $14: Runtime object representing type: T.proc.params(arg0: Integer).returns() = $15: T.class_of(T.proc).params($18: Symbol(:x), $20: T.class_of(Integer)) + $22: T.class_of(String) = alias + $13: Runtime object representing type: T.proc.params(arg0: Integer).returns(String) = $14: Runtime object representing type: T.proc.params(arg0: Integer).returns().returns($22: T.class_of(String)) + $10: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($12: Symbol(:blk), $13: Runtime object representing type: T.proc.params(arg0: Integer).returns(String)) + $24: T.class_of(String) = alias + $9: T::Private::Methods::DeclBuilder = $10: T::Private::Methods::DeclBuilder.returns($24: T.class_of(String)) + $25: T.noreturn = blockreturn $9: T::Private::Methods::DeclBuilder -> bb2 # backedges # - bb3(rubyRegionId=0) # - bb11(rubyRegionId=2) -bb6[rubyRegionId=2, firstDead=-1](: T.class_of(), $41: Sorbet::Private::Static::Void, $42: T.class_of()): +bb6[rubyRegionId=2, firstDead=-1](: T.class_of(), $40: Sorbet::Private::Static::Void, $41: T.class_of()): # outerLoops: 1 -> (NilClass ? bb9 : bb7) # backedges # - bb6(rubyRegionId=2) -bb7[rubyRegionId=0, firstDead=-1]($41: Sorbet::Private::Static::Void, $42: T.class_of()): - a: String = Solve<$41, bar> +bb7[rubyRegionId=0, firstDead=-1]($40: Sorbet::Private::Static::Void, $41: T.class_of()): + a: String = Solve<$40, bar> -> bb8 # backedges # - bb7(rubyRegionId=0) # - bb10(rubyRegionId=2) -bb8[rubyRegionId=0, firstDead=-1](a: T.any(String, Integer), $42: T.class_of()): - : T.class_of() = $42 - $56: T.class_of(T) = alias - $54: T.any(String, Integer) = $56: T.class_of(T).reveal_type(a: T.any(String, Integer)) - $60: Sorbet::Private::Static::Void = : T.class_of().bar() - $61: T.class_of() = +bb8[rubyRegionId=0, firstDead=-1](a: T.any(String, Integer), $41: T.class_of()): + : T.class_of() = $41 + $55: T.class_of(T) = alias + $53: T.any(String, Integer) = $55: T.class_of(T).reveal_type(a: T.any(String, Integer)) + $59: Sorbet::Private::Static::Void = : T.class_of().bar() + $60: T.class_of() = -> bb12 # backedges # - bb6(rubyRegionId=2) -bb9[rubyRegionId=2, firstDead=-1](: T.class_of(), $41: Sorbet::Private::Static::Void, $42: T.class_of()): +bb9[rubyRegionId=2, firstDead=-1](: T.class_of(), $40: Sorbet::Private::Static::Void, $41: T.class_of()): # outerLoops: 1 : T.class_of() = loadSelf(bar) - $43: [Integer] = load_yield_params(bar) - x$2: Integer = yield_load_arg(0, $43: [Integer]) - $48: Integer(5) = 5 - $46: T::Boolean = x$2: Integer.>($48: Integer(5)) - $46 -> (T::Boolean ? bb10 : bb11) + $42: [Integer] = load_yield_params(bar) + x$2: Integer = yield_load_arg(0, $42: [Integer]) + $47: Integer(5) = 5 + $45: T::Boolean = x$2: Integer.>($47: Integer(5)) + $45 -> (T::Boolean ? bb10 : bb11) # backedges # - bb9(rubyRegionId=2) -bb10[rubyRegionId=2, firstDead=-1]($42: T.class_of()): +bb10[rubyRegionId=2, firstDead=-1]($41: T.class_of()): # outerLoops: 1 - $49: Integer(10) = 10 - $50: Integer(10) = $49 - $51: T.class_of() = alias > - $52: T.untyped = $51: T.class_of().($49: Integer(10)) - a: Integer(10) = $50 + $48: Integer(10) = 10 + $49: Integer(10) = $48 + $50: T.class_of() = alias > + $51: T.untyped = $50: T.class_of().($48: Integer(10)) + a: Integer(10) = $49 -> bb8 # backedges # - bb9(rubyRegionId=2) -bb11[rubyRegionId=2, firstDead=2](: T.class_of(), $41: Sorbet::Private::Static::Void, $42: T.class_of()): +bb11[rubyRegionId=2, firstDead=2](: T.class_of(), $40: Sorbet::Private::Static::Void, $41: T.class_of()): # outerLoops: 1 - $44: String("test") = "test" - $53: T.noreturn = blockreturn $44: String("test") + $43: String("test") = "test" + $52: T.noreturn = blockreturn $43: String("test") -> bb6 # backedges # - bb8(rubyRegionId=0) # - bb17(rubyRegionId=3) -bb12[rubyRegionId=3, firstDead=-1](: T.class_of(), $60: Sorbet::Private::Static::Void, $61: T.class_of()): +bb12[rubyRegionId=3, firstDead=-1](: T.class_of(), $59: Sorbet::Private::Static::Void, $60: T.class_of()): # outerLoops: 1 -> (NilClass ? bb15 : bb13) # backedges # - bb12(rubyRegionId=3) -bb13[rubyRegionId=0, firstDead=-1]($60: Sorbet::Private::Static::Void, $61: T.class_of()): - b: String = Solve<$60, bar> +bb13[rubyRegionId=0, firstDead=-1]($59: Sorbet::Private::Static::Void, $60: T.class_of()): + b: String = Solve<$59, bar> -> bb14 # backedges # - bb13(rubyRegionId=0) # - bb16(rubyRegionId=3) -bb14[rubyRegionId=0, firstDead=-1](b: T.nilable(String), $61: T.class_of()): - $75: T.class_of(T) = alias - $73: T.nilable(String) = $75: T.class_of(T).reveal_type(b: T.nilable(String)) +bb14[rubyRegionId=0, firstDead=-1](b: T.nilable(String), $60: T.class_of()): + $74: T.class_of(T) = alias + $72: T.nilable(String) = $74: T.class_of(T).reveal_type(b: T.nilable(String)) -> bb18 # backedges # - bb12(rubyRegionId=3) -bb15[rubyRegionId=3, firstDead=-1](: T.class_of(), $60: Sorbet::Private::Static::Void, $61: T.class_of()): +bb15[rubyRegionId=3, firstDead=-1](: T.class_of(), $59: Sorbet::Private::Static::Void, $60: T.class_of()): # outerLoops: 1 : T.class_of() = loadSelf(bar) - $62: [Integer] = load_yield_params(bar) - x$3: Integer = yield_load_arg(0, $62: [Integer]) - $67: Integer(5) = 5 - $65: T::Boolean = x$3: Integer.>($67: Integer(5)) - $65 -> (T::Boolean ? bb16 : bb17) + $61: [Integer] = load_yield_params(bar) + x$3: Integer = yield_load_arg(0, $61: [Integer]) + $66: Integer(5) = 5 + $64: T::Boolean = x$3: Integer.>($66: Integer(5)) + $64 -> (T::Boolean ? bb16 : bb17) # backedges # - bb15(rubyRegionId=3) -bb16[rubyRegionId=3, firstDead=-1]($61: T.class_of()): +bb16[rubyRegionId=3, firstDead=-1]($60: T.class_of()): # outerLoops: 1 - $69: NilClass = $68 - $70: T.class_of() = alias > - $71: T.untyped = $70: T.class_of().($68: NilClass) - b: NilClass = $69 + $68: NilClass = $67 + $69: T.class_of() = alias > + $70: T.untyped = $69: T.class_of().($67: NilClass) + b: NilClass = $68 -> bb14 # backedges # - bb15(rubyRegionId=3) -bb17[rubyRegionId=3, firstDead=2](: T.class_of(), $60: Sorbet::Private::Static::Void, $61: T.class_of()): +bb17[rubyRegionId=3, firstDead=2](: T.class_of(), $59: Sorbet::Private::Static::Void, $60: T.class_of()): # outerLoops: 1 - $63: String("test") = "test" - $72: T.noreturn = blockreturn $63: String("test") + $62: String("test") = "test" + $71: T.noreturn = blockreturn $62: String("test") -> bb12 # backedges @@ -230,11 +230,11 @@ bb17[rubyRegionId=3, firstDead=2](: T.class_of(), $80: Integer(1) = 1 - $79: String = $80: Integer(1).to_s() - $81: String("") = "" - $78: T::Boolean = $79: String.==($81: String("")) - $78 -> (T::Boolean ? bb21 : bb19) + $79: Integer(1) = 1 + $78: String = $79: Integer(1).to_s() + $80: String("") = "" + $77: T::Boolean = $78: String.==($80: String("")) + $77 -> (T::Boolean ? bb21 : bb19) # backedges # - bb18(rubyRegionId=0) @@ -246,8 +246,8 @@ bb19[rubyRegionId=0, firstDead=-1](): # - bb19(rubyRegionId=0) # - bb22(rubyRegionId=0) bb20[rubyRegionId=0, firstDead=3](c: T.nilable(Symbol)): - $91: T.class_of(T) = alias - $89: T.nilable(Symbol) = $91: T.class_of(T).reveal_type(c: T.nilable(Symbol)) + $90: T.class_of(T) = alias + $88: T.nilable(Symbol) = $90: T.class_of(T).reveal_type(c: T.nilable(Symbol)) : T.noreturn = return $2: NilClass -> bb1 @@ -255,19 +255,19 @@ bb20[rubyRegionId=0, firstDead=3](c: T.nilable(Symbol)): # - bb18(rubyRegionId=0) bb21[rubyRegionId=0, firstDead=-1](): # outerLoops: 1 - $85: Integer(1) = 1 - $84: String = $85: Integer(1).to_s() - $86: String("") = "" - $83: T::Boolean = $84: String.==($86: String("")) - $83 -> (T::Boolean ? bb22 : bb18) + $84: Integer(1) = 1 + $83: String = $84: Integer(1).to_s() + $85: String("") = "" + $82: T::Boolean = $83: String.==($85: String("")) + $82 -> (T::Boolean ? bb22 : bb18) # backedges # - bb21(rubyRegionId=0) bb22[rubyRegionId=0, firstDead=-1](): # outerLoops: 1 - $87: Symbol(:abc) = :abc - $88: Symbol(:abc) = $87 - c: Symbol(:abc) = $88 + $86: Symbol(:abc) = :abc + $87: Symbol(:abc) = $86 + c: Symbol(:abc) = $87 -> bb20 } diff --git a/test/testdata/cfg/dealias_with_return.rb.cfg-text.exp b/test/testdata/cfg/dealias_with_return.rb.cfg-text.exp index cbfe00bcf4..66969336cc 100644 --- a/test/testdata/cfg/dealias_with_return.rb.cfg-text.exp +++ b/test/testdata/cfg/dealias_with_return.rb.cfg-text.exp @@ -18,8 +18,8 @@ bb1[rubyRegionId=0, firstDead=-1](): # - bb0(rubyRegionId=0) bb3[rubyRegionId=2, firstDead=-1]($4: T.untyped, $6: T.class_of()): $9: T.class_of(StandardError) = alias - $10: T.untyped = $4: T.untyped.is_a?($9: T.class_of(StandardError)) - $10 -> (T.untyped ? bb7 : bb8) + $10: T::Boolean = $9: T.class_of(StandardError).===($4: T.untyped) + $10 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) diff --git a/test/testdata/cfg/default_args_cases.rb.cfg-text.exp b/test/testdata/cfg/default_args_cases.rb.cfg-text.exp index ce0a99e845..132da1f818 100644 --- a/test/testdata/cfg/default_args_cases.rb.cfg-text.exp +++ b/test/testdata/cfg/default_args_cases.rb.cfg-text.exp @@ -221,9 +221,9 @@ bb2[rubyRegionId=1, firstDead=-1](: T.class_of(Test), $7: Sorbet::Private::Static::Void, $8: T.class_of(Test)): $3: Sorbet::Private::Static::Void = Solve<$7, sig> : T.class_of(Test) = $8 - $39: T.class_of(Sorbet::Private::Static) = alias - $41: Sorbet::Private::Static::Void = $39: T.class_of(Sorbet::Private::Static).sig(: T.class_of(Test)) - $42: T.class_of(Test) = + $38: T.class_of(Sorbet::Private::Static) = alias + $40: Sorbet::Private::Static::Void = $38: T.class_of(Sorbet::Private::Static).sig(: T.class_of(Test)) + $41: T.class_of(Test) = -> bb6 # backedges @@ -231,96 +231,96 @@ bb3[rubyRegionId=0, firstDead=-1]($7: Sorbet::Private::Stat bb5[rubyRegionId=1, firstDead=20](: T.class_of(Test), $7: Sorbet::Private::Static::Void, $8: T.class_of(Test)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $13: Symbol(:a) = :a - $15: T.class_of(Integer) = alias - $16: Symbol(:b) = :b - $18: T.class_of(Integer) = alias - $19: Symbol(:c) = :c - $21: T.class_of(Integer) = alias - $22: Symbol(:d) = :d - $24: T.class_of(Integer) = alias - $25: Symbol(:e) = :e - $27: T.class_of(Integer) = alias - $28: Symbol(:f) = :f - $30: T.class_of(String) = alias - $31: Symbol(:blk) = :blk - $35: T.class_of(T) = alias - $33: T.class_of(T.proc) = $35: T.class_of(T).proc() - $32: Runtime object representing type: T.proc.void = $33: T.class_of(T.proc).void() - $11: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($13: Symbol(:a), $15: T.class_of(Integer), $16: Symbol(:b), $18: T.class_of(Integer), $19: Symbol(:c), $21: T.class_of(Integer), $22: Symbol(:d), $24: T.class_of(Integer), $25: Symbol(:e), $27: T.class_of(Integer), $28: Symbol(:f), $30: T.class_of(String), $31: Symbol(:blk), $32: Runtime object representing type: T.proc.void) - $10: T::Private::Methods::DeclBuilder = $11: T::Private::Methods::DeclBuilder.void() - $36: T.noreturn = blockreturn $10: T::Private::Methods::DeclBuilder + $12: Symbol(:a) = :a + $14: T.class_of(Integer) = alias + $15: Symbol(:b) = :b + $17: T.class_of(Integer) = alias + $18: Symbol(:c) = :c + $20: T.class_of(Integer) = alias + $21: Symbol(:d) = :d + $23: T.class_of(Integer) = alias + $24: Symbol(:e) = :e + $26: T.class_of(Integer) = alias + $27: Symbol(:f) = :f + $29: T.class_of(String) = alias + $30: Symbol(:blk) = :blk + $34: T.class_of(T) = alias + $32: T.class_of(T.proc) = $34: T.class_of(T).proc() + $31: Runtime object representing type: T.proc.void = $32: T.class_of(T.proc).void() + $10: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($12: Symbol(:a), $14: T.class_of(Integer), $15: Symbol(:b), $17: T.class_of(Integer), $18: Symbol(:c), $20: T.class_of(Integer), $21: Symbol(:d), $23: T.class_of(Integer), $24: Symbol(:e), $26: T.class_of(Integer), $27: Symbol(:f), $29: T.class_of(String), $30: Symbol(:blk), $31: Runtime object representing type: T.proc.void) + $9: T::Private::Methods::DeclBuilder = $10: T::Private::Methods::DeclBuilder.void() + $35: T.noreturn = blockreturn $9: T::Private::Methods::DeclBuilder -> bb2 # backedges # - bb3(rubyRegionId=0) # - bb9(rubyRegionId=2) -bb6[rubyRegionId=2, firstDead=-1](: T.class_of(Test), $41: Sorbet::Private::Static::Void, $42: T.class_of(Test)): +bb6[rubyRegionId=2, firstDead=-1](: T.class_of(Test), $40: Sorbet::Private::Static::Void, $41: T.class_of(Test)): # outerLoops: 1 -> (NilClass ? bb9 : bb7) # backedges # - bb6(rubyRegionId=2) -bb7[rubyRegionId=0, firstDead=-1]($41: Sorbet::Private::Static::Void, $42: T.class_of(Test)): - $37: Sorbet::Private::Static::Void = Solve<$41, sig> - : T.class_of(Test) = $42 - $61: T.class_of(Sorbet::Private::Static) = alias - $63: Sorbet::Private::Static::Void = $61: T.class_of(Sorbet::Private::Static).sig(: T.class_of(Test)) - $64: T.class_of(Test) = +bb7[rubyRegionId=0, firstDead=-1]($40: Sorbet::Private::Static::Void, $41: T.class_of(Test)): + $36: Sorbet::Private::Static::Void = Solve<$40, sig> + : T.class_of(Test) = $41 + $59: T.class_of(Sorbet::Private::Static) = alias + $61: Sorbet::Private::Static::Void = $59: T.class_of(Sorbet::Private::Static).sig(: T.class_of(Test)) + $62: T.class_of(Test) = -> bb10 # backedges # - bb6(rubyRegionId=2) -bb9[rubyRegionId=2, firstDead=12](: T.class_of(Test), $41: Sorbet::Private::Static::Void, $42: T.class_of(Test)): +bb9[rubyRegionId=2, firstDead=12](: T.class_of(Test), $40: Sorbet::Private::Static::Void, $41: T.class_of(Test)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $47: Symbol(:x) = :x - $49: T.class_of(Integer) = alias - $50: Symbol(:rest) = :rest - $52: T.class_of(Integer) = alias - $53: Symbol(:blk) = :blk - $57: T.class_of(T) = alias - $55: T.class_of(T.proc) = $57: T.class_of(T).proc() - $54: Runtime object representing type: T.proc.void = $55: T.class_of(T.proc).void() - $45: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($47: Symbol(:x), $49: T.class_of(Integer), $50: Symbol(:rest), $52: T.class_of(Integer), $53: Symbol(:blk), $54: Runtime object representing type: T.proc.void) - $44: T::Private::Methods::DeclBuilder = $45: T::Private::Methods::DeclBuilder.void() - $58: T.noreturn = blockreturn $44: T::Private::Methods::DeclBuilder + $45: Symbol(:x) = :x + $47: T.class_of(Integer) = alias + $48: Symbol(:rest) = :rest + $50: T.class_of(Integer) = alias + $51: Symbol(:blk) = :blk + $55: T.class_of(T) = alias + $53: T.class_of(T.proc) = $55: T.class_of(T).proc() + $52: Runtime object representing type: T.proc.void = $53: T.class_of(T.proc).void() + $43: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($45: Symbol(:x), $47: T.class_of(Integer), $48: Symbol(:rest), $50: T.class_of(Integer), $51: Symbol(:blk), $52: Runtime object representing type: T.proc.void) + $42: T::Private::Methods::DeclBuilder = $43: T::Private::Methods::DeclBuilder.void() + $56: T.noreturn = blockreturn $42: T::Private::Methods::DeclBuilder -> bb6 # backedges # - bb7(rubyRegionId=0) # - bb13(rubyRegionId=3) -bb10[rubyRegionId=3, firstDead=-1](: T.class_of(Test), $63: Sorbet::Private::Static::Void, $64: T.class_of(Test)): +bb10[rubyRegionId=3, firstDead=-1](: T.class_of(Test), $61: Sorbet::Private::Static::Void, $62: T.class_of(Test)): # outerLoops: 1 -> (NilClass ? bb13 : bb11) # backedges # - bb10(rubyRegionId=3) -bb11[rubyRegionId=0, firstDead=6]($63: Sorbet::Private::Static::Void, $64: T.class_of(Test)): - $59: Sorbet::Private::Static::Void = Solve<$63, sig> - : T.class_of(Test) = $64 - $84: T.class_of(T::Sig) = alias - $86: T.class_of(T) = alias - $81: T.class_of(Test) = : T.class_of(Test).extend($84: T.class_of(T::Sig)) +bb11[rubyRegionId=0, firstDead=6]($61: Sorbet::Private::Static::Void, $62: T.class_of(Test)): + $57: Sorbet::Private::Static::Void = Solve<$61, sig> + : T.class_of(Test) = $62 + $81: T.class_of(T::Sig) = alias + $83: T.class_of(T) = alias + $78: T.class_of(Test) = : T.class_of(Test).extend($81: T.class_of(T::Sig)) : T.noreturn = return $2: NilClass -> bb1 # backedges # - bb10(rubyRegionId=3) -bb13[rubyRegionId=3, firstDead=12](: T.class_of(Test), $63: Sorbet::Private::Static::Void, $64: T.class_of(Test)): +bb13[rubyRegionId=3, firstDead=12](: T.class_of(Test), $61: Sorbet::Private::Static::Void, $62: T.class_of(Test)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $69: Symbol(:x) = :x + $66: Symbol(:x) = :x + $68: T.class_of(Integer) = alias + $69: Symbol(:rest) = :rest $71: T.class_of(Integer) = alias - $72: Symbol(:rest) = :rest - $74: T.class_of(Integer) = alias - $75: Symbol(:blk) = :blk - $79: T.class_of(T) = alias - $77: T.class_of(T.proc) = $79: T.class_of(T).proc() - $76: Runtime object representing type: T.proc.void = $77: T.class_of(T.proc).void() - $67: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($69: Symbol(:x), $71: T.class_of(Integer), $72: Symbol(:rest), $74: T.class_of(Integer), $75: Symbol(:blk), $76: Runtime object representing type: T.proc.void) - $66: T::Private::Methods::DeclBuilder = $67: T::Private::Methods::DeclBuilder.void() - $80: T.noreturn = blockreturn $66: T::Private::Methods::DeclBuilder + $72: Symbol(:blk) = :blk + $76: T.class_of(T) = alias + $74: T.class_of(T.proc) = $76: T.class_of(T).proc() + $73: Runtime object representing type: T.proc.void = $74: T.class_of(T.proc).void() + $64: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($66: Symbol(:x), $68: T.class_of(Integer), $69: Symbol(:rest), $71: T.class_of(Integer), $72: Symbol(:blk), $73: Runtime object representing type: T.proc.void) + $63: T::Private::Methods::DeclBuilder = $64: T::Private::Methods::DeclBuilder.void() + $77: T.noreturn = blockreturn $63: T::Private::Methods::DeclBuilder -> bb10 } diff --git a/test/testdata/cfg/reassign_dead_block_bug.rb.cfg-text.exp b/test/testdata/cfg/reassign_dead_block_bug.rb.cfg-text.exp index 4bb565307d..581bc234ea 100644 --- a/test/testdata/cfg/reassign_dead_block_bug.rb.cfg-text.exp +++ b/test/testdata/cfg/reassign_dead_block_bug.rb.cfg-text.exp @@ -33,8 +33,8 @@ bb5[rubyRegionId=1, firstDead=4](: T.class_of()): # outerLoops: 1 : T.class_of() = loadSelf(times) x$1: Integer(1) = 1 - $10: Integer(1) = 1 - $9: T.noreturn = return $10: Integer(1) + $9: Integer(1) = 1 + $8: T.noreturn = return $9: Integer(1) -> bb1 } diff --git a/test/testdata/cfg/rescue.rb.cfg-text.exp b/test/testdata/cfg/rescue.rb.cfg-text.exp index f1e9ebb1e7..c0e84d8fa1 100644 --- a/test/testdata/cfg/rescue.rb.cfg-text.exp +++ b/test/testdata/cfg/rescue.rb.cfg-text.exp @@ -17,8 +17,8 @@ bb1[rubyRegionId=0, firstDead=-1](): # - bb4(rubyRegionId=1) bb3[rubyRegionId=2, firstDead=-1](: Object, $2: T.untyped, $3: T.untyped, $6: T.class_of()): $9: T.class_of(StandardError) = alias - $10: T.untyped = $3: T.untyped.is_a?($9: T.class_of(StandardError)) - $10 -> (T.untyped ? bb7 : bb8) + $10: T::Boolean = $9: T.class_of(StandardError).===($3: T.untyped) + $10 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) diff --git a/test/testdata/cfg/rescue_complex.rb b/test/testdata/cfg/rescue_complex.rb index be784fc19b..5b69e41a87 100644 --- a/test/testdata/cfg/rescue_complex.rb +++ b/test/testdata/cfg/rescue_complex.rb @@ -1,10 +1,15 @@ # typed: true class TestRescue + extend T::Sig + def meth; 0; end def foo; 1; end def bar; 2; end def baz; 3; end def take_arg(x); x; end + def untyped_exceptions(); [Exception]; end + sig {returns(T::Array[T.class_of(Exception)])} + def typed_exceptions(); [Exception]; end def initialize @ex = T.let(nil, T.nilable(StandardError)) @@ -30,6 +35,44 @@ def multiple_rescue_classes() end end + def multiple_rescue_classes_varuse() + begin + meth + rescue LoadError, SocketError => baz + baz + end + + T.reveal_type(baz) # error: Revealed type: `T.untyped` + end + + def rescue_loop() + ex = T.let(nil, T.nilable(StandardError)) + + loop do + ex = nil + begin + meth + rescue => ex + end + end + end + + def rescue_untyped_splat() + begin + meth + rescue *untyped_exceptions => e + T.reveal_type(e) # error: Revealed type: `T.untyped` + end + end + + def rescue_typed_splat() + begin + meth + rescue *typed_exceptions => e + T.reveal_type(e) # error: Revealed type: `T.untyped` + end + end + def parse_rescue_ensure() begin; meth; rescue; baz; ensure; bar; end end diff --git a/test/testdata/cfg/rescue_complex.rb.cfg-text.exp b/test/testdata/cfg/rescue_complex.rb.cfg-text.exp index 4e270a3ebf..80f58a2fc1 100644 --- a/test/testdata/cfg/rescue_complex.rb.cfg-text.exp +++ b/test/testdata/cfg/rescue_complex.rb.cfg-text.exp @@ -91,6 +91,40 @@ bb1[rubyRegionId=0, firstDead=-1](): } +method ::TestRescue#untyped_exceptions { + +bb0[rubyRegionId=0, firstDead=5](): + : TestRescue = cast(: NilClass, TestRescue); + $4: T.class_of(Exception) = alias + $5: T.class_of() = alias > + $2: [T.class_of(Exception)] = $5: T.class_of().($4: T.class_of(Exception)) + : T.noreturn = return $2: [T.class_of(Exception)] + -> bb1 + +# backedges +# - bb0(rubyRegionId=0) +bb1[rubyRegionId=0, firstDead=-1](): + -> bb1 + +} + +method ::TestRescue#typed_exceptions { + +bb0[rubyRegionId=0, firstDead=5](): + : TestRescue = cast(: NilClass, TestRescue); + $4: T.class_of(Exception) = alias + $5: T.class_of() = alias > + $2: [T.class_of(Exception)] = $5: T.class_of().($4: T.class_of(Exception)) + : T.noreturn = return $2: [T.class_of(Exception)] + -> bb1 + +# backedges +# - bb0(rubyRegionId=0) +bb1[rubyRegionId=0, firstDead=-1](): + -> bb1 + +} + method ::TestRescue#initialize { bb0[rubyRegionId=0, firstDead=10](): @@ -132,8 +166,8 @@ bb1[rubyRegionId=0, firstDead=-1](): # - bb4(rubyRegionId=1) bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: T.untyped, $5: T.class_of()): $8: T.class_of(StandardError) = alias - $9: T.untyped = $3: T.untyped.is_a?($8: T.class_of(StandardError)) - $9 -> (T.untyped ? bb7 : bb8) + $9: T::Boolean = $8: T.class_of(StandardError).===($3: T.untyped) + $9 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) @@ -167,8 +201,8 @@ bb7[rubyRegionId=2, firstDead=-1](: TestRescue, $5: T.class_of(: TestRescue, $2: T.untyped, $3: T.untyped, $5: T.class_of()): $13: T.class_of(StandardError) = alias - $14: T.untyped = $3: T.untyped.is_a?($13: T.class_of(StandardError)) - $14 -> (T.untyped ? bb9 : bb10) + $14: T::Boolean = $13: T.class_of(StandardError).===($3: T.untyped) + $14 -> (T::Boolean ? bb9 : bb10) # backedges # - bb8(rubyRegionId=2) @@ -212,7 +246,7 @@ bb1[rubyRegionId=0, firstDead=-1](): bb3[rubyRegionId=2, firstDead=-1]($2: T.untyped, $3: T.untyped, $5: T.class_of()): baz: T.untyped = $3 $8: T.untyped = alias - $9: T.untyped = baz: T.untyped.is_a?($8: T.untyped) + $9: T.untyped = $8: T.untyped.===(baz: T.untyped) $9 -> (T.untyped ? bb7 : bb8) # backedges @@ -247,7 +281,7 @@ bb7[rubyRegionId=2, firstDead=-1]($5: T.class_of(), baz: T.untyped # - bb3(rubyRegionId=2) bb8[rubyRegionId=2, firstDead=-1]($2: T.untyped, $5: T.class_of(), baz: T.untyped): $11: T.untyped = alias - $12: T.untyped = baz: T.untyped.is_a?($11: T.untyped) + $12: T.untyped = $11: T.untyped.===(baz: T.untyped) $12 -> (T.untyped ? bb7 : bb9) # backedges @@ -264,6 +298,313 @@ bb10[rubyRegionId=0, firstDead=1]($2: T.untyped): } +method ::TestRescue#multiple_rescue_classes_varuse { + +bb0[rubyRegionId=0, firstDead=-1](): + : TestRescue = cast(: NilClass, TestRescue); + $6: T.class_of() = alias > + $4: T.untyped = + $4 -> (T.untyped ? bb3 : bb4) + +# backedges +# - bb6(rubyRegionId=3) +# - bb10(rubyRegionId=0) +bb1[rubyRegionId=0, firstDead=-1](): + -> bb1 + +# backedges +# - bb0(rubyRegionId=0) +# - bb4(rubyRegionId=1) +bb3[rubyRegionId=2, firstDead=-1]($4: T.untyped, $6: T.class_of()): + baz: T.untyped = $4 + $9: T.class_of(LoadError) = alias + $10: T::Boolean = $9: T.class_of(LoadError).===(baz: T.untyped) + $10 -> (T::Boolean ? bb7 : bb8) + +# backedges +# - bb0(rubyRegionId=0) +bb4[rubyRegionId=1, firstDead=-1](: TestRescue, $6: T.class_of()): + $3: T.untyped = : TestRescue.meth() + $4: T.untyped = + $4 -> (T.untyped ? bb3 : bb5) + +# backedges +# - bb4(rubyRegionId=1) +bb5[rubyRegionId=4, firstDead=-1](): + -> bb6 + +# backedges +# - bb5(rubyRegionId=4) +# - bb7(rubyRegionId=2) +# - bb9(rubyRegionId=2) +bb6[rubyRegionId=3, firstDead=-1](baz: T.untyped, $14: T.nilable(TrueClass)): + $14 -> (T.nilable(TrueClass) ? bb1 : bb10) + +# backedges +# - bb3(rubyRegionId=2) +# - bb8(rubyRegionId=2) +bb7[rubyRegionId=2, firstDead=-1]($6: T.class_of(), baz: T.any(LoadError, SocketError)): + $4: NilClass = nil + $7: Sorbet::Private::Static::Void = $6: T.class_of().($4: NilClass) + -> bb6 + +# backedges +# - bb3(rubyRegionId=2) +bb8[rubyRegionId=2, firstDead=-1]($6: T.class_of(), baz: T.untyped): + $12: T.class_of(SocketError) = alias + $13: T::Boolean = $12: T.class_of(SocketError).===(baz: T.untyped) + $13 -> (T::Boolean ? bb7 : bb9) + +# backedges +# - bb8(rubyRegionId=2) +bb9[rubyRegionId=2, firstDead=-1](baz: T.untyped): + $14: TrueClass = true + -> bb6 + +# backedges +# - bb6(rubyRegionId=3) +bb10[rubyRegionId=0, firstDead=3](baz: T.untyped): + $17: T.class_of(T) = alias + $2: T.untyped = $17: T.class_of(T).reveal_type(baz: T.untyped) + : T.noreturn = return $2: T.untyped + -> bb1 + +} + +method ::TestRescue#rescue_loop { + +bb0[rubyRegionId=0, firstDead=-1](): + : TestRescue = cast(: NilClass, TestRescue); + $6: T.class_of(T) = alias + $8: T.class_of(StandardError) = alias + keep_for_ide$4: Runtime object representing type: T.nilable(StandardError) = $6: T.class_of(T).nilable($8: T.class_of(StandardError)) + keep_for_ide$4: T.untyped = keep_for_ide$4 + $9: NilClass = nil + ex: T.nilable(StandardError) = cast($9: NilClass, T.nilable(StandardError)); + $11: Sorbet::Private::Static::Void = : TestRescue.loop() + $12: TestRescue = + -> bb2 + +# backedges +# - bb3(rubyRegionId=0) +# - bb10(rubyRegionId=4) +bb1[rubyRegionId=0, firstDead=-1](): + -> bb1 + +# backedges +# - bb0(rubyRegionId=0) +# - bb13(rubyRegionId=1) +bb2[rubyRegionId=1, firstDead=-1](: TestRescue, ex: T.nilable(StandardError), $11: Sorbet::Private::Static::Void, $12: TestRescue, $13: NilClass, $22: NilClass): + # outerLoops: 1 + -> (NilClass ? bb5 : bb3) + +# backedges +# - bb2(rubyRegionId=1) +bb3[rubyRegionId=0, firstDead=1]($11: Sorbet::Private::Static::Void, $12: TestRescue): + $2: T.noreturn = Solve<$11, loop> + = return $2 + -> bb1 + +# backedges +# - bb2(rubyRegionId=1) +bb5[rubyRegionId=1, firstDead=-1](: TestRescue, ex: T.nilable(StandardError), $11: Sorbet::Private::Static::Void, $12: TestRescue, $13: NilClass, $22: NilClass): + # outerLoops: 1 + : TestRescue = loadSelf(loop) + ex: NilClass = nil + $17: T.class_of() = alias > + $15: T.untyped = + $15 -> (T.untyped ? bb7 : bb8) + +# backedges +# - bb5(rubyRegionId=1) +# - bb8(rubyRegionId=2) +bb7[rubyRegionId=3, firstDead=-1](: TestRescue, ex: NilClass, $11: Sorbet::Private::Static::Void, $12: TestRescue, $13: T.untyped, $15: T.untyped, $17: T.class_of(), $22: NilClass): + # outerLoops: 1 + ex: T.untyped = $15 + $20: T.class_of(StandardError) = alias + $21: T::Boolean = $20: T.class_of(StandardError).===(ex: T.untyped) + $21 -> (T::Boolean ? bb11 : bb12) + +# backedges +# - bb5(rubyRegionId=1) +bb8[rubyRegionId=2, firstDead=-1](: TestRescue, ex: NilClass, $11: Sorbet::Private::Static::Void, $12: TestRescue, $17: T.class_of(), $22: NilClass): + # outerLoops: 1 + $13: T.untyped = : TestRescue.meth() + $15: T.untyped = + $15 -> (T.untyped ? bb7 : bb9) + +# backedges +# - bb8(rubyRegionId=2) +bb9[rubyRegionId=5, firstDead=-1](: TestRescue, ex: NilClass, $11: Sorbet::Private::Static::Void, $12: TestRescue, $13: T.untyped, $22: NilClass): + # outerLoops: 1 + -> bb10 + +# backedges +# - bb9(rubyRegionId=5) +# - bb11(rubyRegionId=3) +# - bb12(rubyRegionId=3) +bb10[rubyRegionId=4, firstDead=-1](: TestRescue, ex: T.untyped, $11: Sorbet::Private::Static::Void, $12: TestRescue, $13: T.untyped, $22: T.nilable(TrueClass)): + # outerLoops: 1 + $22 -> (T.nilable(TrueClass) ? bb1 : bb13) + +# backedges +# - bb7(rubyRegionId=3) +bb11[rubyRegionId=3, firstDead=-1](: TestRescue, ex: StandardError, $11: Sorbet::Private::Static::Void, $12: TestRescue, $13: T.untyped, $17: T.class_of(), $22: NilClass): + # outerLoops: 1 + $15: NilClass = nil + $18: Sorbet::Private::Static::Void = $17: T.class_of().($15: NilClass) + -> bb10 + +# backedges +# - bb7(rubyRegionId=3) +bb12[rubyRegionId=3, firstDead=-1](: TestRescue, ex: T.untyped, $11: Sorbet::Private::Static::Void, $12: TestRescue, $13: T.untyped): + # outerLoops: 1 + $22: TrueClass = true + -> bb10 + +# backedges +# - bb10(rubyRegionId=4) +bb13[rubyRegionId=1, firstDead=1](: TestRescue, ex: T.nilable(StandardError), $11: Sorbet::Private::Static::Void, $12: TestRescue, $13: T.untyped, $22: NilClass): + # outerLoops: 1 + $24: T.noreturn = blockreturn $13: T.untyped + -> bb2 + +} + +method ::TestRescue#rescue_untyped_splat { + +bb0[rubyRegionId=0, firstDead=-1](): + : TestRescue = cast(: NilClass, TestRescue); + $5: T.class_of() = alias > + $3: T.untyped = + $3 -> (T.untyped ? bb3 : bb4) + +# backedges +# - bb6(rubyRegionId=3) +# - bb9(rubyRegionId=0) +bb1[rubyRegionId=0, firstDead=-1](): + -> bb1 + +# backedges +# - bb0(rubyRegionId=0) +# - bb4(rubyRegionId=1) +bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: T.untyped, $5: T.class_of()): + e: T.untyped = $3 + $9: T.class_of() = alias > + $10: T.untyped = : TestRescue.untyped_exceptions() + $7: T.untyped = $9: T.class_of().($10: T.untyped) + $12: T.untyped = $7: T.untyped.===(e: T.untyped) + $12 -> (T.untyped ? bb7 : bb8) + +# backedges +# - bb0(rubyRegionId=0) +bb4[rubyRegionId=1, firstDead=-1](: TestRescue, $5: T.class_of()): + $2: T.untyped = : TestRescue.meth() + $3: T.untyped = + $3 -> (T.untyped ? bb3 : bb5) + +# backedges +# - bb4(rubyRegionId=1) +bb5[rubyRegionId=4, firstDead=-1]($2: T.untyped): + -> bb6 + +# backedges +# - bb5(rubyRegionId=4) +# - bb7(rubyRegionId=2) +# - bb8(rubyRegionId=2) +bb6[rubyRegionId=3, firstDead=-1]($2: T.untyped, $16: T.nilable(TrueClass)): + $16 -> (T.nilable(TrueClass) ? bb1 : bb9) + +# backedges +# - bb3(rubyRegionId=2) +bb7[rubyRegionId=2, firstDead=-1]($5: T.class_of(), e: T.untyped): + $3: NilClass = nil + $6: Sorbet::Private::Static::Void = $5: T.class_of().($3: NilClass) + $14: T.class_of(T) = alias + $2: T.untyped = $14: T.class_of(T).reveal_type(e: T.untyped) + -> bb6 + +# backedges +# - bb3(rubyRegionId=2) +bb8[rubyRegionId=2, firstDead=-1]($2: T.untyped): + $16: TrueClass = true + -> bb6 + +# backedges +# - bb6(rubyRegionId=3) +bb9[rubyRegionId=0, firstDead=1]($2: T.untyped): + : T.noreturn = return $2: T.untyped + -> bb1 + +} + +method ::TestRescue#rescue_typed_splat { + +bb0[rubyRegionId=0, firstDead=-1](): + : TestRescue = cast(: NilClass, TestRescue); + $5: T.class_of() = alias > + $3: T.untyped = + $3 -> (T.untyped ? bb3 : bb4) + +# backedges +# - bb6(rubyRegionId=3) +# - bb9(rubyRegionId=0) +bb1[rubyRegionId=0, firstDead=-1](): + -> bb1 + +# backedges +# - bb0(rubyRegionId=0) +# - bb4(rubyRegionId=1) +bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: T.untyped, $5: T.class_of()): + e: T.untyped = $3 + $9: T.class_of() = alias > + $10: T::Array[T.class_of(Exception)] = : TestRescue.typed_exceptions() + $7: T::Array[T.class_of(Exception)] = $9: T.class_of().($10: T::Array[T.class_of(Exception)]) + $12: T::Boolean = $7: T::Array[T.class_of(Exception)].===(e: T.untyped) + $12 -> (T::Boolean ? bb7 : bb8) + +# backedges +# - bb0(rubyRegionId=0) +bb4[rubyRegionId=1, firstDead=-1](: TestRescue, $5: T.class_of()): + $2: T.untyped = : TestRescue.meth() + $3: T.untyped = + $3 -> (T.untyped ? bb3 : bb5) + +# backedges +# - bb4(rubyRegionId=1) +bb5[rubyRegionId=4, firstDead=-1]($2: T.untyped): + -> bb6 + +# backedges +# - bb5(rubyRegionId=4) +# - bb7(rubyRegionId=2) +# - bb8(rubyRegionId=2) +bb6[rubyRegionId=3, firstDead=-1]($2: T.untyped, $16: T.nilable(TrueClass)): + $16 -> (T.nilable(TrueClass) ? bb1 : bb9) + +# backedges +# - bb3(rubyRegionId=2) +bb7[rubyRegionId=2, firstDead=-1]($5: T.class_of(), e: T.untyped): + $3: NilClass = nil + $6: Sorbet::Private::Static::Void = $5: T.class_of().($3: NilClass) + $14: T.class_of(T) = alias + $2: T.untyped = $14: T.class_of(T).reveal_type(e: T.untyped) + -> bb6 + +# backedges +# - bb3(rubyRegionId=2) +bb8[rubyRegionId=2, firstDead=-1]($2: T.untyped): + $16: TrueClass = true + -> bb6 + +# backedges +# - bb6(rubyRegionId=3) +bb9[rubyRegionId=0, firstDead=1]($2: T.untyped): + : T.noreturn = return $2: T.untyped + -> bb1 + +} + method ::TestRescue#parse_rescue_ensure { bb0[rubyRegionId=0, firstDead=-1](): @@ -283,8 +624,8 @@ bb1[rubyRegionId=0, firstDead=-1](): # - bb4(rubyRegionId=1) bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: T.untyped, $5: T.class_of()): $8: T.class_of(StandardError) = alias - $9: T.untyped = $3: T.untyped.is_a?($8: T.class_of(StandardError)) - $9 -> (T.untyped ? bb7 : bb8) + $9: T::Boolean = $8: T.class_of(StandardError).===($3: T.untyped) + $9 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) @@ -347,8 +688,8 @@ bb1[rubyRegionId=0, firstDead=-1](): # - bb4(rubyRegionId=1) bb3[rubyRegionId=2, firstDead=-1]($3: T.untyped, $4: T.class_of()): $7: T.class_of(LoadError) = alias - $8: T.untyped = $3: T.untyped.is_a?($7: T.class_of(LoadError)) - $8 -> (T.untyped ? bb7 : bb8) + $8: T::Boolean = $7: T.class_of(LoadError).===($3: T.untyped) + $8 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) @@ -408,8 +749,8 @@ bb1[rubyRegionId=0, firstDead=-1](): # - bb4(rubyRegionId=1) bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $4: T.untyped, $5: T.untyped, $7: T.class_of()): $10: T.class_of(StandardError) = alias - $11: T.untyped = $5: T.untyped.is_a?($10: T.class_of(StandardError)) - $11 -> (T.untyped ? bb7 : bb8) + $11: T::Boolean = $10: T.class_of(StandardError).===($5: T.untyped) + $11 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) @@ -472,8 +813,8 @@ bb1[rubyRegionId=0, firstDead=-1](): # - bb4(rubyRegionId=1) bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: T.untyped, $5: T.class_of()): $8: T.class_of(StandardError) = alias - $9: T.untyped = $3: T.untyped.is_a?($8: T.class_of(StandardError)) - $9 -> (T.untyped ? bb7 : bb8) + $9: T::Boolean = $8: T.class_of(StandardError).===($3: T.untyped) + $9 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) @@ -536,7 +877,7 @@ bb1[rubyRegionId=0, firstDead=-1](): bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: T.untyped, $5: T.class_of()): ex: T.untyped = $3 $7: T.untyped = : TestRescue.foo() - $9: T.untyped = ex: T.untyped.is_a?($7: T.untyped) + $9: T.untyped = $7: T.untyped.===(ex: T.untyped) $9 -> (T.untyped ? bb7 : bb8) # backedges @@ -599,8 +940,8 @@ bb1[rubyRegionId=0, firstDead=-1](): # - bb4(rubyRegionId=1) bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: T.untyped, $6: T.class_of()): $9: T.class_of(StandardError) = alias - $10: T.untyped = $3: T.untyped.is_a?($9: T.class_of(StandardError)) - $10 -> (T.untyped ? bb7 : bb8) + $10: T::Boolean = $9: T.class_of(StandardError).===($3: T.untyped) + $10 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) @@ -664,8 +1005,8 @@ bb1[rubyRegionId=0, firstDead=-1](): # - bb4(rubyRegionId=1) bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: T.untyped, $5: T.class_of()): $8: T.class_of(StandardError) = alias - $9: T.untyped = $3: T.untyped.is_a?($8: T.class_of(StandardError)) - $9 -> (T.untyped ? bb7 : bb8) + $9: T::Boolean = $8: T.class_of(StandardError).===($3: T.untyped) + $9 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) @@ -728,8 +1069,8 @@ bb1[rubyRegionId=0, firstDead=-1](): bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: T.untyped, $5: T.class_of()): ex: T.untyped = $3 $8: T.class_of(StandardError) = alias - $9: T.untyped = ex: T.untyped.is_a?($8: T.class_of(StandardError)) - $9 -> (T.untyped ? bb7 : bb8) + $9: T::Boolean = $8: T.class_of(StandardError).===(ex: T.untyped) + $9 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) @@ -793,8 +1134,8 @@ bb1[rubyRegionId=0, firstDead=-1](): bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: T.untyped, $5: T.class_of(), @ex$11: T.nilable(StandardError)): $2: T.untyped = $3 $8: T.class_of(StandardError) = alias - $9: T.untyped = $3: T.untyped.is_a?($8: T.class_of(StandardError)) - $9 -> (T.untyped ? bb7 : bb8) + $9: T::Boolean = $8: T.class_of(StandardError).===($3: T.untyped) + $9 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) @@ -858,8 +1199,8 @@ bb1[rubyRegionId=0, firstDead=-1](): # - bb4(rubyRegionId=1) bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $3: NilClass, $4: T.untyped, $5: T.untyped, $7: T.class_of()): $10: T.class_of(StandardError) = alias - $11: T.untyped = $5: T.untyped.is_a?($10: T.class_of(StandardError)) - $11 -> (T.untyped ? bb7 : bb8) + $11: T::Boolean = $10: T.class_of(StandardError).===($5: T.untyped) + $11 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) @@ -923,8 +1264,8 @@ bb1[rubyRegionId=0, firstDead=-1](): # - bb4(rubyRegionId=1) bb3[rubyRegionId=2, firstDead=-1](foo: NilClass, $3: T.untyped, $7: T.class_of()): $10: T.class_of(StandardError) = alias - $11: T.untyped = $3: T.untyped.is_a?($10: T.class_of(StandardError)) - $11 -> (T.untyped ? bb7 : bb8) + $11: T::Boolean = $10: T.class_of(StandardError).===($3: T.untyped) + $11 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) @@ -989,8 +1330,8 @@ bb1[rubyRegionId=0, firstDead=-1](): # - bb4(rubyRegionId=1) bb3[rubyRegionId=2, firstDead=-1]($3: NilClass, $4: NilClass, $5: T.untyped, $9: T.class_of()): $12: T.class_of(StandardError) = alias - $13: T.untyped = $5: T.untyped.is_a?($12: T.class_of(StandardError)) - $13 -> (T.untyped ? bb7 : bb8) + $13: T::Boolean = $12: T.class_of(StandardError).===($5: T.untyped) + $13 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) @@ -1058,8 +1399,8 @@ bb1[rubyRegionId=0, firstDead=-1](): # - bb4(rubyRegionId=1) bb3[rubyRegionId=2, firstDead=-1]([]$3: T.untyped, []$4: Integer(0), $9: T.untyped, $12: NilClass, $13: T.untyped, $17: T.class_of()): $20: T.class_of(StandardError) = alias - $21: T.untyped = $13: T.untyped.is_a?($20: T.class_of(StandardError)) - $21 -> (T.untyped ? bb7 : bb8) + $21: T::Boolean = $20: T.class_of(StandardError).===($13: T.untyped) + $21 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) @@ -1107,15 +1448,50 @@ bb9[rubyRegionId=0, firstDead=3]([]$3: T.untyped, []$4: Integer(0), $9 method ::# { -bb0[rubyRegionId=0, firstDead=2](): +bb0[rubyRegionId=0, firstDead=-1](): : T.class_of(TestRescue) = cast(: NilClass, T.class_of(TestRescue)); - : T.noreturn = return $2: NilClass + $5: T.class_of(Sorbet::Private::Static) = alias + $7: Sorbet::Private::Static::Void = $5: T.class_of(Sorbet::Private::Static).sig(: T.class_of(TestRescue)) + $8: T.class_of(TestRescue) = + -> bb2 + +# backedges +# - bb3(rubyRegionId=0) +bb1[rubyRegionId=0, firstDead=-1](): -> bb1 # backedges # - bb0(rubyRegionId=0) -bb1[rubyRegionId=0, firstDead=-1](): +# - bb5(rubyRegionId=1) +bb2[rubyRegionId=1, firstDead=-1](: T.class_of(TestRescue), $7: Sorbet::Private::Static::Void, $8: T.class_of(TestRescue)): + # outerLoops: 1 + -> (NilClass ? bb5 : bb3) + +# backedges +# - bb2(rubyRegionId=1) +bb3[rubyRegionId=0, firstDead=6]($7: Sorbet::Private::Static::Void, $8: T.class_of(TestRescue)): + $3: Sorbet::Private::Static::Void = Solve<$7, sig> + : T.class_of(TestRescue) = $8 + $25: T.class_of(T::Sig) = alias + $27: T.class_of(T) = alias + $22: T.class_of(TestRescue) = : T.class_of(TestRescue).extend($25: T.class_of(T::Sig)) + : T.noreturn = return $2: NilClass -> bb1 +# backedges +# - bb2(rubyRegionId=1) +bb5[rubyRegionId=1, firstDead=9](: T.class_of(TestRescue), $7: Sorbet::Private::Static::Void, $8: T.class_of(TestRescue)): + # outerLoops: 1 + : T::Private::Methods::DeclBuilder = loadSelf(sig) + $13: T.class_of(T::Array) = alias + $15: T.class_of(T) = alias + $18: T.class_of(T) = alias + $20: T.class_of(Exception) = alias + $16: Runtime object representing type: T.class_of(Exception) = $18: T.class_of(T).class_of($20: T.class_of(Exception)) + $11: Runtime object representing type: T::Array[T.class_of(Exception)] = $13: T.class_of(T::Array).[]($16: Runtime object representing type: T.class_of(Exception)) + $9: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.returns($11: Runtime object representing type: T::Array[T.class_of(Exception)]) + $21: T.noreturn = blockreturn $9: T::Private::Methods::DeclBuilder + -> bb2 + } diff --git a/test/testdata/cfg/rescue_complex.rb.desugar-tree.exp b/test/testdata/cfg/rescue_complex.rb.desugar-tree.exp index b2bd80073b..bf8a602210 100644 --- a/test/testdata/cfg/rescue_complex.rb.desugar-tree.exp +++ b/test/testdata/cfg/rescue_complex.rb.desugar-tree.exp @@ -1,5 +1,7 @@ class <>> < (::) class ::<>> < (::) + .extend(::::) + def meth<>(&) 0 end @@ -20,6 +22,18 @@ class <>> < (::) x end + def untyped_exceptions<>(&) + [::] + end + + .sig() do || + .returns(::::.[](::.class_of(::))) + end + + def typed_exceptions<>(&) + [::] + end + def initialize<>(&) @ex = ::.let(nil, ::.nilable(::)) end @@ -38,6 +52,41 @@ class <>> < (::) baz end + def multiple_rescue_classes_varuse<>(&) + begin + .meth() + rescue ::, :: => baz + baz + ::.reveal_type(baz) + end + end + + def rescue_loop<>(&) + begin + ex = ::.let(nil, ::.nilable(::)) + .loop() do || + begin + ex = nil + .meth() + rescue => ex + + end + end + end + end + + def rescue_untyped_splat<>(&) + .meth() + rescue ::.(.untyped_exceptions()) => e + ::.reveal_type(e) + end + + def rescue_typed_splat<>(&) + .meth() + rescue ::.(.typed_exceptions()) => e + ::.reveal_type(e) + end + def parse_rescue_ensure<>(&) .meth() rescue => $2 diff --git a/test/testdata/cfg/rescue_else_block.rb.cfg-text.exp b/test/testdata/cfg/rescue_else_block.rb.cfg-text.exp index 096a250e91..65d38f054c 100644 --- a/test/testdata/cfg/rescue_else_block.rb.cfg-text.exp +++ b/test/testdata/cfg/rescue_else_block.rb.cfg-text.exp @@ -17,8 +17,8 @@ bb1[rubyRegionId=0, firstDead=-1](): # - bb4(rubyRegionId=1) bb3[rubyRegionId=2, firstDead=-1]($2: T.nilable(Integer), $3: T.untyped, $5: T.class_of()): $8: T.class_of(StandardError) = alias - $9: T.untyped = $3: T.untyped.is_a?($8: T.class_of(StandardError)) - $9 -> (T.untyped ? bb10 : bb11) + $9: T::Boolean = $8: T.class_of(StandardError).===($3: T.untyped) + $9 -> (T::Boolean ? bb10 : bb11) # backedges # - bb0(rubyRegionId=0) diff --git a/test/testdata/cfg/rescue_expression.rb.cfg-text.exp b/test/testdata/cfg/rescue_expression.rb.cfg-text.exp index 6067341d10..5146009054 100644 --- a/test/testdata/cfg/rescue_expression.rb.cfg-text.exp +++ b/test/testdata/cfg/rescue_expression.rb.cfg-text.exp @@ -20,8 +20,8 @@ bb3[rubyRegionId=2, firstDead=-1]($2: NilClass, $13: T.class_of(MyException) = alias $11: MyException = $13: T.class_of(MyException).new() $10: T.class_of(MyException) = $11: MyException.class() - $14: T.untyped = e: T.untyped.is_a?($10: T.class_of(MyException)) - $14 -> (T.untyped ? bb7 : bb8) + $14: T::Boolean = $10: T.class_of(MyException).===(e: T.untyped) + $14 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) diff --git a/test/testdata/cfg/rescue_two_return.rb.cfg-text.exp b/test/testdata/cfg/rescue_two_return.rb.cfg-text.exp index 5fe2217c8e..7817fc7d47 100644 --- a/test/testdata/cfg/rescue_two_return.rb.cfg-text.exp +++ b/test/testdata/cfg/rescue_two_return.rb.cfg-text.exp @@ -19,8 +19,8 @@ bb1[rubyRegionId=0, firstDead=-1](): # - bb0(rubyRegionId=0) bb3[rubyRegionId=2, firstDead=-1](: Object, $4: T.untyped, $6: T.class_of()): $9: T.class_of(StandardError) = alias - $10: T.untyped = $4: T.untyped.is_a?($9: T.class_of(StandardError)) - $10 -> (T.untyped ? bb7 : bb8) + $10: T::Boolean = $9: T.class_of(StandardError).===($4: T.untyped) + $10 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) diff --git a/test/testdata/cfg/rescue_var_expression.rb.cfg-text.exp b/test/testdata/cfg/rescue_var_expression.rb.cfg-text.exp index 07374a0ef6..cc85c5d1cf 100644 --- a/test/testdata/cfg/rescue_var_expression.rb.cfg-text.exp +++ b/test/testdata/cfg/rescue_var_expression.rb.cfg-text.exp @@ -18,8 +18,8 @@ bb1[rubyRegionId=0, firstDead=-1](): bb3[rubyRegionId=2, firstDead=-1]($2: NilClass, $3: T.untyped, $6: T.class_of()): $2: T.untyped = $3 $9: T.class_of(Exception) = alias - $10: T.untyped = $3: T.untyped.is_a?($9: T.class_of(Exception)) - $10 -> (T.untyped ? bb7 : bb8) + $10: T::Boolean = $9: T.class_of(Exception).===($3: T.untyped) + $10 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) diff --git a/test/testdata/cfg/rescue_with_return.rb.cfg-text.exp b/test/testdata/cfg/rescue_with_return.rb.cfg-text.exp index 5590ecabff..7cf6486067 100644 --- a/test/testdata/cfg/rescue_with_return.rb.cfg-text.exp +++ b/test/testdata/cfg/rescue_with_return.rb.cfg-text.exp @@ -18,8 +18,8 @@ bb1[rubyRegionId=0, firstDead=-1](): # - bb0(rubyRegionId=0) bb3[rubyRegionId=2, firstDead=-1]($3: T.untyped, $5: T.class_of()): $8: T.class_of(StandardError) = alias - $9: T.untyped = $3: T.untyped.is_a?($8: T.class_of(StandardError)) - $9 -> (T.untyped ? bb7 : bb8) + $9: T::Boolean = $8: T.class_of(StandardError).===($3: T.untyped) + $9 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) diff --git a/test/testdata/cfg/retry.rb.cfg-text.exp b/test/testdata/cfg/retry.rb.cfg-text.exp index 5e25b03bbf..1f5a6ffe97 100644 --- a/test/testdata/cfg/retry.rb.cfg-text.exp +++ b/test/testdata/cfg/retry.rb.cfg-text.exp @@ -24,8 +24,8 @@ bb2[rubyRegionId=0, firstDead=-1](: Object, $2: NilClass # - bb7(rubyRegionId=1) bb3[rubyRegionId=2, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $4: T.untyped, $13: T.class_of()): $16: T.class_of(StandardError) = alias - $17: T.untyped = $4: T.untyped.is_a?($16: T.class_of(StandardError)) - $17 -> (T.untyped ? bb10 : bb11) + $17: T::Boolean = $16: T.class_of(StandardError).===($4: T.untyped) + $17 -> (T::Boolean ? bb10 : bb11) # backedges # - bb2(rubyRegionId=0) diff --git a/test/testdata/cfg/retry_multiple.rb.cfg-text.exp b/test/testdata/cfg/retry_multiple.rb.cfg-text.exp index 3483ed9044..297f8a2321 100644 --- a/test/testdata/cfg/retry_multiple.rb.cfg-text.exp +++ b/test/testdata/cfg/retry_multiple.rb.cfg-text.exp @@ -25,8 +25,8 @@ bb2[rubyRegionId=0, firstDead=-1](: Object, $2: NilClass # - bb10(rubyRegionId=1) bb3[rubyRegionId=2, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $4: T.untyped, $25: T.class_of()): $28: T.class_of(A) = alias - $29: T.untyped = $4: T.untyped.is_a?($28: T.class_of(A)) - $29 -> (T.untyped ? bb13 : bb14) + $29: T::Boolean = $28: T.class_of(A).===($4: T.untyped) + $29 -> (T::Boolean ? bb13 : bb14) # backedges # - bb2(rubyRegionId=0) @@ -98,8 +98,8 @@ bb13[rubyRegionId=2, firstDead=-1](: Object, $2: NilClas # - bb3(rubyRegionId=2) bb14[rubyRegionId=2, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $4: T.untyped, $25: T.class_of()): $38: T.class_of(B) = alias - $39: T.untyped = $4: T.untyped.is_a?($38: T.class_of(B)) - $39 -> (T.untyped ? bb15 : bb16) + $39: T::Boolean = $38: T.class_of(B).===($4: T.untyped) + $39 -> (T::Boolean ? bb15 : bb16) # backedges # - bb14(rubyRegionId=2) diff --git a/test/testdata/cfg/retry_nested.rb.cfg-text.exp b/test/testdata/cfg/retry_nested.rb.cfg-text.exp index 732004b58c..3b41fb2c3d 100644 --- a/test/testdata/cfg/retry_nested.rb.cfg-text.exp +++ b/test/testdata/cfg/retry_nested.rb.cfg-text.exp @@ -25,8 +25,8 @@ bb2[rubyRegionId=0, firstDead=-1](: Object, $2: NilClass # - bb18(rubyRegionId=1) bb3[rubyRegionId=2, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $4: T.untyped, $40: NilClass, $42: T.class_of()): $45: T.class_of(B) = alias - $46: T.untyped = $4: T.untyped.is_a?($45: T.class_of(B)) - $46 -> (T.untyped ? bb21 : bb22) + $46: T::Boolean = $45: T.class_of(B).===($4: T.untyped) + $46 -> (T::Boolean ? bb21 : bb22) # backedges # - bb2(rubyRegionId=0) @@ -48,8 +48,8 @@ bb5[rubyRegionId=1, firstDead=-1](: Object, $2: NilClass # - bb13(rubyRegionId=5) bb6[rubyRegionId=6, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $8: T.untyped, $29: T.class_of(), $40: NilClass, $42: T.class_of()): $32: T.class_of(A) = alias - $33: T.untyped = $8: T.untyped.is_a?($32: T.class_of(A)) - $33 -> (T.untyped ? bb16 : bb17) + $33: T::Boolean = $32: T.class_of(A).===($8: T.untyped) + $33 -> (T::Boolean ? bb16 : bb17) # backedges # - bb5(rubyRegionId=1) diff --git a/test/testdata/cfg/return_type_of_nilable_blk_param.rb b/test/testdata/cfg/return_type_of_nilable_blk_param.rb index a785438f79..c06a10f2e1 100644 --- a/test/testdata/cfg/return_type_of_nilable_blk_param.rb +++ b/test/testdata/cfg/return_type_of_nilable_blk_param.rb @@ -22,12 +22,14 @@ def baz(&blk) end def main - A.new.bar do |r| # error: Expected `Integer` but found `String` for block result type + A.new.bar do |r| r.to_s + # ^^^^^^ error: Expected `Integer` but found `String` for block result type end - A.new.baz do |r| # error: Expected `Integer` but found `String` for block result type + A.new.baz do |r| r.to_s + # ^^^^^^ error: Expected `Integer` but found `String` for block result type end A.new.bar do |r| diff --git a/test/testdata/cfg/textoutput.rb.cfg-text.exp b/test/testdata/cfg/textoutput.rb.cfg-text.exp index c214960e82..d26252caa6 100644 --- a/test/testdata/cfg/textoutput.rb.cfg-text.exp +++ b/test/testdata/cfg/textoutput.rb.cfg-text.exp @@ -21,8 +21,8 @@ bb1[rubyRegionId=0, firstDead=-1](): bb3[rubyRegionId=2, firstDead=-1](: T.class_of(), $11: T.untyped, $22: T.class_of()): e: T.untyped = $11 $25: T.class_of(StandardError) = alias - $26: T.untyped = e: T.untyped.is_a?($25: T.class_of(StandardError)) - $26 -> (T.untyped ? bb7 : bb8) + $26: T::Boolean = $25: T.class_of(StandardError).===(e: T.untyped) + $26 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) diff --git a/test/testdata/cfg/uaf1.rb.cfg-text.exp b/test/testdata/cfg/uaf1.rb.cfg-text.exp index df6675ebd5..4608b0e8ff 100644 --- a/test/testdata/cfg/uaf1.rb.cfg-text.exp +++ b/test/testdata/cfg/uaf1.rb.cfg-text.exp @@ -33,7 +33,7 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb13(rubyRegionId=1) -bb2[rubyRegionId=1, firstDead=-1](: A, $5: Sorbet::Private::Static::Void, $6: A, $8: NilClass, $15: NilClass): +bb2[rubyRegionId=1, firstDead=-1](: A, $5: Sorbet::Private::Static::Void, $6: A, $7: NilClass, $14: NilClass): # outerLoops: 1 -> (NilClass ? bb5 : bb3) @@ -46,34 +46,34 @@ bb3[rubyRegionId=0, firstDead=2]($5: Sorbet::Private::Stati # backedges # - bb2(rubyRegionId=1) -bb5[rubyRegionId=1, firstDead=-1](: A, $5: Sorbet::Private::Static::Void, $6: A, $8: NilClass, $15: NilClass): +bb5[rubyRegionId=1, firstDead=-1](: A, $5: Sorbet::Private::Static::Void, $6: A, $7: NilClass, $14: NilClass): # outerLoops: 1 : A = loadSelf(map) - $10: T.class_of() = alias > - $9: T.untyped = - $9 -> (T.untyped ? bb7 : bb8) + $9: T.class_of() = alias > + $8: T.untyped = + $8 -> (T.untyped ? bb7 : bb8) # backedges # - bb5(rubyRegionId=1) # - bb8(rubyRegionId=2) -bb7[rubyRegionId=3, firstDead=-1](: A, $5: Sorbet::Private::Static::Void, $6: A, $8: T.nilable(Integer), $9: T.untyped, $10: T.class_of(), $15: NilClass): +bb7[rubyRegionId=3, firstDead=-1](: A, $5: Sorbet::Private::Static::Void, $6: A, $7: T.nilable(Integer), $8: T.untyped, $9: T.class_of(), $14: NilClass): # outerLoops: 1 - se$1: T.untyped = $9 - $13: T.class_of(StandardError) = alias - $14: T.untyped = se$1: T.untyped.is_a?($13: T.class_of(StandardError)) - $14 -> (T.untyped ? bb11 : bb12) + se$1: T.untyped = $8 + $12: T.class_of(StandardError) = alias + $13: T::Boolean = $12: T.class_of(StandardError).===(se$1: T.untyped) + $13 -> (T::Boolean ? bb11 : bb12) # backedges # - bb5(rubyRegionId=1) -bb8[rubyRegionId=2, firstDead=-1](: A, $5: Sorbet::Private::Static::Void, $6: A, $10: T.class_of(), $15: NilClass): +bb8[rubyRegionId=2, firstDead=-1](: A, $5: Sorbet::Private::Static::Void, $6: A, $9: T.class_of(), $14: NilClass): # outerLoops: 1 - $8: Integer(1) = 1 - $9: T.untyped = - $9 -> (T.untyped ? bb7 : bb9) + $7: Integer(1) = 1 + $8: T.untyped = + $8 -> (T.untyped ? bb7 : bb9) # backedges # - bb8(rubyRegionId=2) -bb9[rubyRegionId=5, firstDead=-1](: A, $5: Sorbet::Private::Static::Void, $6: A, $8: Integer(1), $15: NilClass): +bb9[rubyRegionId=5, firstDead=-1](: A, $5: Sorbet::Private::Static::Void, $6: A, $7: Integer(1), $14: NilClass): # outerLoops: 1 -> bb10 @@ -81,31 +81,31 @@ bb9[rubyRegionId=5, firstDead=-1](: A, $5: Sorbet::Pr # - bb9(rubyRegionId=5) # - bb11(rubyRegionId=3) # - bb12(rubyRegionId=3) -bb10[rubyRegionId=4, firstDead=-1](: A, $5: Sorbet::Private::Static::Void, $6: A, $8: T.nilable(Integer), $15: T.nilable(TrueClass)): +bb10[rubyRegionId=4, firstDead=-1](: A, $5: Sorbet::Private::Static::Void, $6: A, $7: T.nilable(Integer), $14: T.nilable(TrueClass)): # outerLoops: 1 - $15 -> (T.nilable(TrueClass) ? bb1 : bb13) + $14 -> (T.nilable(TrueClass) ? bb1 : bb13) # backedges # - bb7(rubyRegionId=3) -bb11[rubyRegionId=3, firstDead=-1](: A, $5: Sorbet::Private::Static::Void, $6: A, $10: T.class_of(), $15: NilClass): +bb11[rubyRegionId=3, firstDead=-1](: A, $5: Sorbet::Private::Static::Void, $6: A, $9: T.class_of(), $14: NilClass): # outerLoops: 1 - $9: NilClass = nil - $11: Sorbet::Private::Static::Void = $10: T.class_of().($9: NilClass) - $8: Integer(2) = 2 + $8: NilClass = nil + $10: Sorbet::Private::Static::Void = $9: T.class_of().($8: NilClass) + $7: Integer(2) = 2 -> bb10 # backedges # - bb7(rubyRegionId=3) -bb12[rubyRegionId=3, firstDead=-1](: A, $5: Sorbet::Private::Static::Void, $6: A, $8: T.nilable(Integer)): +bb12[rubyRegionId=3, firstDead=-1](: A, $5: Sorbet::Private::Static::Void, $6: A, $7: T.nilable(Integer)): # outerLoops: 1 - $15: TrueClass = true + $14: TrueClass = true -> bb10 # backedges # - bb10(rubyRegionId=4) -bb13[rubyRegionId=1, firstDead=1](: A, $5: Sorbet::Private::Static::Void, $6: A, $8: Integer, $15: NilClass): +bb13[rubyRegionId=1, firstDead=1](: A, $5: Sorbet::Private::Static::Void, $6: A, $7: Integer, $14: NilClass): # outerLoops: 1 - $17: T.noreturn = blockreturn $8: Integer + $16: T.noreturn = blockreturn $7: Integer -> bb2 } diff --git a/test/testdata/compiler/all_arguments.opt.ll.exp b/test/testdata/compiler/all_arguments.opt.ll.exp index 4cd7b2dcf6..39616d994e 100644 --- a/test/testdata/compiler/all_arguments.opt.ll.exp +++ b/test/testdata/compiler/all_arguments.opt.ll.exp @@ -2,16 +2,17 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -27,34 +28,34 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.rb_fiber_struct = type opaque -%struct.anon.3 = type { [65 x i64] } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -68,7 +69,7 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } +%struct.anon.6 = type { i64, i64, i64, i64 } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } %struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } diff --git a/test/testdata/compiler/block_arg_expand.opt.ll.exp b/test/testdata/compiler/block_arg_expand.opt.ll.exp index e8ada5f634..44ec48703d 100644 --- a/test/testdata/compiler/block_arg_expand.opt.ll.exp +++ b/test/testdata/compiler/block_arg_expand.opt.ll.exp @@ -2,10 +2,10 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -21,27 +21,28 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_fiber_struct = type opaque -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.anon.3 = type { [65 x i64] } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -49,24 +50,24 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_objspace = type opaque %struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.anon.6 = type { i64, i64, i64, i64 } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %union.pthread_mutex_t = type { %struct.__pthread_mutex_s } %struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } %struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } @@ -74,20 +75,19 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } %struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } %struct.rb_call_info_kw_arg = type { i32, [1 x i64] } -%struct.rb_captured_block = type { i64, i64*, %union.anon.17 } -%union.anon.17 = type { %struct.rb_iseq_struct* } +%struct.rb_captured_block = type { i64, i64*, %union.anon.20 } +%union.anon.20 = type { %struct.rb_iseq_struct* } %struct.vm_ifunc = type { i64, i64, i64 (i64, i64, i32, i64*, i64)*, i8*, %struct.rb_code_position_struct } %struct.iseq_inline_iv_cache_entry = type { i64, i64 } -%struct.RArray = type { %struct.iseq_inline_iv_cache_entry, %union.anon.25 } -%union.anon.25 = type { %struct.anon.26 } -%struct.anon.26 = type { i64, %union.anon.27, i64* } -%union.anon.27 = type { i64 } +%struct.RArray = type { %struct.iseq_inline_iv_cache_entry, %union.anon.28 } +%union.anon.28 = type { %struct.anon.29 } +%struct.anon.29 = type { i64, %union.anon, i64* } %struct.sorbet_inlineIntrinsicEnv = type { i64, i64, i32, i64*, i64 } @ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 @rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 +@.str.8 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 +@.str.9 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 @"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 @iseqEncodedArray = internal global [32 x i64] zeroinitializer @fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer @@ -174,14 +174,14 @@ declare %struct.vm_ifunc* @rb_vm_ifunc_new(i64 (i64, i64, i32, i64*, i64)*, i8*, ; Function Attrs: nounwind ssp uwtable define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #7 { %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #12 + tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.9, i64 0, i64 0)) #12 unreachable } ; Function Attrs: nounwind ssp uwtable define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #7 { %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #12 + tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.8, i64 0, i64 0)) #12 unreachable } @@ -278,22 +278,22 @@ fillFromDefaultBlockDone2: ; preds = %argArrayExpandArray br i1 %44, label %45, label %typeTestSuccess, !dbg !30, !prof !28 45: ; preds = %fillFromDefaultBlockDone2.thread, %fillFromDefaultBlockDone2 - %el1.sroa.0.149 = phi i64 [ 8, %fillFromDefaultBlockDone2.thread ], [ %el1.sroa.0.1, %fillFromDefaultBlockDone2 ] - %el2.sroa.0.046 = phi i64 [ 8, %fillFromDefaultBlockDone2.thread ], [ %el2.sroa.0.0, %fillFromDefaultBlockDone2 ] - %46 = and i64 %el1.sroa.0.149, 7, !dbg !30 + %el1.sroa.0.148 = phi i64 [ 8, %fillFromDefaultBlockDone2.thread ], [ %el1.sroa.0.1, %fillFromDefaultBlockDone2 ] + %el2.sroa.0.045 = phi i64 [ 8, %fillFromDefaultBlockDone2.thread ], [ %el2.sroa.0.0, %fillFromDefaultBlockDone2 ] + %46 = and i64 %el1.sroa.0.148, 7, !dbg !30 %47 = icmp ne i64 %46, 0, !dbg !30 - %48 = and i64 %el1.sroa.0.149, -9, !dbg !30 + %48 = and i64 %el1.sroa.0.148, -9, !dbg !30 %49 = icmp eq i64 %48, 0, !dbg !30 %50 = or i1 %47, %49, !dbg !30 - br i1 %50, label %codeRepl41, label %sorbet_isa_Integer.exit, !dbg !30 + br i1 %50, label %codeRepl40, label %sorbet_isa_Integer.exit, !dbg !30 sorbet_isa_Integer.exit: ; preds = %45 - %51 = inttoptr i64 %el1.sroa.0.149 to %struct.iseq_inline_iv_cache_entry*, !dbg !30 + %51 = inttoptr i64 %el1.sroa.0.148 to %struct.iseq_inline_iv_cache_entry*, !dbg !30 %52 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %51, i64 0, i32 0, !dbg !30 %53 = load i64, i64* %52, align 8, !dbg !30, !tbaa !25 %54 = and i64 %53, 31, !dbg !30 %55 = icmp eq i64 %54, 10, !dbg !30 - br i1 %55, label %typeTestSuccess, label %codeRepl41, !dbg !30, !prof !31 + br i1 %55, label %typeTestSuccess, label %codeRepl40, !dbg !30, !prof !31 fillRequiredArgs: ; preds = %functionEntryInitializers, %rb_array_len.exit %argcPhi = phi i32 [ %argc, %functionEntryInitializers ], [ %41, %rb_array_len.exit ], !dbg !24 @@ -306,48 +306,48 @@ fillFromDefaultBlockDone2.thread: ; preds = %fillRequiredArgs br label %45, !dbg !30 typeTestSuccess: ; preds = %fillFromDefaultBlockDone2, %sorbet_isa_Integer.exit - %el1.sroa.0.148 = phi i64 [ %el1.sroa.0.1, %fillFromDefaultBlockDone2 ], [ %el1.sroa.0.149, %sorbet_isa_Integer.exit ] - %el2.sroa.0.045 = phi i64 [ %el2.sroa.0.0, %fillFromDefaultBlockDone2 ], [ %el2.sroa.0.046, %sorbet_isa_Integer.exit ] - %56 = and i64 %el2.sroa.0.045, 1, !dbg !32 + %el1.sroa.0.147 = phi i64 [ %el1.sroa.0.1, %fillFromDefaultBlockDone2 ], [ %el1.sroa.0.148, %sorbet_isa_Integer.exit ] + %el2.sroa.0.044 = phi i64 [ %el2.sroa.0.0, %fillFromDefaultBlockDone2 ], [ %el2.sroa.0.045, %sorbet_isa_Integer.exit ] + %56 = and i64 %el2.sroa.0.044, 1, !dbg !32 %57 = icmp eq i64 %56, 0, !dbg !32 br i1 %57, label %58, label %"fastSymCallIntrinsic_Integer_+", !dbg !32, !prof !28 58: ; preds = %typeTestSuccess - %59 = and i64 %el2.sroa.0.045, 7, !dbg !32 + %59 = and i64 %el2.sroa.0.044, 7, !dbg !32 %60 = icmp ne i64 %59, 0, !dbg !32 - %61 = and i64 %el2.sroa.0.045, -9, !dbg !32 + %61 = and i64 %el2.sroa.0.044, -9, !dbg !32 %62 = icmp eq i64 %61, 0, !dbg !32 %63 = or i1 %60, %62, !dbg !32 - br i1 %63, label %codeRepl, label %sorbet_isa_Integer.exit42, !dbg !32 + br i1 %63, label %codeRepl, label %sorbet_isa_Integer.exit41, !dbg !32 -sorbet_isa_Integer.exit42: ; preds = %58 - %64 = inttoptr i64 %el2.sroa.0.045 to %struct.iseq_inline_iv_cache_entry*, !dbg !32 +sorbet_isa_Integer.exit41: ; preds = %58 + %64 = inttoptr i64 %el2.sroa.0.044 to %struct.iseq_inline_iv_cache_entry*, !dbg !32 %65 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %64, i64 0, i32 0, !dbg !32 %66 = load i64, i64* %65, align 8, !dbg !32, !tbaa !25 %67 = and i64 %66, 31, !dbg !32 %68 = icmp eq i64 %67, 10, !dbg !32 br i1 %68, label %"fastSymCallIntrinsic_Integer_+", label %codeRepl, !dbg !32, !prof !31 -codeRepl41: ; preds = %45, %sorbet_isa_Integer.exit - %el1.sroa.0.150 = phi i64 [ %el1.sroa.0.149, %45 ], [ %el1.sroa.0.149, %sorbet_isa_Integer.exit ] - tail call fastcc void @"func_.13$block_1.cold.1"(i64 %el1.sroa.0.150) #14, !dbg !30 +codeRepl40: ; preds = %45, %sorbet_isa_Integer.exit + %el1.sroa.0.149 = phi i64 [ %el1.sroa.0.148, %45 ], [ %el1.sroa.0.148, %sorbet_isa_Integer.exit ] + tail call fastcc void @"func_.13$block_1.cold.1"(i64 %el1.sroa.0.149) #14, !dbg !30 unreachable -codeRepl: ; preds = %58, %sorbet_isa_Integer.exit42 - %el2.sroa.0.047 = phi i64 [ %el2.sroa.0.045, %58 ], [ %el2.sroa.0.045, %sorbet_isa_Integer.exit42 ] - tail call fastcc void @"func_.13$block_1.cold.1"(i64 %el2.sroa.0.047) #14, !dbg !32 +codeRepl: ; preds = %58, %sorbet_isa_Integer.exit41 + %el2.sroa.0.046 = phi i64 [ %el2.sroa.0.044, %58 ], [ %el2.sroa.0.044, %sorbet_isa_Integer.exit41 ] + tail call fastcc void @"func_.13$block_1.cold.1"(i64 %el2.sroa.0.046) #14, !dbg !32 unreachable -"fastSymCallIntrinsic_Integer_+": ; preds = %typeTestSuccess, %sorbet_isa_Integer.exit42 +"fastSymCallIntrinsic_Integer_+": ; preds = %typeTestSuccess, %sorbet_isa_Integer.exit41 tail call void @llvm.experimental.noalias.scope.decl(metadata !33), !dbg !30 - %69 = and i64 %el2.sroa.0.045, 1, !dbg !30 - %70 = and i64 %69, %el1.sroa.0.148, !dbg !30 + %69 = and i64 %el2.sroa.0.044, 1, !dbg !30 + %70 = and i64 %69, %el1.sroa.0.147, !dbg !30 %71 = icmp eq i64 %70, 0, !dbg !30 br i1 %71, label %81, label %72, !dbg !30, !prof !36 72: ; preds = %"fastSymCallIntrinsic_Integer_+" - %73 = add nsw i64 %el2.sroa.0.045, -1, !dbg !30 - %74 = tail call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %el1.sroa.0.148, i64 %73) #15, !dbg !30 + %73 = add nsw i64 %el2.sroa.0.044, -1, !dbg !30 + %74 = tail call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %el1.sroa.0.147, i64 %73) #15, !dbg !30 %75 = extractvalue { i64, i1 } %74, 1, !dbg !30 %76 = extractvalue { i64, i1 } %74, 0, !dbg !30 br i1 %75, label %77, label %sorbet_rb_int_plus.exit, !dbg !30 @@ -359,7 +359,7 @@ codeRepl: ; preds = %58, %sorbet_isa_Int br label %sorbet_rb_int_plus.exit, !dbg !30 81: ; preds = %"fastSymCallIntrinsic_Integer_+" - %82 = tail call i64 @sorbet_rb_int_plus_slowpath(i64 %el1.sroa.0.148, i64 %el2.sroa.0.045) #13, !dbg !30, !noalias !33 + %82 = tail call i64 @sorbet_rb_int_plus_slowpath(i64 %el1.sroa.0.147, i64 %el2.sroa.0.044) #13, !dbg !30, !noalias !33 br label %sorbet_rb_int_plus.exit, !dbg !30 sorbet_rb_int_plus.exit: ; preds = %77, %72, %81 @@ -381,18 +381,17 @@ sorbet_rb_int_plus.exit: ; preds = %77, %72, %81 br label %rb_vm_check_ints.exit, !dbg !30 rb_vm_check_ints.exit: ; preds = %sorbet_rb_int_plus.exit, %92 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %8, align 8, !dbg !30, !tbaa !15 - ret i64 %83, !dbg !40 + ret i64 %83, !dbg !30 } ; Function Attrs: ssp -define internal i64 @"func_.13$block_2"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture readonly %argArray, i64 %blockArg) #8 !dbg !41 { +define internal i64 @"func_.13$block_2"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture readonly %argArray, i64 %blockArg) #8 !dbg !40 { functionEntryInitializers: %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !17 %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !42 + %4 = load i64, i64* %3, align 8, !tbaa !41 %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_2", align 8 %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %5, align 8, !tbaa !21 @@ -403,36 +402,35 @@ functionEntryInitializers: store i64 %9, i64* %7, align 8, !tbaa !6 %10 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %10, align 8, !tbaa !15 - %default0 = icmp eq i32 %argc, 0, !dbg !43 - br i1 %default0, label %fillFromDefaultBlockDone1, label %fillFromArgBlock0, !dbg !43, !prof !28 + %default0 = icmp eq i32 %argc, 0, !dbg !42 + br i1 %default0, label %fillFromDefaultBlockDone1, label %fillFromArgBlock0, !dbg !42, !prof !28 fillFromArgBlock0: ; preds = %functionEntryInitializers - %rawArg_array = load i64, i64* %argArray, align 8, !dbg !43 - br label %fillFromDefaultBlockDone1, !dbg !43 + %rawArg_array = load i64, i64* %argArray, align 8, !dbg !42 + br label %fillFromDefaultBlockDone1, !dbg !42 fillFromDefaultBlockDone1: ; preds = %functionEntryInitializers, %fillFromArgBlock0 - %array.sroa.0.0 = phi i64 [ %rawArg_array, %fillFromArgBlock0 ], [ 8, %functionEntryInitializers ], !dbg !43 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %10, align 8, !dbg !44, !tbaa !15 - %11 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !45 - %12 = load i64*, i64** %11, align 8, !dbg !45 - store i64 %4, i64* %12, align 8, !dbg !45, !tbaa !6 - %13 = getelementptr inbounds i64, i64* %12, i64 1, !dbg !45 - store i64 %array.sroa.0.0, i64* %13, align 8, !dbg !45, !tbaa !6 - %14 = getelementptr inbounds i64, i64* %13, i64 1, !dbg !45 - store i64* %14, i64** %11, align 8, !dbg !45 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p, i64 0), !dbg !45 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %10, align 8, !dbg !45, !tbaa !15 - ret i64 %send, !dbg !46 + %array.sroa.0.0 = phi i64 [ %rawArg_array, %fillFromArgBlock0 ], [ 8, %functionEntryInitializers ], !dbg !42 + store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %10, align 8, !dbg !43, !tbaa !15 + %11 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !44 + %12 = load i64*, i64** %11, align 8, !dbg !44 + store i64 %4, i64* %12, align 8, !dbg !44, !tbaa !6 + %13 = getelementptr inbounds i64, i64* %12, i64 1, !dbg !44 + store i64 %array.sroa.0.0, i64* %13, align 8, !dbg !44, !tbaa !6 + %14 = getelementptr inbounds i64, i64* %13, i64 1, !dbg !44 + store i64* %14, i64** %11, align 8, !dbg !44 + %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p, i64 0), !dbg !44 + ret i64 %send, !dbg !44 } ; Function Attrs: ssp -define internal i64 @"func_.13$block_3"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture readonly %argArray, i64 %blockArg) #8 !dbg !47 { +define internal i64 @"func_.13$block_3"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture readonly %argArray, i64 %blockArg) #8 !dbg !45 { functionEntryInitializers: %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !17 %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !42 + %4 = load i64, i64* %3, align 8, !tbaa !41 %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_3", align 8 %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %5, align 8, !tbaa !21 @@ -443,43 +441,42 @@ functionEntryInitializers: store i64 %9, i64* %7, align 8, !tbaa !6 %10 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %10, align 8, !tbaa !15 - %default0 = icmp eq i32 %argc, 0, !dbg !48 - br i1 %default0, label %BB15, label %BB14, !dbg !48, !prof !28 + %default0 = icmp eq i32 %argc, 0, !dbg !46 + br i1 %default0, label %BB15, label %BB14, !dbg !46, !prof !28 BB14: ; preds = %functionEntryInitializers - %rawArg_array = load i64, i64* %argArray, align 8, !dbg !48 + %rawArg_array = load i64, i64* %argArray, align 8, !dbg !46 store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %10, align 8, !tbaa !15 - br label %BB16, !dbg !49 + br label %BB16, !dbg !47 BB15: ; preds = %functionEntryInitializers store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %10, align 8, !tbaa !15 - %rubyId_x = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 7), align 8, !dbg !50, !invariant.load !5 - %rawSym = tail call i64 @rb_id2sym(i64 %rubyId_x) #16, !dbg !50 - br label %BB16, !dbg !50 + %rubyId_x = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 7), align 8, !dbg !48, !invariant.load !5 + %rawSym = tail call i64 @rb_id2sym(i64 %rubyId_x) #16, !dbg !48 + br label %BB16, !dbg !48 BB16: ; preds = %BB15, %BB14 - %array.sroa.0.0 = phi i64 [ %rawArg_array, %BB14 ], [ %rawSym, %BB15 ], !dbg !51 + %array.sroa.0.0 = phi i64 [ %rawArg_array, %BB14 ], [ %rawSym, %BB15 ], !dbg !49 store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 19), i64** %10, align 8, !tbaa !15 - %11 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !52 - %12 = load i64*, i64** %11, align 8, !dbg !52 - store i64 %4, i64* %12, align 8, !dbg !52, !tbaa !6 - %13 = getelementptr inbounds i64, i64* %12, i64 1, !dbg !52 - store i64 %array.sroa.0.0, i64* %13, align 8, !dbg !52, !tbaa !6 - %14 = getelementptr inbounds i64, i64* %13, i64 1, !dbg !52 - store i64* %14, i64** %11, align 8, !dbg !52 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.5, i64 0), !dbg !52 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %10, align 8, !dbg !52, !tbaa !15 - ret i64 %send, !dbg !53 + %11 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !50 + %12 = load i64*, i64** %11, align 8, !dbg !50 + store i64 %4, i64* %12, align 8, !dbg !50, !tbaa !6 + %13 = getelementptr inbounds i64, i64* %12, i64 1, !dbg !50 + store i64 %array.sroa.0.0, i64* %13, align 8, !dbg !50, !tbaa !6 + %14 = getelementptr inbounds i64, i64* %13, i64 1, !dbg !50 + store i64* %14, i64** %11, align 8, !dbg !50 + %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.5, i64 0), !dbg !50 + ret i64 %send, !dbg !50 } ; Function Attrs: ssp -define internal i64 @"func_.13$block_4"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture readonly %argArray, i64 %blockArg) #8 !dbg !54 { +define internal i64 @"func_.13$block_4"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture readonly %argArray, i64 %blockArg) #8 !dbg !51 { functionEntryInitializers: %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !17 %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !42 + %4 = load i64, i64* %3, align 8, !tbaa !41 %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_4", align 8 %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %5, align 8, !tbaa !21 @@ -490,132 +487,131 @@ functionEntryInitializers: store i64 %9, i64* %7, align 8, !tbaa !6 %10 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %10, align 8, !tbaa !15 - %arrayExpansionSizeGuard = icmp eq i32 %argc, 1, !dbg !55 - br i1 %arrayExpansionSizeGuard, label %argArrayExpandArrayTest, label %fillRequiredArgs, !dbg !55 + %arrayExpansionSizeGuard = icmp eq i32 %argc, 1, !dbg !52 + br i1 %arrayExpansionSizeGuard, label %argArrayExpandArrayTest, label %fillRequiredArgs, !dbg !52 BB23.thread: ; preds = %fillRequiredArgs store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 23), i64** %10, align 8, !tbaa !15 - %rubyId_default = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 8), align 8, !dbg !56, !invariant.load !5 - %rawSym = tail call i64 @rb_id2sym(i64 %rubyId_default) #16, !dbg !56 - br label %BB25, !dbg !57 + %rubyId_default = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 8), align 8, !dbg !53, !invariant.load !5 + %rawSym = tail call i64 @rb_id2sym(i64 %rubyId_default) #16, !dbg !53 + br label %BB25, !dbg !54 -BB23.thread62: ; preds = %argArrayExpandArrayTest, %sorbet_isa_Array.exit, %fillFromArgBlock0 +BB23.thread61: ; preds = %argArrayExpandArrayTest, %sorbet_isa_Array.exit, %fillFromArgBlock0 %x.sroa.0.2.ph.ph = phi i64 [ %rawArg_x, %fillFromArgBlock0 ], [ %arg1_maybeExpandToFullArgs, %sorbet_isa_Array.exit ], [ %arg1_maybeExpandToFullArgs, %argArrayExpandArrayTest ] store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 23), i64** %10, align 8, !tbaa !15 - br label %BB25, !dbg !57 + br label %BB25, !dbg !54 BB24: ; preds = %fillFromArgBlock0 - %11 = getelementptr i64, i64* %argArrayPhi, i32 1, !dbg !55 - %rawArg_y = load i64, i64* %11, align 8, !dbg !55 + %11 = getelementptr i64, i64* %argArrayPhi, i32 1, !dbg !52 + %rawArg_y = load i64, i64* %11, align 8, !dbg !52 store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 23), i64** %10, align 8, !tbaa !15 - br label %BB26, !dbg !57 + br label %BB26, !dbg !54 -BB25: ; preds = %BB23.thread62, %BB23.thread - %x.sroa.0.061 = phi i64 [ %rawSym, %BB23.thread ], [ %x.sroa.0.2.ph.ph, %BB23.thread62 ] +BB25: ; preds = %BB23.thread61, %BB23.thread + %x.sroa.0.060 = phi i64 [ %rawSym, %BB23.thread ], [ %x.sroa.0.2.ph.ph, %BB23.thread61 ] store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 23), i64** %10, align 8, !tbaa !15 - %rubyId_something = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 9), align 8, !dbg !58, !invariant.load !5 - %rawSym16 = tail call i64 @rb_id2sym(i64 %rubyId_something) #16, !dbg !58 - br label %BB26, !dbg !58 + %rubyId_something = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 9), align 8, !dbg !55, !invariant.load !5 + %rawSym16 = tail call i64 @rb_id2sym(i64 %rubyId_something) #16, !dbg !55 + br label %BB26, !dbg !55 BB26: ; preds = %BB25, %BB24 - %x.sroa.0.060 = phi i64 [ %rawArg_x, %BB24 ], [ %x.sroa.0.061, %BB25 ] - %y.sroa.0.0 = phi i64 [ %rawArg_y, %BB24 ], [ %rawSym16, %BB25 ], !dbg !59 + %x.sroa.0.059 = phi i64 [ %rawArg_x, %BB24 ], [ %x.sroa.0.060, %BB25 ] + %y.sroa.0.0 = phi i64 [ %rawArg_y, %BB24 ], [ %rawSym16, %BB25 ], !dbg !56 store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 24), i64** %10, align 8, !tbaa !15 - %12 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !60 - %13 = load i64*, i64** %12, align 8, !dbg !60 - store i64 %4, i64* %13, align 8, !dbg !60, !tbaa !6 - %14 = getelementptr inbounds i64, i64* %13, i64 1, !dbg !60 - store i64 %x.sroa.0.060, i64* %14, align 8, !dbg !60, !tbaa !6 - %15 = getelementptr inbounds i64, i64* %14, i64 1, !dbg !60 - store i64* %15, i64** %12, align 8, !dbg !60 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.8, i64 0), !dbg !60 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 25), i64** %10, align 8, !dbg !60, !tbaa !15 - %16 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !61 - %17 = load i64*, i64** %16, align 8, !dbg !61 - store i64 %4, i64* %17, align 8, !dbg !61, !tbaa !6 - %18 = getelementptr inbounds i64, i64* %17, i64 1, !dbg !61 - store i64 %y.sroa.0.0, i64* %18, align 8, !dbg !61, !tbaa !6 - %19 = getelementptr inbounds i64, i64* %18, i64 1, !dbg !61 - store i64* %19, i64** %16, align 8, !dbg !61 - %send67 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.9, i64 0), !dbg !61 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 23), i64** %10, align 8, !dbg !61, !tbaa !15 - ret i64 %send67, !dbg !62 + %12 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !57 + %13 = load i64*, i64** %12, align 8, !dbg !57 + store i64 %4, i64* %13, align 8, !dbg !57, !tbaa !6 + %14 = getelementptr inbounds i64, i64* %13, i64 1, !dbg !57 + store i64 %x.sroa.0.059, i64* %14, align 8, !dbg !57, !tbaa !6 + %15 = getelementptr inbounds i64, i64* %14, i64 1, !dbg !57 + store i64* %15, i64** %12, align 8, !dbg !57 + %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.8, i64 0), !dbg !57 + store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 25), i64** %10, align 8, !dbg !57, !tbaa !15 + %16 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !58 + %17 = load i64*, i64** %16, align 8, !dbg !58 + store i64 %4, i64* %17, align 8, !dbg !58, !tbaa !6 + %18 = getelementptr inbounds i64, i64* %17, i64 1, !dbg !58 + store i64 %y.sroa.0.0, i64* %18, align 8, !dbg !58, !tbaa !6 + %19 = getelementptr inbounds i64, i64* %18, i64 1, !dbg !58 + store i64* %19, i64** %16, align 8, !dbg !58 + %send66 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.9, i64 0), !dbg !58 + ret i64 %send66, !dbg !58 argArrayExpandArrayTest: ; preds = %functionEntryInitializers - %arg1_maybeExpandToFullArgs = load i64, i64* %argArray, align 8, !dbg !55 - %20 = and i64 %arg1_maybeExpandToFullArgs, 7, !dbg !55 - %21 = icmp ne i64 %20, 0, !dbg !55 - %22 = and i64 %arg1_maybeExpandToFullArgs, -9, !dbg !55 - %23 = icmp eq i64 %22, 0, !dbg !55 - %24 = or i1 %21, %23, !dbg !55 - br i1 %24, label %BB23.thread62, label %sorbet_isa_Array.exit, !dbg !55 + %arg1_maybeExpandToFullArgs = load i64, i64* %argArray, align 8, !dbg !52 + %20 = and i64 %arg1_maybeExpandToFullArgs, 7, !dbg !52 + %21 = icmp ne i64 %20, 0, !dbg !52 + %22 = and i64 %arg1_maybeExpandToFullArgs, -9, !dbg !52 + %23 = icmp eq i64 %22, 0, !dbg !52 + %24 = or i1 %21, %23, !dbg !52 + br i1 %24, label %BB23.thread61, label %sorbet_isa_Array.exit, !dbg !52 sorbet_isa_Array.exit: ; preds = %argArrayExpandArrayTest - %25 = inttoptr i64 %arg1_maybeExpandToFullArgs to %struct.iseq_inline_iv_cache_entry*, !dbg !55 - %26 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %25, i64 0, i32 0, !dbg !55 - %27 = load i64, i64* %26, align 8, !dbg !55, !tbaa !25 - %28 = and i64 %27, 31, !dbg !55 - %29 = icmp eq i64 %28, 7, !dbg !55 - br i1 %29, label %argArrayExpand, label %BB23.thread62, !dbg !55 + %25 = inttoptr i64 %arg1_maybeExpandToFullArgs to %struct.iseq_inline_iv_cache_entry*, !dbg !52 + %26 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %25, i64 0, i32 0, !dbg !52 + %27 = load i64, i64* %26, align 8, !dbg !52, !tbaa !25 + %28 = and i64 %27, 31, !dbg !52 + %29 = icmp eq i64 %28, 7, !dbg !52 + br i1 %29, label %argArrayExpand, label %BB23.thread61, !dbg !52 argArrayExpand: ; preds = %sorbet_isa_Array.exit - %30 = inttoptr i64 %arg1_maybeExpandToFullArgs to %struct.iseq_inline_iv_cache_entry*, !dbg !55 - %31 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %30, i64 0, i32 0, !dbg !55 - %32 = load i64, i64* %31, align 8, !dbg !55, !tbaa !25 - %33 = and i64 %32, 33554432, !dbg !55 - %34 = icmp eq i64 %33, 0, !dbg !55 - br i1 %34, label %36, label %35, !dbg !55 + %30 = inttoptr i64 %arg1_maybeExpandToFullArgs to %struct.iseq_inline_iv_cache_entry*, !dbg !52 + %31 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %30, i64 0, i32 0, !dbg !52 + %32 = load i64, i64* %31, align 8, !dbg !52, !tbaa !25 + %33 = and i64 %32, 33554432, !dbg !52 + %34 = icmp eq i64 %33, 0, !dbg !52 + br i1 %34, label %36, label %35, !dbg !52 35: ; preds = %argArrayExpand - tail call void @rb_ary_detransient(i64 %arg1_maybeExpandToFullArgs) #13, !dbg !55 - br label %36, !dbg !55 + tail call void @rb_ary_detransient(i64 %arg1_maybeExpandToFullArgs) #13, !dbg !52 + br label %36, !dbg !52 36: ; preds = %35, %argArrayExpand - %37 = load i64, i64* %31, align 8, !dbg !55, !tbaa !25 - %38 = and i64 %37, 8192, !dbg !55 - %39 = icmp eq i64 %38, 0, !dbg !55 - %40 = inttoptr i64 %arg1_maybeExpandToFullArgs to %struct.RArray*, !dbg !55 - br i1 %39, label %45, label %41, !dbg !55 + %37 = load i64, i64* %31, align 8, !dbg !52, !tbaa !25 + %38 = and i64 %37, 8192, !dbg !52 + %39 = icmp eq i64 %38, 0, !dbg !52 + %40 = inttoptr i64 %arg1_maybeExpandToFullArgs to %struct.RArray*, !dbg !52 + br i1 %39, label %45, label %41, !dbg !52 41: ; preds = %36 - %42 = getelementptr inbounds %struct.RArray, %struct.RArray* %40, i64 0, i32 1, i32 0, i32 0, !dbg !55 - %43 = lshr i64 %37, 15, !dbg !55 - %44 = and i64 %43, 3, !dbg !55 - br label %rb_array_len.exit, !dbg !55 + %42 = getelementptr inbounds %struct.RArray, %struct.RArray* %40, i64 0, i32 1, i32 0, i32 0, !dbg !52 + %43 = lshr i64 %37, 15, !dbg !52 + %44 = and i64 %43, 3, !dbg !52 + br label %rb_array_len.exit, !dbg !52 45: ; preds = %36 - %46 = getelementptr inbounds %struct.RArray, %struct.RArray* %40, i64 0, i32 1, i32 0, i32 2, !dbg !55 - %47 = load i64*, i64** %46, align 8, !dbg !55, !tbaa !27 - %48 = getelementptr inbounds %struct.RArray, %struct.RArray* %40, i64 0, i32 1, i32 0, i32 0, !dbg !55 - %49 = load i64, i64* %48, align 8, !dbg !55, !tbaa !27 - br label %rb_array_len.exit, !dbg !55 + %46 = getelementptr inbounds %struct.RArray, %struct.RArray* %40, i64 0, i32 1, i32 0, i32 2, !dbg !52 + %47 = load i64*, i64** %46, align 8, !dbg !52, !tbaa !27 + %48 = getelementptr inbounds %struct.RArray, %struct.RArray* %40, i64 0, i32 1, i32 0, i32 0, !dbg !52 + %49 = load i64, i64* %48, align 8, !dbg !52, !tbaa !27 + br label %rb_array_len.exit, !dbg !52 rb_array_len.exit: ; preds = %41, %45 %50 = phi i64* [ %42, %41 ], [ %47, %45 ] - %51 = phi i64 [ %44, %41 ], [ %49, %45 ], !dbg !55 - %52 = trunc i64 %51 to i32, !dbg !55 - br label %fillRequiredArgs, !dbg !55 + %51 = phi i64 [ %44, %41 ], [ %49, %45 ], !dbg !52 + %52 = trunc i64 %51 to i32, !dbg !52 + br label %fillRequiredArgs, !dbg !52 fillFromArgBlock0: ; preds = %fillRequiredArgs - %rawArg_x = load i64, i64* %argArrayPhi, align 8, !dbg !55 - %default1 = icmp eq i32 %argcPhi, 1, !dbg !55 - br i1 %default1, label %BB23.thread62, label %BB24, !dbg !55, !prof !28 + %rawArg_x = load i64, i64* %argArrayPhi, align 8, !dbg !52 + %default1 = icmp eq i32 %argcPhi, 1, !dbg !52 + br i1 %default1, label %BB23.thread61, label %BB24, !dbg !52, !prof !28 fillRequiredArgs: ; preds = %functionEntryInitializers, %rb_array_len.exit - %argcPhi = phi i32 [ %argc, %functionEntryInitializers ], [ %52, %rb_array_len.exit ], !dbg !55 - %argArrayPhi = phi i64* [ %argArray, %functionEntryInitializers ], [ %50, %rb_array_len.exit ], !dbg !55 - %default0 = icmp eq i32 %argcPhi, 0, !dbg !55 - br i1 %default0, label %BB23.thread, label %fillFromArgBlock0, !dbg !55, !prof !28 + %argcPhi = phi i32 [ %argc, %functionEntryInitializers ], [ %52, %rb_array_len.exit ], !dbg !52 + %argArrayPhi = phi i64* [ %argArray, %functionEntryInitializers ], [ %50, %rb_array_len.exit ], !dbg !52 + %default0 = icmp eq i32 %argcPhi, 0, !dbg !52 + br i1 %default0, label %BB23.thread, label %fillFromArgBlock0, !dbg !52, !prof !28 } ; Function Attrs: ssp -define internal i64 @"func_.13$block_5"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture readonly %argArray, i64 %blockArg) #8 !dbg !63 { +define internal i64 @"func_.13$block_5"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture readonly %argArray, i64 %blockArg) #8 !dbg !59 { functionEntryInitializers: %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !17 %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !42 + %4 = load i64, i64* %3, align 8, !tbaa !41 %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_5", align 8 %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %5, align 8, !tbaa !21 @@ -626,111 +622,110 @@ functionEntryInitializers: store i64 %9, i64* %7, align 8, !tbaa !6 %10 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %10, align 8, !tbaa !15 - %arrayExpansionSizeGuard = icmp eq i32 %argc, 1, !dbg !64 - br i1 %arrayExpansionSizeGuard, label %argArrayExpandArrayTest, label %fillRequiredArgs, !dbg !64 + %arrayExpansionSizeGuard = icmp eq i32 %argc, 1, !dbg !60 + br i1 %arrayExpansionSizeGuard, label %argArrayExpandArrayTest, label %fillRequiredArgs, !dbg !60 BB31: ; preds = %fillFromArgBlock0 - %11 = getelementptr i64, i64* %argArrayPhi, i32 1, !dbg !64 - %rawArg_y = load i64, i64* %11, align 8, !dbg !64 + %11 = getelementptr i64, i64* %argArrayPhi, i32 1, !dbg !60 + %rawArg_y = load i64, i64* %11, align 8, !dbg !60 store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 28), i64** %10, align 8, !tbaa !15 - br label %BB33, !dbg !65 + br label %BB33, !dbg !61 BB32: ; preds = %argArrayExpandArrayTest, %sorbet_isa_Array.exit, %fillRequiredArgs, %fillFromArgBlock0 %x.sroa.0.1.ph = phi i64 [ 8, %fillRequiredArgs ], [ %rawArg_x, %fillFromArgBlock0 ], [ %arg1_maybeExpandToFullArgs, %sorbet_isa_Array.exit ], [ %arg1_maybeExpandToFullArgs, %argArrayExpandArrayTest ] store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 28), i64** %10, align 8, !tbaa !15 - %rubyId_something = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 9), align 8, !dbg !66, !invariant.load !5 - %rawSym = tail call i64 @rb_id2sym(i64 %rubyId_something) #16, !dbg !66 - br label %BB33, !dbg !66 + %rubyId_something = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 9), align 8, !dbg !62, !invariant.load !5 + %rawSym = tail call i64 @rb_id2sym(i64 %rubyId_something) #16, !dbg !62 + br label %BB33, !dbg !62 BB33: ; preds = %BB32, %BB31 - %x.sroa.0.141 = phi i64 [ %rawArg_x, %BB31 ], [ %x.sroa.0.1.ph, %BB32 ] - %y.sroa.0.0 = phi i64 [ %rawArg_y, %BB31 ], [ %rawSym, %BB32 ], !dbg !67 + %x.sroa.0.140 = phi i64 [ %rawArg_x, %BB31 ], [ %x.sroa.0.1.ph, %BB32 ] + %y.sroa.0.0 = phi i64 [ %rawArg_y, %BB31 ], [ %rawSym, %BB32 ], !dbg !63 store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 29), i64** %10, align 8, !tbaa !15 - %12 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !68 - %13 = load i64*, i64** %12, align 8, !dbg !68 - store i64 %4, i64* %13, align 8, !dbg !68, !tbaa !6 - %14 = getelementptr inbounds i64, i64* %13, i64 1, !dbg !68 - store i64 %x.sroa.0.141, i64* %14, align 8, !dbg !68, !tbaa !6 - %15 = getelementptr inbounds i64, i64* %14, i64 1, !dbg !68 - store i64* %15, i64** %12, align 8, !dbg !68 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.12, i64 0), !dbg !68 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 30), i64** %10, align 8, !dbg !68, !tbaa !15 - %16 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !69 - %17 = load i64*, i64** %16, align 8, !dbg !69 - store i64 %4, i64* %17, align 8, !dbg !69, !tbaa !6 - %18 = getelementptr inbounds i64, i64* %17, i64 1, !dbg !69 - store i64 %y.sroa.0.0, i64* %18, align 8, !dbg !69, !tbaa !6 - %19 = getelementptr inbounds i64, i64* %18, i64 1, !dbg !69 - store i64* %19, i64** %16, align 8, !dbg !69 - %send44 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.13, i64 0), !dbg !69 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 28), i64** %10, align 8, !dbg !69, !tbaa !15 - ret i64 %send44, !dbg !70 + %12 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !64 + %13 = load i64*, i64** %12, align 8, !dbg !64 + store i64 %4, i64* %13, align 8, !dbg !64, !tbaa !6 + %14 = getelementptr inbounds i64, i64* %13, i64 1, !dbg !64 + store i64 %x.sroa.0.140, i64* %14, align 8, !dbg !64, !tbaa !6 + %15 = getelementptr inbounds i64, i64* %14, i64 1, !dbg !64 + store i64* %15, i64** %12, align 8, !dbg !64 + %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.12, i64 0), !dbg !64 + store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 30), i64** %10, align 8, !dbg !64, !tbaa !15 + %16 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !65 + %17 = load i64*, i64** %16, align 8, !dbg !65 + store i64 %4, i64* %17, align 8, !dbg !65, !tbaa !6 + %18 = getelementptr inbounds i64, i64* %17, i64 1, !dbg !65 + store i64 %y.sroa.0.0, i64* %18, align 8, !dbg !65, !tbaa !6 + %19 = getelementptr inbounds i64, i64* %18, i64 1, !dbg !65 + store i64* %19, i64** %16, align 8, !dbg !65 + %send43 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.13, i64 0), !dbg !65 + ret i64 %send43, !dbg !65 argArrayExpandArrayTest: ; preds = %functionEntryInitializers - %arg1_maybeExpandToFullArgs = load i64, i64* %argArray, align 8, !dbg !64 - %20 = and i64 %arg1_maybeExpandToFullArgs, 7, !dbg !64 - %21 = icmp ne i64 %20, 0, !dbg !64 - %22 = and i64 %arg1_maybeExpandToFullArgs, -9, !dbg !64 - %23 = icmp eq i64 %22, 0, !dbg !64 - %24 = or i1 %21, %23, !dbg !64 - br i1 %24, label %BB32, label %sorbet_isa_Array.exit, !dbg !64 + %arg1_maybeExpandToFullArgs = load i64, i64* %argArray, align 8, !dbg !60 + %20 = and i64 %arg1_maybeExpandToFullArgs, 7, !dbg !60 + %21 = icmp ne i64 %20, 0, !dbg !60 + %22 = and i64 %arg1_maybeExpandToFullArgs, -9, !dbg !60 + %23 = icmp eq i64 %22, 0, !dbg !60 + %24 = or i1 %21, %23, !dbg !60 + br i1 %24, label %BB32, label %sorbet_isa_Array.exit, !dbg !60 sorbet_isa_Array.exit: ; preds = %argArrayExpandArrayTest - %25 = inttoptr i64 %arg1_maybeExpandToFullArgs to %struct.iseq_inline_iv_cache_entry*, !dbg !64 - %26 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %25, i64 0, i32 0, !dbg !64 - %27 = load i64, i64* %26, align 8, !dbg !64, !tbaa !25 - %28 = and i64 %27, 31, !dbg !64 - %29 = icmp eq i64 %28, 7, !dbg !64 - br i1 %29, label %argArrayExpand, label %BB32, !dbg !64 + %25 = inttoptr i64 %arg1_maybeExpandToFullArgs to %struct.iseq_inline_iv_cache_entry*, !dbg !60 + %26 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %25, i64 0, i32 0, !dbg !60 + %27 = load i64, i64* %26, align 8, !dbg !60, !tbaa !25 + %28 = and i64 %27, 31, !dbg !60 + %29 = icmp eq i64 %28, 7, !dbg !60 + br i1 %29, label %argArrayExpand, label %BB32, !dbg !60 argArrayExpand: ; preds = %sorbet_isa_Array.exit - %30 = inttoptr i64 %arg1_maybeExpandToFullArgs to %struct.iseq_inline_iv_cache_entry*, !dbg !64 - %31 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %30, i64 0, i32 0, !dbg !64 - %32 = load i64, i64* %31, align 8, !dbg !64, !tbaa !25 - %33 = and i64 %32, 33554432, !dbg !64 - %34 = icmp eq i64 %33, 0, !dbg !64 - br i1 %34, label %36, label %35, !dbg !64 + %30 = inttoptr i64 %arg1_maybeExpandToFullArgs to %struct.iseq_inline_iv_cache_entry*, !dbg !60 + %31 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %30, i64 0, i32 0, !dbg !60 + %32 = load i64, i64* %31, align 8, !dbg !60, !tbaa !25 + %33 = and i64 %32, 33554432, !dbg !60 + %34 = icmp eq i64 %33, 0, !dbg !60 + br i1 %34, label %36, label %35, !dbg !60 35: ; preds = %argArrayExpand - tail call void @rb_ary_detransient(i64 %arg1_maybeExpandToFullArgs) #13, !dbg !64 - br label %36, !dbg !64 + tail call void @rb_ary_detransient(i64 %arg1_maybeExpandToFullArgs) #13, !dbg !60 + br label %36, !dbg !60 36: ; preds = %35, %argArrayExpand - %37 = load i64, i64* %31, align 8, !dbg !64, !tbaa !25 - %38 = and i64 %37, 8192, !dbg !64 - %39 = icmp eq i64 %38, 0, !dbg !64 - %40 = inttoptr i64 %arg1_maybeExpandToFullArgs to %struct.RArray*, !dbg !64 - br i1 %39, label %45, label %41, !dbg !64 + %37 = load i64, i64* %31, align 8, !dbg !60, !tbaa !25 + %38 = and i64 %37, 8192, !dbg !60 + %39 = icmp eq i64 %38, 0, !dbg !60 + %40 = inttoptr i64 %arg1_maybeExpandToFullArgs to %struct.RArray*, !dbg !60 + br i1 %39, label %45, label %41, !dbg !60 41: ; preds = %36 - %42 = getelementptr inbounds %struct.RArray, %struct.RArray* %40, i64 0, i32 1, i32 0, i32 0, !dbg !64 - %43 = lshr i64 %37, 15, !dbg !64 - %44 = and i64 %43, 3, !dbg !64 - br label %rb_array_len.exit, !dbg !64 + %42 = getelementptr inbounds %struct.RArray, %struct.RArray* %40, i64 0, i32 1, i32 0, i32 0, !dbg !60 + %43 = lshr i64 %37, 15, !dbg !60 + %44 = and i64 %43, 3, !dbg !60 + br label %rb_array_len.exit, !dbg !60 45: ; preds = %36 - %46 = getelementptr inbounds %struct.RArray, %struct.RArray* %40, i64 0, i32 1, i32 0, i32 2, !dbg !64 - %47 = load i64*, i64** %46, align 8, !dbg !64, !tbaa !27 - %48 = getelementptr inbounds %struct.RArray, %struct.RArray* %40, i64 0, i32 1, i32 0, i32 0, !dbg !64 - %49 = load i64, i64* %48, align 8, !dbg !64, !tbaa !27 - br label %rb_array_len.exit, !dbg !64 + %46 = getelementptr inbounds %struct.RArray, %struct.RArray* %40, i64 0, i32 1, i32 0, i32 2, !dbg !60 + %47 = load i64*, i64** %46, align 8, !dbg !60, !tbaa !27 + %48 = getelementptr inbounds %struct.RArray, %struct.RArray* %40, i64 0, i32 1, i32 0, i32 0, !dbg !60 + %49 = load i64, i64* %48, align 8, !dbg !60, !tbaa !27 + br label %rb_array_len.exit, !dbg !60 rb_array_len.exit: ; preds = %41, %45 %50 = phi i64* [ %42, %41 ], [ %47, %45 ] - %51 = phi i64 [ %44, %41 ], [ %49, %45 ], !dbg !64 - %52 = trunc i64 %51 to i32, !dbg !64 - br label %fillRequiredArgs, !dbg !64 + %51 = phi i64 [ %44, %41 ], [ %49, %45 ], !dbg !60 + %52 = trunc i64 %51 to i32, !dbg !60 + br label %fillRequiredArgs, !dbg !60 fillFromArgBlock0: ; preds = %fillRequiredArgs - %rawArg_x = load i64, i64* %argArrayPhi, align 8, !dbg !64 - %default1 = icmp eq i32 %argcPhi, 1, !dbg !64 - br i1 %default1, label %BB32, label %BB31, !dbg !64, !prof !28 + %rawArg_x = load i64, i64* %argArrayPhi, align 8, !dbg !60 + %default1 = icmp eq i32 %argcPhi, 1, !dbg !60 + br i1 %default1, label %BB32, label %BB31, !dbg !60, !prof !28 fillRequiredArgs: ; preds = %functionEntryInitializers, %rb_array_len.exit - %argcPhi = phi i32 [ %argc, %functionEntryInitializers ], [ %52, %rb_array_len.exit ], !dbg !64 - %argArrayPhi = phi i64* [ %argArray, %functionEntryInitializers ], [ %50, %rb_array_len.exit ], !dbg !64 - %default0 = icmp eq i32 %argcPhi, 0, !dbg !64 - br i1 %default0, label %BB32, label %fillFromArgBlock0, !dbg !64, !prof !28 + %argcPhi = phi i32 [ %argc, %functionEntryInitializers ], [ %52, %rb_array_len.exit ], !dbg !60 + %argArrayPhi = phi i64* [ %argArray, %functionEntryInitializers ], [ %50, %rb_array_len.exit ], !dbg !60 + %default0 = icmp eq i32 %argcPhi, 0, !dbg !60 + br i1 %default0, label %BB32, label %fillFromArgBlock0, !dbg !60, !prof !28 } ; Function Attrs: sspreq @@ -786,25 +781,25 @@ entry: %19 = ptrtoint %struct.vm_ifunc* %18 to i64 %20 = call i64 @sorbet_globalConstRegister(i64 %19) store i64 %20, i64* @"func_.13$block_2_ifunc", align 8 - %rubyId_p.i = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 6), align 8, !dbg !45, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p, i64 %rubyId_p.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !45 + %rubyId_p.i = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 6), align 8, !dbg !44, !invariant.load !5 + call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p, i64 %rubyId_p.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !44 %21 = call %struct.vm_ifunc* @rb_vm_ifunc_new(i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_3", i8* noundef null, i32 noundef 0, i32 noundef 1) #13 %22 = ptrtoint %struct.vm_ifunc* %21 to i64 %23 = call i64 @sorbet_globalConstRegister(i64 %22) store i64 %23, i64* @"func_.13$block_3_ifunc", align 8 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.5, i64 %rubyId_p.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !52 + call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.5, i64 %rubyId_p.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !50 %24 = call %struct.vm_ifunc* @rb_vm_ifunc_new(i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_4", i8* noundef null, i32 noundef 0, i32 noundef 2) #13 %25 = ptrtoint %struct.vm_ifunc* %24 to i64 %26 = call i64 @sorbet_globalConstRegister(i64 %25) store i64 %26, i64* @"func_.13$block_4_ifunc", align 8 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.8, i64 %rubyId_p.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !60 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.9, i64 %rubyId_p.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !61 + call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.8, i64 %rubyId_p.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !57 + call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.9, i64 %rubyId_p.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !58 %27 = call %struct.vm_ifunc* @rb_vm_ifunc_new(i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_5", i8* noundef null, i32 noundef 1, i32 noundef 2) #13 %28 = ptrtoint %struct.vm_ifunc* %27 to i64 %29 = call i64 @sorbet_globalConstRegister(i64 %28) store i64 %29, i64* @"func_.13$block_5_ifunc", align 8 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.12, i64 %rubyId_p.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !68 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.13, i64 %rubyId_p.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !69 + call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.12, i64 %rubyId_p.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !64 + call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.13, i64 %rubyId_p.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !65 %30 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 %31 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %30, i64 0, i32 2 %32 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %31, align 8, !tbaa !17 @@ -821,641 +816,639 @@ entry: store i64 %39, i64* %37, align 8, !tbaa !6 call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %30, %struct.rb_control_frame_struct* %34, %struct.rb_iseq_struct* %stackFrame.i) #13 %40 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %32, i64 0, i32 0 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %40, align 8, !dbg !71, !tbaa !15 - %callArgs0Addr.i = getelementptr [3 x i64], [3 x i64]* %callArgs.i, i64 0, i64 0, !dbg !72 - %41 = bitcast i64* %callArgs0Addr.i to <2 x i64>*, !dbg !72 - store <2 x i64> , <2 x i64>* %41, align 8, !dbg !72 - call void @llvm.experimental.noalias.scope.decl(metadata !73) #13, !dbg !72 - %42 = call i64 @rb_ary_new_from_values(i64 noundef 2, i64* noundef nonnull align 8 %callArgs0Addr.i) #13, !dbg !72 - store i64 %42, i64* %callArgs0Addr.i, align 8, !dbg !76 - call void @llvm.experimental.noalias.scope.decl(metadata !77) #13, !dbg !76 - %43 = call i64 @rb_ary_new_from_values(i64 noundef 1, i64* noundef nonnull %callArgs0Addr.i) #13, !dbg !76 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %40, align 8, !dbg !76, !tbaa !15 - %rubyId_each.i = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !dbg !80, !invariant.load !5 - %44 = load i64, i64* @"func_.13$block_1_ifunc", align 8, !dbg !80 - %45 = call %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64 %44) #13, !dbg !80 - %46 = bitcast %struct.sorbet_inlineIntrinsicEnv* %6 to i8*, !dbg !80 - call void @llvm.lifetime.start.p0i8(i64 noundef 40, i8* noundef nonnull %46) #13, !dbg !80 - %47 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %6, i64 0, i32 0, !dbg !80 - store i64 %43, i64* %47, align 8, !dbg !80, !tbaa !81 - %48 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %6, i64 0, i32 1, !dbg !80 - store i64 %rubyId_each.i, i64* %48, align 8, !dbg !80, !tbaa !83 - %49 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %6, i64 0, i32 2, !dbg !80 - store i32 0, i32* %49, align 8, !dbg !80, !tbaa !84 - %50 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %6, i64 0, i32 3, !dbg !80 - %51 = bitcast i64** %50 to i8*, !dbg !80 - call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %51, i8 0, i64 16, i1 false) #13, !dbg !80 - %52 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !80, !tbaa !15 - %53 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %52, i64 0, i32 2, !dbg !80 - %54 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %53, align 8, !dbg !80, !tbaa !17 - %55 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %54, i64 0, i32 3, !dbg !80 - %56 = bitcast i64* %55 to %struct.rb_captured_block*, !dbg !80 - %57 = getelementptr inbounds i64, i64* %55, i64 2, !dbg !80 - %58 = bitcast i64* %57 to %struct.vm_ifunc**, !dbg !80 - store %struct.vm_ifunc* %45, %struct.vm_ifunc** %58, align 8, !dbg !80, !tbaa !27 - call void @llvm.experimental.noalias.scope.decl(metadata !85) #13, !dbg !80 - %59 = ptrtoint %struct.rb_captured_block* %56 to i64, !dbg !80 - %60 = or i64 %59, 3, !dbg !80 - %61 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %52, i64 0, i32 17, !dbg !80 - %62 = and i64 %60, -4, !dbg !88 - %63 = inttoptr i64 %62 to %struct.rb_captured_block*, !dbg !88 - store i64 0, i64* %61, align 8, !dbg !88, !tbaa !90 - call void @sorbet_pushBlockFrame(%struct.rb_captured_block* %63) #13, !dbg !88 - %64 = inttoptr i64 %43 to %struct.iseq_inline_iv_cache_entry*, !dbg !88 - %65 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %64, i64 0, i32 0, !dbg !88 - %66 = load i64, i64* %65, align 8, !dbg !88, !tbaa !25 - %67 = and i64 %66, 8192, !dbg !88 - %68 = icmp eq i64 %67, 0, !dbg !88 - br i1 %68, label %72, label %69, !dbg !88 + store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %40, align 8, !dbg !66, !tbaa !15 + %callArgs0Addr.i = getelementptr [3 x i64], [3 x i64]* %callArgs.i, i64 0, i64 0, !dbg !67 + %41 = bitcast i64* %callArgs0Addr.i to <2 x i64>*, !dbg !67 + store <2 x i64> , <2 x i64>* %41, align 8, !dbg !67 + call void @llvm.experimental.noalias.scope.decl(metadata !68) #13, !dbg !67 + %42 = call i64 @rb_ary_new_from_values(i64 noundef 2, i64* noundef nonnull align 8 %callArgs0Addr.i) #13, !dbg !67 + store i64 %42, i64* %callArgs0Addr.i, align 8, !dbg !71 + call void @llvm.experimental.noalias.scope.decl(metadata !72) #13, !dbg !71 + %43 = call i64 @rb_ary_new_from_values(i64 noundef 1, i64* noundef nonnull %callArgs0Addr.i) #13, !dbg !71 + store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %40, align 8, !dbg !71, !tbaa !15 + %rubyId_each.i = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !dbg !75, !invariant.load !5 + %44 = load i64, i64* @"func_.13$block_1_ifunc", align 8, !dbg !75 + %45 = call %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64 %44) #13, !dbg !75 + %46 = bitcast %struct.sorbet_inlineIntrinsicEnv* %6 to i8*, !dbg !75 + call void @llvm.lifetime.start.p0i8(i64 noundef 40, i8* noundef nonnull %46) #13, !dbg !75 + %47 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %6, i64 0, i32 0, !dbg !75 + store i64 %43, i64* %47, align 8, !dbg !75, !tbaa !76 + %48 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %6, i64 0, i32 1, !dbg !75 + store i64 %rubyId_each.i, i64* %48, align 8, !dbg !75, !tbaa !78 + %49 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %6, i64 0, i32 2, !dbg !75 + store i32 0, i32* %49, align 8, !dbg !75, !tbaa !79 + %50 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %6, i64 0, i32 3, !dbg !75 + %51 = bitcast i64** %50 to i8*, !dbg !75 + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %51, i8 0, i64 16, i1 false) #13, !dbg !75 + %52 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !75, !tbaa !15 + %53 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %52, i64 0, i32 2, !dbg !75 + %54 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %53, align 8, !dbg !75, !tbaa !17 + %55 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %54, i64 0, i32 3, !dbg !75 + %56 = bitcast i64* %55 to %struct.rb_captured_block*, !dbg !75 + %57 = getelementptr inbounds i64, i64* %55, i64 2, !dbg !75 + %58 = bitcast i64* %57 to %struct.vm_ifunc**, !dbg !75 + store %struct.vm_ifunc* %45, %struct.vm_ifunc** %58, align 8, !dbg !75, !tbaa !27 + call void @llvm.experimental.noalias.scope.decl(metadata !80) #13, !dbg !75 + %59 = ptrtoint %struct.rb_captured_block* %56 to i64, !dbg !75 + %60 = or i64 %59, 3, !dbg !75 + %61 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %52, i64 0, i32 17, !dbg !75 + %62 = and i64 %60, -4, !dbg !83 + %63 = inttoptr i64 %62 to %struct.rb_captured_block*, !dbg !83 + store i64 0, i64* %61, align 8, !dbg !83, !tbaa !85 + call void @sorbet_pushBlockFrame(%struct.rb_captured_block* %63) #13, !dbg !83 + %64 = inttoptr i64 %43 to %struct.iseq_inline_iv_cache_entry*, !dbg !83 + %65 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %64, i64 0, i32 0, !dbg !83 + %66 = load i64, i64* %65, align 8, !dbg !83, !tbaa !25 + %67 = and i64 %66, 8192, !dbg !83 + %68 = icmp eq i64 %67, 0, !dbg !83 + br i1 %68, label %72, label %69, !dbg !83 69: ; preds = %entry - %70 = lshr i64 %66, 15, !dbg !88 - %71 = and i64 %70, 3, !dbg !88 - br label %rb_array_len.exit1.i3.i, !dbg !88 + %70 = lshr i64 %66, 15, !dbg !83 + %71 = and i64 %70, 3, !dbg !83 + br label %rb_array_len.exit1.i2.i, !dbg !83 72: ; preds = %entry - %73 = inttoptr i64 %43 to %struct.RArray*, !dbg !88 - %74 = getelementptr inbounds %struct.RArray, %struct.RArray* %73, i64 0, i32 1, i32 0, i32 0, !dbg !88 - %75 = load i64, i64* %74, align 8, !dbg !88, !tbaa !27 - br label %rb_array_len.exit1.i3.i, !dbg !88 - -rb_array_len.exit1.i3.i: ; preds = %72, %69 - %76 = phi i64 [ %71, %69 ], [ %75, %72 ], !dbg !88 - %77 = icmp sgt i64 %76, 0, !dbg !88 - br i1 %77, label %78, label %forward_sorbet_rb_array_each_withBlock.exit.i, !dbg !88 - -78: ; preds = %rb_array_len.exit1.i3.i - %79 = bitcast i64* %1 to i8*, !dbg !88 - %80 = inttoptr i64 %43 to %struct.RArray*, !dbg !80 - %81 = getelementptr inbounds %struct.RArray, %struct.RArray* %80, i64 0, i32 1, i32 0, i32 0, !dbg !80 - %82 = getelementptr inbounds %struct.RArray, %struct.RArray* %80, i64 0, i32 1, i32 0, i32 2, !dbg !80 - br label %83, !dbg !88 - -83: ; preds = %rb_array_len.exit.i5.i, %78 - %84 = phi i64 [ 0, %78 ], [ %94, %rb_array_len.exit.i5.i ], !dbg !88 - call void @llvm.lifetime.start.p0i8(i64 noundef 8, i8* noundef nonnull align 8 dereferenceable(8) %79) #13, !dbg !88 - %85 = load i64, i64* %65, align 8, !dbg !88, !tbaa !25 - %86 = and i64 %85, 8192, !dbg !88 - %87 = icmp eq i64 %86, 0, !dbg !88 - br i1 %87, label %88, label %rb_array_const_ptr_transient.exit.i4.i, !dbg !88 + %73 = inttoptr i64 %43 to %struct.RArray*, !dbg !83 + %74 = getelementptr inbounds %struct.RArray, %struct.RArray* %73, i64 0, i32 1, i32 0, i32 0, !dbg !83 + %75 = load i64, i64* %74, align 8, !dbg !83, !tbaa !27 + br label %rb_array_len.exit1.i2.i, !dbg !83 + +rb_array_len.exit1.i2.i: ; preds = %72, %69 + %76 = phi i64 [ %71, %69 ], [ %75, %72 ], !dbg !83 + %77 = icmp sgt i64 %76, 0, !dbg !83 + br i1 %77, label %78, label %forward_sorbet_rb_array_each_withBlock.exit.i, !dbg !83 + +78: ; preds = %rb_array_len.exit1.i2.i + %79 = bitcast i64* %1 to i8*, !dbg !83 + %80 = inttoptr i64 %43 to %struct.RArray*, !dbg !75 + %81 = getelementptr inbounds %struct.RArray, %struct.RArray* %80, i64 0, i32 1, i32 0, i32 0, !dbg !75 + %82 = getelementptr inbounds %struct.RArray, %struct.RArray* %80, i64 0, i32 1, i32 0, i32 2, !dbg !75 + br label %83, !dbg !83 + +83: ; preds = %rb_array_len.exit.i4.i, %78 + %84 = phi i64 [ 0, %78 ], [ %94, %rb_array_len.exit.i4.i ], !dbg !83 + call void @llvm.lifetime.start.p0i8(i64 noundef 8, i8* noundef nonnull align 8 dereferenceable(8) %79) #13, !dbg !83 + %85 = load i64, i64* %65, align 8, !dbg !83, !tbaa !25 + %86 = and i64 %85, 8192, !dbg !83 + %87 = icmp eq i64 %86, 0, !dbg !83 + br i1 %87, label %88, label %rb_array_const_ptr_transient.exit.i3.i, !dbg !83 88: ; preds = %83 - %89 = load i64*, i64** %82, align 8, !dbg !88, !tbaa !27 - br label %rb_array_const_ptr_transient.exit.i4.i, !dbg !88 - -rb_array_const_ptr_transient.exit.i4.i: ; preds = %88, %83 - %90 = phi i64* [ %89, %88 ], [ %81, %83 ], !dbg !88 - %91 = getelementptr inbounds i64, i64* %90, i64 %84, !dbg !88 - %92 = load i64, i64* %91, align 8, !dbg !88, !tbaa !6 - store i64 %92, i64* %1, align 8, !dbg !88, !tbaa !6 - %93 = call i64 @"func_.13$block_1"(i64 undef, i64 undef, i32 noundef 1, i64* noalias nocapture noundef nonnull readonly align 8 dereferenceable(8) %1, i64 undef) #13, !dbg !88 - call void @llvm.lifetime.end.p0i8(i64 noundef 8, i8* noundef nonnull %79) #13, !dbg !88 - %94 = add nuw nsw i64 %84, 1, !dbg !88 - %95 = load i64, i64* %65, align 8, !dbg !88, !tbaa !25 - %96 = and i64 %95, 8192, !dbg !88 - %97 = icmp eq i64 %96, 0, !dbg !88 - br i1 %97, label %101, label %98, !dbg !88 - -98: ; preds = %rb_array_const_ptr_transient.exit.i4.i - %99 = lshr i64 %95, 15, !dbg !88 - %100 = and i64 %99, 3, !dbg !88 - br label %rb_array_len.exit.i5.i, !dbg !88 - -101: ; preds = %rb_array_const_ptr_transient.exit.i4.i - %102 = load i64, i64* %81, align 8, !dbg !88, !tbaa !27 - br label %rb_array_len.exit.i5.i, !dbg !88 - -rb_array_len.exit.i5.i: ; preds = %101, %98 - %103 = phi i64 [ %100, %98 ], [ %102, %101 ], !dbg !88 - %104 = icmp sgt i64 %103, %94, !dbg !88 - br i1 %104, label %83, label %forward_sorbet_rb_array_each_withBlock.exit.i, !dbg !88, !llvm.loop !91 - -forward_sorbet_rb_array_each_withBlock.exit.i: ; preds = %rb_array_len.exit.i5.i, %rb_array_len.exit1.i3.i - call void @sorbet_popFrame() #13, !dbg !88 - call void @llvm.lifetime.end.p0i8(i64 noundef 40, i8* noundef nonnull %46) #13, !dbg !80 - %105 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !80, !tbaa !15 - %106 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %105, i64 0, i32 5, !dbg !80 - %107 = load i32, i32* %106, align 8, !dbg !80, !tbaa !37 - %108 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %105, i64 0, i32 6, !dbg !80 - %109 = load i32, i32* %108, align 4, !dbg !80, !tbaa !38 - %110 = xor i32 %109, -1, !dbg !80 - %111 = and i32 %110, %107, !dbg !80 - %112 = icmp eq i32 %111, 0, !dbg !80 - br i1 %112, label %rb_check_arity.1.exit.i8.i, label %113, !dbg !80, !prof !31 + %89 = load i64*, i64** %82, align 8, !dbg !83, !tbaa !27 + br label %rb_array_const_ptr_transient.exit.i3.i, !dbg !83 + +rb_array_const_ptr_transient.exit.i3.i: ; preds = %88, %83 + %90 = phi i64* [ %89, %88 ], [ %81, %83 ], !dbg !83 + %91 = getelementptr inbounds i64, i64* %90, i64 %84, !dbg !83 + %92 = load i64, i64* %91, align 8, !dbg !83, !tbaa !6 + store i64 %92, i64* %1, align 8, !dbg !83, !tbaa !6 + %93 = call i64 @"func_.13$block_1"(i64 undef, i64 undef, i32 noundef 1, i64* noalias nocapture noundef nonnull readonly align 8 dereferenceable(8) %1, i64 undef) #13, !dbg !83 + call void @llvm.lifetime.end.p0i8(i64 noundef 8, i8* noundef nonnull %79) #13, !dbg !83 + %94 = add nuw nsw i64 %84, 1, !dbg !83 + %95 = load i64, i64* %65, align 8, !dbg !83, !tbaa !25 + %96 = and i64 %95, 8192, !dbg !83 + %97 = icmp eq i64 %96, 0, !dbg !83 + br i1 %97, label %101, label %98, !dbg !83 + +98: ; preds = %rb_array_const_ptr_transient.exit.i3.i + %99 = lshr i64 %95, 15, !dbg !83 + %100 = and i64 %99, 3, !dbg !83 + br label %rb_array_len.exit.i4.i, !dbg !83 + +101: ; preds = %rb_array_const_ptr_transient.exit.i3.i + %102 = load i64, i64* %81, align 8, !dbg !83, !tbaa !27 + br label %rb_array_len.exit.i4.i, !dbg !83 + +rb_array_len.exit.i4.i: ; preds = %101, %98 + %103 = phi i64 [ %100, %98 ], [ %102, %101 ], !dbg !83 + %104 = icmp sgt i64 %103, %94, !dbg !83 + br i1 %104, label %83, label %forward_sorbet_rb_array_each_withBlock.exit.i, !dbg !83, !llvm.loop !86 + +forward_sorbet_rb_array_each_withBlock.exit.i: ; preds = %rb_array_len.exit.i4.i, %rb_array_len.exit1.i2.i + call void @sorbet_popFrame() #13, !dbg !83 + call void @llvm.lifetime.end.p0i8(i64 noundef 40, i8* noundef nonnull %46) #13, !dbg !75 + %105 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !75, !tbaa !15 + %106 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %105, i64 0, i32 5, !dbg !75 + %107 = load i32, i32* %106, align 8, !dbg !75, !tbaa !37 + %108 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %105, i64 0, i32 6, !dbg !75 + %109 = load i32, i32* %108, align 4, !dbg !75, !tbaa !38 + %110 = xor i32 %109, -1, !dbg !75 + %111 = and i32 %110, %107, !dbg !75 + %112 = icmp eq i32 %111, 0, !dbg !75 + br i1 %112, label %rb_check_arity.1.exit.i6.i, label %113, !dbg !75, !prof !31 113: ; preds = %forward_sorbet_rb_array_each_withBlock.exit.i - %114 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %105, i64 0, i32 8, !dbg !80 - %115 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %114, align 8, !dbg !80, !tbaa !39 - %116 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %115, i32 noundef 0) #13, !dbg !80 - br label %rb_check_arity.1.exit.i8.i, !dbg !80 - -rb_check_arity.1.exit.i8.i: ; preds = %113, %forward_sorbet_rb_array_each_withBlock.exit.i - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %40, align 8, !dbg !80, !tbaa !15 - %117 = load i64, i64* @"func_.13$block_2_ifunc", align 8, !dbg !93 - %118 = call %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64 %117) #13, !dbg !93 - %119 = bitcast %struct.sorbet_inlineIntrinsicEnv* %5 to i8*, !dbg !93 - call void @llvm.lifetime.start.p0i8(i64 noundef 40, i8* noundef nonnull %119) #13, !dbg !93 - %120 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %5, i64 0, i32 0, !dbg !93 - store i64 %43, i64* %120, align 8, !dbg !93, !tbaa !81 - %121 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %5, i64 0, i32 1, !dbg !93 - store i64 %rubyId_each.i, i64* %121, align 8, !dbg !93, !tbaa !83 - %122 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %5, i64 0, i32 2, !dbg !93 - store i32 0, i32* %122, align 8, !dbg !93, !tbaa !84 - %123 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %5, i64 0, i32 3, !dbg !93 - %124 = bitcast i64** %123 to i8*, !dbg !93 - call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %124, i8 0, i64 16, i1 false) #13, !dbg !93 - %125 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !93, !tbaa !15 - %126 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %125, i64 0, i32 2, !dbg !93 - %127 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %126, align 8, !dbg !93, !tbaa !17 - %128 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %127, i64 0, i32 3, !dbg !93 - %129 = bitcast i64* %128 to %struct.rb_captured_block*, !dbg !93 - %130 = getelementptr inbounds i64, i64* %128, i64 2, !dbg !93 - %131 = bitcast i64* %130 to %struct.vm_ifunc**, !dbg !93 - store %struct.vm_ifunc* %118, %struct.vm_ifunc** %131, align 8, !dbg !93, !tbaa !27 - call void @llvm.experimental.noalias.scope.decl(metadata !94) #13, !dbg !93 - %132 = ptrtoint %struct.rb_captured_block* %129 to i64, !dbg !93 - %133 = or i64 %132, 3, !dbg !93 - %134 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %125, i64 0, i32 17, !dbg !93 - %135 = and i64 %133, -4, !dbg !97 - %136 = inttoptr i64 %135 to %struct.rb_captured_block*, !dbg !97 - store i64 0, i64* %134, align 8, !dbg !97, !tbaa !90 - call void @sorbet_pushBlockFrame(%struct.rb_captured_block* %136) #13, !dbg !97 - %137 = load i64, i64* %65, align 8, !dbg !97, !tbaa !25 - %138 = and i64 %137, 8192, !dbg !97 - %139 = icmp eq i64 %138, 0, !dbg !97 - br i1 %139, label %143, label %140, !dbg !97 - -140: ; preds = %rb_check_arity.1.exit.i8.i - %141 = lshr i64 %137, 15, !dbg !97 - %142 = and i64 %141, 3, !dbg !97 - br label %rb_array_len.exit1.i9.i, !dbg !97 - -143: ; preds = %rb_check_arity.1.exit.i8.i - %144 = inttoptr i64 %43 to %struct.RArray*, !dbg !97 - %145 = getelementptr inbounds %struct.RArray, %struct.RArray* %144, i64 0, i32 1, i32 0, i32 0, !dbg !97 - %146 = load i64, i64* %145, align 8, !dbg !97, !tbaa !27 - br label %rb_array_len.exit1.i9.i, !dbg !97 - -rb_array_len.exit1.i9.i: ; preds = %143, %140 - %147 = phi i64 [ %142, %140 ], [ %146, %143 ], !dbg !97 - %148 = icmp sgt i64 %147, 0, !dbg !97 - br i1 %148, label %149, label %forward_sorbet_rb_array_each_withBlock.1.exit.i, !dbg !97 - -149: ; preds = %rb_array_len.exit1.i9.i - %150 = inttoptr i64 %43 to %struct.RArray*, !dbg !93 - %151 = getelementptr inbounds %struct.RArray, %struct.RArray* %150, i64 0, i32 1, i32 0, i32 0, !dbg !93 - %152 = getelementptr inbounds %struct.RArray, %struct.RArray* %150, i64 0, i32 1, i32 0, i32 2, !dbg !93 - br label %153, !dbg !97 - -153: ; preds = %rb_array_len.exit.i11.i, %149 - %154 = phi i64 [ 0, %149 ], [ %178, %rb_array_len.exit.i11.i ], !dbg !97 - %155 = load i64, i64* %65, align 8, !dbg !97, !tbaa !25 - %156 = and i64 %155, 8192, !dbg !97 - %157 = icmp eq i64 %156, 0, !dbg !97 - br i1 %157, label %158, label %rb_array_const_ptr_transient.exit.i10.i, !dbg !97 + %114 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %105, i64 0, i32 8, !dbg !75 + %115 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %114, align 8, !dbg !75, !tbaa !39 + %116 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %115, i32 noundef 0) #13, !dbg !75 + br label %rb_check_arity.1.exit.i6.i, !dbg !75 + +rb_check_arity.1.exit.i6.i: ; preds = %113, %forward_sorbet_rb_array_each_withBlock.exit.i + store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %40, align 8, !dbg !75, !tbaa !15 + %117 = load i64, i64* @"func_.13$block_2_ifunc", align 8, !dbg !88 + %118 = call %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64 %117) #13, !dbg !88 + %119 = bitcast %struct.sorbet_inlineIntrinsicEnv* %5 to i8*, !dbg !88 + call void @llvm.lifetime.start.p0i8(i64 noundef 40, i8* noundef nonnull %119) #13, !dbg !88 + %120 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %5, i64 0, i32 0, !dbg !88 + store i64 %43, i64* %120, align 8, !dbg !88, !tbaa !76 + %121 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %5, i64 0, i32 1, !dbg !88 + store i64 %rubyId_each.i, i64* %121, align 8, !dbg !88, !tbaa !78 + %122 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %5, i64 0, i32 2, !dbg !88 + store i32 0, i32* %122, align 8, !dbg !88, !tbaa !79 + %123 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %5, i64 0, i32 3, !dbg !88 + %124 = bitcast i64** %123 to i8*, !dbg !88 + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %124, i8 0, i64 16, i1 false) #13, !dbg !88 + %125 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !88, !tbaa !15 + %126 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %125, i64 0, i32 2, !dbg !88 + %127 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %126, align 8, !dbg !88, !tbaa !17 + %128 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %127, i64 0, i32 3, !dbg !88 + %129 = bitcast i64* %128 to %struct.rb_captured_block*, !dbg !88 + %130 = getelementptr inbounds i64, i64* %128, i64 2, !dbg !88 + %131 = bitcast i64* %130 to %struct.vm_ifunc**, !dbg !88 + store %struct.vm_ifunc* %118, %struct.vm_ifunc** %131, align 8, !dbg !88, !tbaa !27 + call void @llvm.experimental.noalias.scope.decl(metadata !89) #13, !dbg !88 + %132 = ptrtoint %struct.rb_captured_block* %129 to i64, !dbg !88 + %133 = or i64 %132, 3, !dbg !88 + %134 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %125, i64 0, i32 17, !dbg !88 + %135 = and i64 %133, -4, !dbg !92 + %136 = inttoptr i64 %135 to %struct.rb_captured_block*, !dbg !92 + store i64 0, i64* %134, align 8, !dbg !92, !tbaa !85 + call void @sorbet_pushBlockFrame(%struct.rb_captured_block* %136) #13, !dbg !92 + %137 = load i64, i64* %65, align 8, !dbg !92, !tbaa !25 + %138 = and i64 %137, 8192, !dbg !92 + %139 = icmp eq i64 %138, 0, !dbg !92 + br i1 %139, label %143, label %140, !dbg !92 + +140: ; preds = %rb_check_arity.1.exit.i6.i + %141 = lshr i64 %137, 15, !dbg !92 + %142 = and i64 %141, 3, !dbg !92 + br label %rb_array_len.exit1.i7.i, !dbg !92 + +143: ; preds = %rb_check_arity.1.exit.i6.i + %144 = inttoptr i64 %43 to %struct.RArray*, !dbg !92 + %145 = getelementptr inbounds %struct.RArray, %struct.RArray* %144, i64 0, i32 1, i32 0, i32 0, !dbg !92 + %146 = load i64, i64* %145, align 8, !dbg !92, !tbaa !27 + br label %rb_array_len.exit1.i7.i, !dbg !92 + +rb_array_len.exit1.i7.i: ; preds = %143, %140 + %147 = phi i64 [ %142, %140 ], [ %146, %143 ], !dbg !92 + %148 = icmp sgt i64 %147, 0, !dbg !92 + br i1 %148, label %149, label %forward_sorbet_rb_array_each_withBlock.1.exit.i, !dbg !92 + +149: ; preds = %rb_array_len.exit1.i7.i + %150 = inttoptr i64 %43 to %struct.RArray*, !dbg !88 + %151 = getelementptr inbounds %struct.RArray, %struct.RArray* %150, i64 0, i32 1, i32 0, i32 0, !dbg !88 + %152 = getelementptr inbounds %struct.RArray, %struct.RArray* %150, i64 0, i32 1, i32 0, i32 2, !dbg !88 + br label %153, !dbg !92 + +153: ; preds = %rb_array_len.exit.i9.i, %149 + %154 = phi i64 [ 0, %149 ], [ %178, %rb_array_len.exit.i9.i ], !dbg !92 + %155 = load i64, i64* %65, align 8, !dbg !92, !tbaa !25 + %156 = and i64 %155, 8192, !dbg !92 + %157 = icmp eq i64 %156, 0, !dbg !92 + br i1 %157, label %158, label %rb_array_const_ptr_transient.exit.i8.i, !dbg !92 158: ; preds = %153 - %159 = load i64*, i64** %152, align 8, !dbg !97, !tbaa !27 - br label %rb_array_const_ptr_transient.exit.i10.i, !dbg !97 - -rb_array_const_ptr_transient.exit.i10.i: ; preds = %158, %153 - %160 = phi i64* [ %159, %158 ], [ %151, %153 ], !dbg !97 - %161 = getelementptr inbounds i64, i64* %160, i64 %154, !dbg !97 - %162 = load i64, i64* %161, align 8, !dbg !97, !tbaa !6 - call void @llvm.experimental.noalias.scope.decl(metadata !99) #13, !dbg !97 - %163 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !93, !tbaa !15, !noalias !99 - %164 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %163, i64 0, i32 2, !dbg !93 - %165 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %164, align 8, !dbg !93, !tbaa !17, !noalias !99 - %166 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %165, i64 0, i32 3, !dbg !93 - %167 = load i64, i64* %166, align 8, !dbg !93, !tbaa !42, !noalias !99 - %stackFrame.i.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_2", align 8, !dbg !93, !noalias !99 - %168 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %165, i64 0, i32 2, !dbg !93 - store %struct.rb_iseq_struct* %stackFrame.i.i.i, %struct.rb_iseq_struct** %168, align 8, !dbg !93, !tbaa !21, !noalias !99 - %169 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %165, i64 0, i32 4, !dbg !93 - %170 = load i64*, i64** %169, align 8, !dbg !93, !tbaa !23, !noalias !99 - %171 = load i64, i64* %170, align 8, !dbg !93, !tbaa !6, !noalias !99 - %172 = and i64 %171, -129, !dbg !93 - store i64 %172, i64* %170, align 8, !dbg !93, !tbaa !6, !noalias !99 - %173 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %165, i64 0, i32 0, !dbg !93 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %173, align 8, !dbg !102, !tbaa !15, !noalias !99 - %174 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %165, i64 0, i32 1, !dbg !104 - %175 = load i64*, i64** %174, align 8, !dbg !104 - store i64 %167, i64* %175, align 8, !dbg !104, !tbaa !6 - %176 = getelementptr inbounds i64, i64* %175, i64 1, !dbg !104 - store i64 %162, i64* %176, align 8, !dbg !104, !tbaa !6 - %177 = getelementptr inbounds i64, i64* %176, i64 1, !dbg !104 - store i64* %177, i64** %174, align 8, !dbg !104 - %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p, i64 0), !dbg !104 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %173, align 8, !dbg !104, !tbaa !15, !noalias !99 - %178 = add nuw nsw i64 %154, 1, !dbg !97 - %179 = load i64, i64* %65, align 8, !dbg !97, !tbaa !25 - %180 = and i64 %179, 8192, !dbg !97 - %181 = icmp eq i64 %180, 0, !dbg !97 - br i1 %181, label %185, label %182, !dbg !97 - -182: ; preds = %rb_array_const_ptr_transient.exit.i10.i - %183 = lshr i64 %179, 15, !dbg !97 - %184 = and i64 %183, 3, !dbg !97 - br label %rb_array_len.exit.i11.i, !dbg !97 - -185: ; preds = %rb_array_const_ptr_transient.exit.i10.i - %186 = load i64, i64* %151, align 8, !dbg !97, !tbaa !27 - br label %rb_array_len.exit.i11.i, !dbg !97 - -rb_array_len.exit.i11.i: ; preds = %185, %182 - %187 = phi i64 [ %184, %182 ], [ %186, %185 ], !dbg !97 - %188 = icmp sgt i64 %187, %178, !dbg !97 - br i1 %188, label %153, label %forward_sorbet_rb_array_each_withBlock.1.exit.i, !dbg !97, !llvm.loop !105 - -forward_sorbet_rb_array_each_withBlock.1.exit.i: ; preds = %rb_array_len.exit.i11.i, %rb_array_len.exit1.i9.i - call void @sorbet_popFrame() #13, !dbg !97 - call void @llvm.lifetime.end.p0i8(i64 noundef 40, i8* noundef nonnull %119) #13, !dbg !93 - %189 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !93, !tbaa !15 - %190 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %189, i64 0, i32 5, !dbg !93 - %191 = load i32, i32* %190, align 8, !dbg !93, !tbaa !37 - %192 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %189, i64 0, i32 6, !dbg !93 - %193 = load i32, i32* %192, align 4, !dbg !93, !tbaa !38 - %194 = xor i32 %193, -1, !dbg !93 - %195 = and i32 %194, %191, !dbg !93 - %196 = icmp eq i32 %195, 0, !dbg !93 - br i1 %196, label %rb_check_arity.1.exit.i14.i, label %197, !dbg !93, !prof !31 + %159 = load i64*, i64** %152, align 8, !dbg !92, !tbaa !27 + br label %rb_array_const_ptr_transient.exit.i8.i, !dbg !92 + +rb_array_const_ptr_transient.exit.i8.i: ; preds = %158, %153 + %160 = phi i64* [ %159, %158 ], [ %151, %153 ], !dbg !92 + %161 = getelementptr inbounds i64, i64* %160, i64 %154, !dbg !92 + %162 = load i64, i64* %161, align 8, !dbg !92, !tbaa !6 + call void @llvm.experimental.noalias.scope.decl(metadata !94) #13, !dbg !92 + %163 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !88, !tbaa !15, !noalias !94 + %164 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %163, i64 0, i32 2, !dbg !88 + %165 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %164, align 8, !dbg !88, !tbaa !17, !noalias !94 + %166 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %165, i64 0, i32 3, !dbg !88 + %167 = load i64, i64* %166, align 8, !dbg !88, !tbaa !41, !noalias !94 + %stackFrame.i.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_2", align 8, !dbg !88, !noalias !94 + %168 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %165, i64 0, i32 2, !dbg !88 + store %struct.rb_iseq_struct* %stackFrame.i.i.i, %struct.rb_iseq_struct** %168, align 8, !dbg !88, !tbaa !21, !noalias !94 + %169 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %165, i64 0, i32 4, !dbg !88 + %170 = load i64*, i64** %169, align 8, !dbg !88, !tbaa !23, !noalias !94 + %171 = load i64, i64* %170, align 8, !dbg !88, !tbaa !6, !noalias !94 + %172 = and i64 %171, -129, !dbg !88 + store i64 %172, i64* %170, align 8, !dbg !88, !tbaa !6, !noalias !94 + %173 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %165, i64 0, i32 0, !dbg !88 + store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %173, align 8, !dbg !97, !tbaa !15, !noalias !94 + %174 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %165, i64 0, i32 1, !dbg !99 + %175 = load i64*, i64** %174, align 8, !dbg !99 + store i64 %167, i64* %175, align 8, !dbg !99, !tbaa !6 + %176 = getelementptr inbounds i64, i64* %175, i64 1, !dbg !99 + store i64 %162, i64* %176, align 8, !dbg !99, !tbaa !6 + %177 = getelementptr inbounds i64, i64* %176, i64 1, !dbg !99 + store i64* %177, i64** %174, align 8, !dbg !99 + %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p, i64 0), !dbg !99 + %178 = add nuw nsw i64 %154, 1, !dbg !92 + %179 = load i64, i64* %65, align 8, !dbg !92, !tbaa !25 + %180 = and i64 %179, 8192, !dbg !92 + %181 = icmp eq i64 %180, 0, !dbg !92 + br i1 %181, label %185, label %182, !dbg !92 + +182: ; preds = %rb_array_const_ptr_transient.exit.i8.i + %183 = lshr i64 %179, 15, !dbg !92 + %184 = and i64 %183, 3, !dbg !92 + br label %rb_array_len.exit.i9.i, !dbg !92 + +185: ; preds = %rb_array_const_ptr_transient.exit.i8.i + %186 = load i64, i64* %151, align 8, !dbg !92, !tbaa !27 + br label %rb_array_len.exit.i9.i, !dbg !92 + +rb_array_len.exit.i9.i: ; preds = %185, %182 + %187 = phi i64 [ %184, %182 ], [ %186, %185 ], !dbg !92 + %188 = icmp sgt i64 %187, %178, !dbg !92 + br i1 %188, label %153, label %forward_sorbet_rb_array_each_withBlock.1.exit.i, !dbg !92, !llvm.loop !100 + +forward_sorbet_rb_array_each_withBlock.1.exit.i: ; preds = %rb_array_len.exit.i9.i, %rb_array_len.exit1.i7.i + call void @sorbet_popFrame() #13, !dbg !92 + call void @llvm.lifetime.end.p0i8(i64 noundef 40, i8* noundef nonnull %119) #13, !dbg !88 + %189 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !88, !tbaa !15 + %190 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %189, i64 0, i32 5, !dbg !88 + %191 = load i32, i32* %190, align 8, !dbg !88, !tbaa !37 + %192 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %189, i64 0, i32 6, !dbg !88 + %193 = load i32, i32* %192, align 4, !dbg !88, !tbaa !38 + %194 = xor i32 %193, -1, !dbg !88 + %195 = and i32 %194, %191, !dbg !88 + %196 = icmp eq i32 %195, 0, !dbg !88 + br i1 %196, label %rb_check_arity.1.exit.i11.i, label %197, !dbg !88, !prof !31 197: ; preds = %forward_sorbet_rb_array_each_withBlock.1.exit.i - %198 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %189, i64 0, i32 8, !dbg !93 - %199 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %198, align 8, !dbg !93, !tbaa !39 - %200 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %199, i32 noundef 0) #13, !dbg !93 - br label %rb_check_arity.1.exit.i14.i, !dbg !93 - -rb_check_arity.1.exit.i14.i: ; preds = %197, %forward_sorbet_rb_array_each_withBlock.1.exit.i - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %40, align 8, !dbg !93, !tbaa !15 - %201 = load i64, i64* @"func_.13$block_3_ifunc", align 8, !dbg !106 - %202 = call %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64 %201) #13, !dbg !106 - %203 = bitcast %struct.sorbet_inlineIntrinsicEnv* %4 to i8*, !dbg !106 - call void @llvm.lifetime.start.p0i8(i64 noundef 40, i8* noundef nonnull %203) #13, !dbg !106 - %204 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %4, i64 0, i32 0, !dbg !106 - store i64 %43, i64* %204, align 8, !dbg !106, !tbaa !81 - %205 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %4, i64 0, i32 1, !dbg !106 - store i64 %rubyId_each.i, i64* %205, align 8, !dbg !106, !tbaa !83 - %206 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %4, i64 0, i32 2, !dbg !106 - store i32 0, i32* %206, align 8, !dbg !106, !tbaa !84 - %207 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %4, i64 0, i32 3, !dbg !106 - %208 = bitcast i64** %207 to i8*, !dbg !106 - call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %208, i8 0, i64 16, i1 false) #13, !dbg !106 - %209 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !106, !tbaa !15 - %210 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %209, i64 0, i32 2, !dbg !106 - %211 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %210, align 8, !dbg !106, !tbaa !17 - %212 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %211, i64 0, i32 3, !dbg !106 - %213 = bitcast i64* %212 to %struct.rb_captured_block*, !dbg !106 - %214 = getelementptr inbounds i64, i64* %212, i64 2, !dbg !106 - %215 = bitcast i64* %214 to %struct.vm_ifunc**, !dbg !106 - store %struct.vm_ifunc* %202, %struct.vm_ifunc** %215, align 8, !dbg !106, !tbaa !27 - call void @llvm.experimental.noalias.scope.decl(metadata !107) #13, !dbg !106 - %216 = ptrtoint %struct.rb_captured_block* %213 to i64, !dbg !106 - %217 = or i64 %216, 3, !dbg !106 - %218 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %209, i64 0, i32 17, !dbg !106 - %219 = and i64 %217, -4, !dbg !110 - %220 = inttoptr i64 %219 to %struct.rb_captured_block*, !dbg !110 - store i64 0, i64* %218, align 8, !dbg !110, !tbaa !90 - call void @sorbet_pushBlockFrame(%struct.rb_captured_block* %220) #13, !dbg !110 - %221 = load i64, i64* %65, align 8, !dbg !110, !tbaa !25 - %222 = and i64 %221, 8192, !dbg !110 - %223 = icmp eq i64 %222, 0, !dbg !110 - br i1 %223, label %227, label %224, !dbg !110 - -224: ; preds = %rb_check_arity.1.exit.i14.i - %225 = lshr i64 %221, 15, !dbg !110 - %226 = and i64 %225, 3, !dbg !110 - br label %rb_array_len.exit1.i15.i, !dbg !110 - -227: ; preds = %rb_check_arity.1.exit.i14.i - %228 = inttoptr i64 %43 to %struct.RArray*, !dbg !110 - %229 = getelementptr inbounds %struct.RArray, %struct.RArray* %228, i64 0, i32 1, i32 0, i32 0, !dbg !110 - %230 = load i64, i64* %229, align 8, !dbg !110, !tbaa !27 - br label %rb_array_len.exit1.i15.i, !dbg !110 - -rb_array_len.exit1.i15.i: ; preds = %227, %224 - %231 = phi i64 [ %226, %224 ], [ %230, %227 ], !dbg !110 - %232 = icmp sgt i64 %231, 0, !dbg !110 - br i1 %232, label %233, label %forward_sorbet_rb_array_each_withBlock.3.exit.i, !dbg !110 - -233: ; preds = %rb_array_len.exit1.i15.i - %234 = inttoptr i64 %43 to %struct.RArray*, !dbg !106 - %235 = getelementptr inbounds %struct.RArray, %struct.RArray* %234, i64 0, i32 1, i32 0, i32 0, !dbg !106 - %236 = getelementptr inbounds %struct.RArray, %struct.RArray* %234, i64 0, i32 1, i32 0, i32 2, !dbg !106 - br label %237, !dbg !110 - -237: ; preds = %rb_array_len.exit.i18.i, %233 - %238 = phi i64 [ 0, %233 ], [ %262, %rb_array_len.exit.i18.i ], !dbg !110 - %239 = load i64, i64* %65, align 8, !dbg !110, !tbaa !25 - %240 = and i64 %239, 8192, !dbg !110 - %241 = icmp eq i64 %240, 0, !dbg !110 - br i1 %241, label %242, label %rb_array_const_ptr_transient.exit.i17.i, !dbg !110 + %198 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %189, i64 0, i32 8, !dbg !88 + %199 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %198, align 8, !dbg !88, !tbaa !39 + %200 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %199, i32 noundef 0) #13, !dbg !88 + br label %rb_check_arity.1.exit.i11.i, !dbg !88 + +rb_check_arity.1.exit.i11.i: ; preds = %197, %forward_sorbet_rb_array_each_withBlock.1.exit.i + store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %40, align 8, !dbg !88, !tbaa !15 + %201 = load i64, i64* @"func_.13$block_3_ifunc", align 8, !dbg !101 + %202 = call %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64 %201) #13, !dbg !101 + %203 = bitcast %struct.sorbet_inlineIntrinsicEnv* %4 to i8*, !dbg !101 + call void @llvm.lifetime.start.p0i8(i64 noundef 40, i8* noundef nonnull %203) #13, !dbg !101 + %204 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %4, i64 0, i32 0, !dbg !101 + store i64 %43, i64* %204, align 8, !dbg !101, !tbaa !76 + %205 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %4, i64 0, i32 1, !dbg !101 + store i64 %rubyId_each.i, i64* %205, align 8, !dbg !101, !tbaa !78 + %206 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %4, i64 0, i32 2, !dbg !101 + store i32 0, i32* %206, align 8, !dbg !101, !tbaa !79 + %207 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %4, i64 0, i32 3, !dbg !101 + %208 = bitcast i64** %207 to i8*, !dbg !101 + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %208, i8 0, i64 16, i1 false) #13, !dbg !101 + %209 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !101, !tbaa !15 + %210 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %209, i64 0, i32 2, !dbg !101 + %211 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %210, align 8, !dbg !101, !tbaa !17 + %212 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %211, i64 0, i32 3, !dbg !101 + %213 = bitcast i64* %212 to %struct.rb_captured_block*, !dbg !101 + %214 = getelementptr inbounds i64, i64* %212, i64 2, !dbg !101 + %215 = bitcast i64* %214 to %struct.vm_ifunc**, !dbg !101 + store %struct.vm_ifunc* %202, %struct.vm_ifunc** %215, align 8, !dbg !101, !tbaa !27 + call void @llvm.experimental.noalias.scope.decl(metadata !102) #13, !dbg !101 + %216 = ptrtoint %struct.rb_captured_block* %213 to i64, !dbg !101 + %217 = or i64 %216, 3, !dbg !101 + %218 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %209, i64 0, i32 17, !dbg !101 + %219 = and i64 %217, -4, !dbg !105 + %220 = inttoptr i64 %219 to %struct.rb_captured_block*, !dbg !105 + store i64 0, i64* %218, align 8, !dbg !105, !tbaa !85 + call void @sorbet_pushBlockFrame(%struct.rb_captured_block* %220) #13, !dbg !105 + %221 = load i64, i64* %65, align 8, !dbg !105, !tbaa !25 + %222 = and i64 %221, 8192, !dbg !105 + %223 = icmp eq i64 %222, 0, !dbg !105 + br i1 %223, label %227, label %224, !dbg !105 + +224: ; preds = %rb_check_arity.1.exit.i11.i + %225 = lshr i64 %221, 15, !dbg !105 + %226 = and i64 %225, 3, !dbg !105 + br label %rb_array_len.exit1.i12.i, !dbg !105 + +227: ; preds = %rb_check_arity.1.exit.i11.i + %228 = inttoptr i64 %43 to %struct.RArray*, !dbg !105 + %229 = getelementptr inbounds %struct.RArray, %struct.RArray* %228, i64 0, i32 1, i32 0, i32 0, !dbg !105 + %230 = load i64, i64* %229, align 8, !dbg !105, !tbaa !27 + br label %rb_array_len.exit1.i12.i, !dbg !105 + +rb_array_len.exit1.i12.i: ; preds = %227, %224 + %231 = phi i64 [ %226, %224 ], [ %230, %227 ], !dbg !105 + %232 = icmp sgt i64 %231, 0, !dbg !105 + br i1 %232, label %233, label %forward_sorbet_rb_array_each_withBlock.3.exit.i, !dbg !105 + +233: ; preds = %rb_array_len.exit1.i12.i + %234 = inttoptr i64 %43 to %struct.RArray*, !dbg !101 + %235 = getelementptr inbounds %struct.RArray, %struct.RArray* %234, i64 0, i32 1, i32 0, i32 0, !dbg !101 + %236 = getelementptr inbounds %struct.RArray, %struct.RArray* %234, i64 0, i32 1, i32 0, i32 2, !dbg !101 + br label %237, !dbg !105 + +237: ; preds = %rb_array_len.exit.i15.i, %233 + %238 = phi i64 [ 0, %233 ], [ %262, %rb_array_len.exit.i15.i ], !dbg !105 + %239 = load i64, i64* %65, align 8, !dbg !105, !tbaa !25 + %240 = and i64 %239, 8192, !dbg !105 + %241 = icmp eq i64 %240, 0, !dbg !105 + br i1 %241, label %242, label %rb_array_const_ptr_transient.exit.i14.i, !dbg !105 242: ; preds = %237 - %243 = load i64*, i64** %236, align 8, !dbg !110, !tbaa !27 - br label %rb_array_const_ptr_transient.exit.i17.i, !dbg !110 - -rb_array_const_ptr_transient.exit.i17.i: ; preds = %242, %237 - %244 = phi i64* [ %243, %242 ], [ %235, %237 ], !dbg !110 - %245 = getelementptr inbounds i64, i64* %244, i64 %238, !dbg !110 - %246 = load i64, i64* %245, align 8, !dbg !110, !tbaa !6 - call void @llvm.experimental.noalias.scope.decl(metadata !112) #13, !dbg !110 - %247 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !106, !tbaa !15, !noalias !112 - %248 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %247, i64 0, i32 2, !dbg !106 - %249 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %248, align 8, !dbg !106, !tbaa !17, !noalias !112 - %250 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %249, i64 0, i32 3, !dbg !106 - %251 = load i64, i64* %250, align 8, !dbg !106, !tbaa !42, !noalias !112 - %stackFrame.i.i16.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_3", align 8, !dbg !106, !noalias !112 - %252 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %249, i64 0, i32 2, !dbg !106 - store %struct.rb_iseq_struct* %stackFrame.i.i16.i, %struct.rb_iseq_struct** %252, align 8, !dbg !106, !tbaa !21, !noalias !112 - %253 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %249, i64 0, i32 4, !dbg !106 - %254 = load i64*, i64** %253, align 8, !dbg !106, !tbaa !23, !noalias !112 - %255 = load i64, i64* %254, align 8, !dbg !106, !tbaa !6, !noalias !112 - %256 = and i64 %255, -129, !dbg !106 - store i64 %256, i64* %254, align 8, !dbg !106, !tbaa !6, !noalias !112 - %257 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %249, i64 0, i32 0, !dbg !106 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 19), i64** %257, align 8, !dbg !106, !tbaa !15, !noalias !112 - %258 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %249, i64 0, i32 1, !dbg !115 - %259 = load i64*, i64** %258, align 8, !dbg !115 - store i64 %251, i64* %259, align 8, !dbg !115, !tbaa !6 - %260 = getelementptr inbounds i64, i64* %259, i64 1, !dbg !115 - store i64 %246, i64* %260, align 8, !dbg !115, !tbaa !6 - %261 = getelementptr inbounds i64, i64* %260, i64 1, !dbg !115 - store i64* %261, i64** %258, align 8, !dbg !115 - %send14 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.5, i64 0), !dbg !115 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %257, align 8, !dbg !115, !tbaa !15, !noalias !112 - %262 = add nuw nsw i64 %238, 1, !dbg !110 - %263 = load i64, i64* %65, align 8, !dbg !110, !tbaa !25 - %264 = and i64 %263, 8192, !dbg !110 - %265 = icmp eq i64 %264, 0, !dbg !110 - br i1 %265, label %269, label %266, !dbg !110 - -266: ; preds = %rb_array_const_ptr_transient.exit.i17.i - %267 = lshr i64 %263, 15, !dbg !110 - %268 = and i64 %267, 3, !dbg !110 - br label %rb_array_len.exit.i18.i, !dbg !110 - -269: ; preds = %rb_array_const_ptr_transient.exit.i17.i - %270 = load i64, i64* %235, align 8, !dbg !110, !tbaa !27 - br label %rb_array_len.exit.i18.i, !dbg !110 - -rb_array_len.exit.i18.i: ; preds = %269, %266 - %271 = phi i64 [ %268, %266 ], [ %270, %269 ], !dbg !110 - %272 = icmp sgt i64 %271, %262, !dbg !110 - br i1 %272, label %237, label %forward_sorbet_rb_array_each_withBlock.3.exit.i, !dbg !110, !llvm.loop !117 - -forward_sorbet_rb_array_each_withBlock.3.exit.i: ; preds = %rb_array_len.exit.i18.i, %rb_array_len.exit1.i15.i - call void @sorbet_popFrame() #13, !dbg !110 - call void @llvm.lifetime.end.p0i8(i64 noundef 40, i8* noundef nonnull %203) #13, !dbg !106 - %273 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !106, !tbaa !15 - %274 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %273, i64 0, i32 5, !dbg !106 - %275 = load i32, i32* %274, align 8, !dbg !106, !tbaa !37 - %276 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %273, i64 0, i32 6, !dbg !106 - %277 = load i32, i32* %276, align 4, !dbg !106, !tbaa !38 - %278 = xor i32 %277, -1, !dbg !106 - %279 = and i32 %278, %275, !dbg !106 - %280 = icmp eq i32 %279, 0, !dbg !106 - br i1 %280, label %rb_check_arity.1.exit.i21.i, label %281, !dbg !106, !prof !31 + %243 = load i64*, i64** %236, align 8, !dbg !105, !tbaa !27 + br label %rb_array_const_ptr_transient.exit.i14.i, !dbg !105 + +rb_array_const_ptr_transient.exit.i14.i: ; preds = %242, %237 + %244 = phi i64* [ %243, %242 ], [ %235, %237 ], !dbg !105 + %245 = getelementptr inbounds i64, i64* %244, i64 %238, !dbg !105 + %246 = load i64, i64* %245, align 8, !dbg !105, !tbaa !6 + call void @llvm.experimental.noalias.scope.decl(metadata !107) #13, !dbg !105 + %247 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !101, !tbaa !15, !noalias !107 + %248 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %247, i64 0, i32 2, !dbg !101 + %249 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %248, align 8, !dbg !101, !tbaa !17, !noalias !107 + %250 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %249, i64 0, i32 3, !dbg !101 + %251 = load i64, i64* %250, align 8, !dbg !101, !tbaa !41, !noalias !107 + %stackFrame.i.i13.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_3", align 8, !dbg !101, !noalias !107 + %252 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %249, i64 0, i32 2, !dbg !101 + store %struct.rb_iseq_struct* %stackFrame.i.i13.i, %struct.rb_iseq_struct** %252, align 8, !dbg !101, !tbaa !21, !noalias !107 + %253 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %249, i64 0, i32 4, !dbg !101 + %254 = load i64*, i64** %253, align 8, !dbg !101, !tbaa !23, !noalias !107 + %255 = load i64, i64* %254, align 8, !dbg !101, !tbaa !6, !noalias !107 + %256 = and i64 %255, -129, !dbg !101 + store i64 %256, i64* %254, align 8, !dbg !101, !tbaa !6, !noalias !107 + %257 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %249, i64 0, i32 0, !dbg !101 + store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 19), i64** %257, align 8, !dbg !101, !tbaa !15, !noalias !107 + %258 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %249, i64 0, i32 1, !dbg !110 + %259 = load i64*, i64** %258, align 8, !dbg !110 + store i64 %251, i64* %259, align 8, !dbg !110, !tbaa !6 + %260 = getelementptr inbounds i64, i64* %259, i64 1, !dbg !110 + store i64 %246, i64* %260, align 8, !dbg !110, !tbaa !6 + %261 = getelementptr inbounds i64, i64* %260, i64 1, !dbg !110 + store i64* %261, i64** %258, align 8, !dbg !110 + %send14 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.5, i64 0), !dbg !110 + %262 = add nuw nsw i64 %238, 1, !dbg !105 + %263 = load i64, i64* %65, align 8, !dbg !105, !tbaa !25 + %264 = and i64 %263, 8192, !dbg !105 + %265 = icmp eq i64 %264, 0, !dbg !105 + br i1 %265, label %269, label %266, !dbg !105 + +266: ; preds = %rb_array_const_ptr_transient.exit.i14.i + %267 = lshr i64 %263, 15, !dbg !105 + %268 = and i64 %267, 3, !dbg !105 + br label %rb_array_len.exit.i15.i, !dbg !105 + +269: ; preds = %rb_array_const_ptr_transient.exit.i14.i + %270 = load i64, i64* %235, align 8, !dbg !105, !tbaa !27 + br label %rb_array_len.exit.i15.i, !dbg !105 + +rb_array_len.exit.i15.i: ; preds = %269, %266 + %271 = phi i64 [ %268, %266 ], [ %270, %269 ], !dbg !105 + %272 = icmp sgt i64 %271, %262, !dbg !105 + br i1 %272, label %237, label %forward_sorbet_rb_array_each_withBlock.3.exit.i, !dbg !105, !llvm.loop !112 + +forward_sorbet_rb_array_each_withBlock.3.exit.i: ; preds = %rb_array_len.exit.i15.i, %rb_array_len.exit1.i12.i + call void @sorbet_popFrame() #13, !dbg !105 + call void @llvm.lifetime.end.p0i8(i64 noundef 40, i8* noundef nonnull %203) #13, !dbg !101 + %273 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !101, !tbaa !15 + %274 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %273, i64 0, i32 5, !dbg !101 + %275 = load i32, i32* %274, align 8, !dbg !101, !tbaa !37 + %276 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %273, i64 0, i32 6, !dbg !101 + %277 = load i32, i32* %276, align 4, !dbg !101, !tbaa !38 + %278 = xor i32 %277, -1, !dbg !101 + %279 = and i32 %278, %275, !dbg !101 + %280 = icmp eq i32 %279, 0, !dbg !101 + br i1 %280, label %rb_check_arity.1.exit.i17.i, label %281, !dbg !101, !prof !31 281: ; preds = %forward_sorbet_rb_array_each_withBlock.3.exit.i - %282 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %273, i64 0, i32 8, !dbg !106 - %283 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %282, align 8, !dbg !106, !tbaa !39 - %284 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %283, i32 noundef 0) #13, !dbg !106 - br label %rb_check_arity.1.exit.i21.i, !dbg !106 - -rb_check_arity.1.exit.i21.i: ; preds = %281, %forward_sorbet_rb_array_each_withBlock.3.exit.i - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 23), i64** %40, align 8, !dbg !106, !tbaa !15 - %285 = load i64, i64* @"func_.13$block_4_ifunc", align 8, !dbg !118 - %286 = call %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64 %285) #13, !dbg !118 - %287 = bitcast %struct.sorbet_inlineIntrinsicEnv* %3 to i8*, !dbg !118 - call void @llvm.lifetime.start.p0i8(i64 noundef 40, i8* noundef nonnull %287) #13, !dbg !118 - %288 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %3, i64 0, i32 0, !dbg !118 - store i64 %43, i64* %288, align 8, !dbg !118, !tbaa !81 - %289 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %3, i64 0, i32 1, !dbg !118 - store i64 %rubyId_each.i, i64* %289, align 8, !dbg !118, !tbaa !83 - %290 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %3, i64 0, i32 2, !dbg !118 - store i32 0, i32* %290, align 8, !dbg !118, !tbaa !84 - %291 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %3, i64 0, i32 3, !dbg !118 - %292 = bitcast i64** %291 to i8*, !dbg !118 - call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %292, i8 0, i64 16, i1 false) #13, !dbg !118 - %293 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !118, !tbaa !15 - %294 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %293, i64 0, i32 2, !dbg !118 - %295 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %294, align 8, !dbg !118, !tbaa !17 - %296 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %295, i64 0, i32 3, !dbg !118 - %297 = bitcast i64* %296 to %struct.rb_captured_block*, !dbg !118 - %298 = getelementptr inbounds i64, i64* %296, i64 2, !dbg !118 - %299 = bitcast i64* %298 to %struct.vm_ifunc**, !dbg !118 - store %struct.vm_ifunc* %286, %struct.vm_ifunc** %299, align 8, !dbg !118, !tbaa !27 - call void @llvm.experimental.noalias.scope.decl(metadata !119) #13, !dbg !118 - %300 = ptrtoint %struct.rb_captured_block* %297 to i64, !dbg !118 - %301 = or i64 %300, 3, !dbg !118 - %302 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %293, i64 0, i32 17, !dbg !118 - %303 = and i64 %301, -4, !dbg !122 - %304 = inttoptr i64 %303 to %struct.rb_captured_block*, !dbg !122 - store i64 0, i64* %302, align 8, !dbg !122, !tbaa !90 - call void @sorbet_pushBlockFrame(%struct.rb_captured_block* %304) #13, !dbg !122 - %305 = load i64, i64* %65, align 8, !dbg !122, !tbaa !25 - %306 = and i64 %305, 8192, !dbg !122 - %307 = icmp eq i64 %306, 0, !dbg !122 - br i1 %307, label %311, label %308, !dbg !122 - -308: ; preds = %rb_check_arity.1.exit.i21.i - %309 = lshr i64 %305, 15, !dbg !122 - %310 = and i64 %309, 3, !dbg !122 - br label %rb_array_len.exit1.i22.i, !dbg !122 - -311: ; preds = %rb_check_arity.1.exit.i21.i - %312 = inttoptr i64 %43 to %struct.RArray*, !dbg !122 - %313 = getelementptr inbounds %struct.RArray, %struct.RArray* %312, i64 0, i32 1, i32 0, i32 0, !dbg !122 - %314 = load i64, i64* %313, align 8, !dbg !122, !tbaa !27 - br label %rb_array_len.exit1.i22.i, !dbg !122 - -rb_array_len.exit1.i22.i: ; preds = %311, %308 - %315 = phi i64 [ %310, %308 ], [ %314, %311 ], !dbg !122 - %316 = icmp sgt i64 %315, 0, !dbg !122 - br i1 %316, label %317, label %forward_sorbet_rb_array_each_withBlock.6.exit.i, !dbg !122 - -317: ; preds = %rb_array_len.exit1.i22.i - %318 = bitcast i64* %0 to i8*, !dbg !122 - %319 = inttoptr i64 %43 to %struct.RArray*, !dbg !118 - %320 = getelementptr inbounds %struct.RArray, %struct.RArray* %319, i64 0, i32 1, i32 0, i32 0, !dbg !118 - %321 = getelementptr inbounds %struct.RArray, %struct.RArray* %319, i64 0, i32 1, i32 0, i32 2, !dbg !118 - br label %322, !dbg !122 - -322: ; preds = %rb_array_len.exit.i24.i, %317 - %323 = phi i64 [ 0, %317 ], [ %333, %rb_array_len.exit.i24.i ], !dbg !122 - call void @llvm.lifetime.start.p0i8(i64 noundef 8, i8* noundef nonnull align 8 dereferenceable(8) %318) #13, !dbg !122 - %324 = load i64, i64* %65, align 8, !dbg !122, !tbaa !25 - %325 = and i64 %324, 8192, !dbg !122 - %326 = icmp eq i64 %325, 0, !dbg !122 - br i1 %326, label %327, label %rb_array_const_ptr_transient.exit.i23.i, !dbg !122 + %282 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %273, i64 0, i32 8, !dbg !101 + %283 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %282, align 8, !dbg !101, !tbaa !39 + %284 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %283, i32 noundef 0) #13, !dbg !101 + br label %rb_check_arity.1.exit.i17.i, !dbg !101 + +rb_check_arity.1.exit.i17.i: ; preds = %281, %forward_sorbet_rb_array_each_withBlock.3.exit.i + store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 23), i64** %40, align 8, !dbg !101, !tbaa !15 + %285 = load i64, i64* @"func_.13$block_4_ifunc", align 8, !dbg !113 + %286 = call %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64 %285) #13, !dbg !113 + %287 = bitcast %struct.sorbet_inlineIntrinsicEnv* %3 to i8*, !dbg !113 + call void @llvm.lifetime.start.p0i8(i64 noundef 40, i8* noundef nonnull %287) #13, !dbg !113 + %288 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %3, i64 0, i32 0, !dbg !113 + store i64 %43, i64* %288, align 8, !dbg !113, !tbaa !76 + %289 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %3, i64 0, i32 1, !dbg !113 + store i64 %rubyId_each.i, i64* %289, align 8, !dbg !113, !tbaa !78 + %290 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %3, i64 0, i32 2, !dbg !113 + store i32 0, i32* %290, align 8, !dbg !113, !tbaa !79 + %291 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %3, i64 0, i32 3, !dbg !113 + %292 = bitcast i64** %291 to i8*, !dbg !113 + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %292, i8 0, i64 16, i1 false) #13, !dbg !113 + %293 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !113, !tbaa !15 + %294 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %293, i64 0, i32 2, !dbg !113 + %295 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %294, align 8, !dbg !113, !tbaa !17 + %296 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %295, i64 0, i32 3, !dbg !113 + %297 = bitcast i64* %296 to %struct.rb_captured_block*, !dbg !113 + %298 = getelementptr inbounds i64, i64* %296, i64 2, !dbg !113 + %299 = bitcast i64* %298 to %struct.vm_ifunc**, !dbg !113 + store %struct.vm_ifunc* %286, %struct.vm_ifunc** %299, align 8, !dbg !113, !tbaa !27 + call void @llvm.experimental.noalias.scope.decl(metadata !114) #13, !dbg !113 + %300 = ptrtoint %struct.rb_captured_block* %297 to i64, !dbg !113 + %301 = or i64 %300, 3, !dbg !113 + %302 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %293, i64 0, i32 17, !dbg !113 + %303 = and i64 %301, -4, !dbg !117 + %304 = inttoptr i64 %303 to %struct.rb_captured_block*, !dbg !117 + store i64 0, i64* %302, align 8, !dbg !117, !tbaa !85 + call void @sorbet_pushBlockFrame(%struct.rb_captured_block* %304) #13, !dbg !117 + %305 = load i64, i64* %65, align 8, !dbg !117, !tbaa !25 + %306 = and i64 %305, 8192, !dbg !117 + %307 = icmp eq i64 %306, 0, !dbg !117 + br i1 %307, label %311, label %308, !dbg !117 + +308: ; preds = %rb_check_arity.1.exit.i17.i + %309 = lshr i64 %305, 15, !dbg !117 + %310 = and i64 %309, 3, !dbg !117 + br label %rb_array_len.exit1.i18.i, !dbg !117 + +311: ; preds = %rb_check_arity.1.exit.i17.i + %312 = inttoptr i64 %43 to %struct.RArray*, !dbg !117 + %313 = getelementptr inbounds %struct.RArray, %struct.RArray* %312, i64 0, i32 1, i32 0, i32 0, !dbg !117 + %314 = load i64, i64* %313, align 8, !dbg !117, !tbaa !27 + br label %rb_array_len.exit1.i18.i, !dbg !117 + +rb_array_len.exit1.i18.i: ; preds = %311, %308 + %315 = phi i64 [ %310, %308 ], [ %314, %311 ], !dbg !117 + %316 = icmp sgt i64 %315, 0, !dbg !117 + br i1 %316, label %317, label %forward_sorbet_rb_array_each_withBlock.6.exit.i, !dbg !117 + +317: ; preds = %rb_array_len.exit1.i18.i + %318 = bitcast i64* %0 to i8*, !dbg !117 + %319 = inttoptr i64 %43 to %struct.RArray*, !dbg !113 + %320 = getelementptr inbounds %struct.RArray, %struct.RArray* %319, i64 0, i32 1, i32 0, i32 0, !dbg !113 + %321 = getelementptr inbounds %struct.RArray, %struct.RArray* %319, i64 0, i32 1, i32 0, i32 2, !dbg !113 + br label %322, !dbg !117 + +322: ; preds = %rb_array_len.exit.i20.i, %317 + %323 = phi i64 [ 0, %317 ], [ %333, %rb_array_len.exit.i20.i ], !dbg !117 + call void @llvm.lifetime.start.p0i8(i64 noundef 8, i8* noundef nonnull align 8 dereferenceable(8) %318) #13, !dbg !117 + %324 = load i64, i64* %65, align 8, !dbg !117, !tbaa !25 + %325 = and i64 %324, 8192, !dbg !117 + %326 = icmp eq i64 %325, 0, !dbg !117 + br i1 %326, label %327, label %rb_array_const_ptr_transient.exit.i19.i, !dbg !117 327: ; preds = %322 - %328 = load i64*, i64** %321, align 8, !dbg !122, !tbaa !27 - br label %rb_array_const_ptr_transient.exit.i23.i, !dbg !122 - -rb_array_const_ptr_transient.exit.i23.i: ; preds = %327, %322 - %329 = phi i64* [ %328, %327 ], [ %320, %322 ], !dbg !122 - %330 = getelementptr inbounds i64, i64* %329, i64 %323, !dbg !122 - %331 = load i64, i64* %330, align 8, !dbg !122, !tbaa !6 - store i64 %331, i64* %0, align 8, !dbg !122, !tbaa !6 - %332 = call i64 @"func_.13$block_4"(i64 undef, i64 undef, i32 noundef 1, i64* noalias nocapture noundef nonnull readonly align 8 dereferenceable(8) %0, i64 undef) #13, !dbg !122 - call void @llvm.lifetime.end.p0i8(i64 noundef 8, i8* noundef nonnull %318) #13, !dbg !122 - %333 = add nuw nsw i64 %323, 1, !dbg !122 - %334 = load i64, i64* %65, align 8, !dbg !122, !tbaa !25 - %335 = and i64 %334, 8192, !dbg !122 - %336 = icmp eq i64 %335, 0, !dbg !122 - br i1 %336, label %340, label %337, !dbg !122 - -337: ; preds = %rb_array_const_ptr_transient.exit.i23.i - %338 = lshr i64 %334, 15, !dbg !122 - %339 = and i64 %338, 3, !dbg !122 - br label %rb_array_len.exit.i24.i, !dbg !122 - -340: ; preds = %rb_array_const_ptr_transient.exit.i23.i - %341 = load i64, i64* %320, align 8, !dbg !122, !tbaa !27 - br label %rb_array_len.exit.i24.i, !dbg !122 - -rb_array_len.exit.i24.i: ; preds = %340, %337 - %342 = phi i64 [ %339, %337 ], [ %341, %340 ], !dbg !122 - %343 = icmp sgt i64 %342, %333, !dbg !122 - br i1 %343, label %322, label %forward_sorbet_rb_array_each_withBlock.6.exit.i, !dbg !122, !llvm.loop !124 - -forward_sorbet_rb_array_each_withBlock.6.exit.i: ; preds = %rb_array_len.exit.i24.i, %rb_array_len.exit1.i22.i - call void @sorbet_popFrame() #13, !dbg !122 - call void @llvm.lifetime.end.p0i8(i64 noundef 40, i8* noundef nonnull %287) #13, !dbg !118 - %344 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !118, !tbaa !15 - %345 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %344, i64 0, i32 5, !dbg !118 - %346 = load i32, i32* %345, align 8, !dbg !118, !tbaa !37 - %347 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %344, i64 0, i32 6, !dbg !118 - %348 = load i32, i32* %347, align 4, !dbg !118, !tbaa !38 - %349 = xor i32 %348, -1, !dbg !118 - %350 = and i32 %349, %346, !dbg !118 - %351 = icmp eq i32 %350, 0, !dbg !118 - br i1 %351, label %rb_check_arity.1.exit.i.i, label %352, !dbg !118, !prof !31 + %328 = load i64*, i64** %321, align 8, !dbg !117, !tbaa !27 + br label %rb_array_const_ptr_transient.exit.i19.i, !dbg !117 + +rb_array_const_ptr_transient.exit.i19.i: ; preds = %327, %322 + %329 = phi i64* [ %328, %327 ], [ %320, %322 ], !dbg !117 + %330 = getelementptr inbounds i64, i64* %329, i64 %323, !dbg !117 + %331 = load i64, i64* %330, align 8, !dbg !117, !tbaa !6 + store i64 %331, i64* %0, align 8, !dbg !117, !tbaa !6 + %332 = call i64 @"func_.13$block_4"(i64 undef, i64 undef, i32 noundef 1, i64* noalias nocapture noundef nonnull readonly align 8 dereferenceable(8) %0, i64 undef) #13, !dbg !117 + call void @llvm.lifetime.end.p0i8(i64 noundef 8, i8* noundef nonnull %318) #13, !dbg !117 + %333 = add nuw nsw i64 %323, 1, !dbg !117 + %334 = load i64, i64* %65, align 8, !dbg !117, !tbaa !25 + %335 = and i64 %334, 8192, !dbg !117 + %336 = icmp eq i64 %335, 0, !dbg !117 + br i1 %336, label %340, label %337, !dbg !117 + +337: ; preds = %rb_array_const_ptr_transient.exit.i19.i + %338 = lshr i64 %334, 15, !dbg !117 + %339 = and i64 %338, 3, !dbg !117 + br label %rb_array_len.exit.i20.i, !dbg !117 + +340: ; preds = %rb_array_const_ptr_transient.exit.i19.i + %341 = load i64, i64* %320, align 8, !dbg !117, !tbaa !27 + br label %rb_array_len.exit.i20.i, !dbg !117 + +rb_array_len.exit.i20.i: ; preds = %340, %337 + %342 = phi i64 [ %339, %337 ], [ %341, %340 ], !dbg !117 + %343 = icmp sgt i64 %342, %333, !dbg !117 + br i1 %343, label %322, label %forward_sorbet_rb_array_each_withBlock.6.exit.i, !dbg !117, !llvm.loop !119 + +forward_sorbet_rb_array_each_withBlock.6.exit.i: ; preds = %rb_array_len.exit.i20.i, %rb_array_len.exit1.i18.i + call void @sorbet_popFrame() #13, !dbg !117 + call void @llvm.lifetime.end.p0i8(i64 noundef 40, i8* noundef nonnull %287) #13, !dbg !113 + %344 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !113, !tbaa !15 + %345 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %344, i64 0, i32 5, !dbg !113 + %346 = load i32, i32* %345, align 8, !dbg !113, !tbaa !37 + %347 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %344, i64 0, i32 6, !dbg !113 + %348 = load i32, i32* %347, align 4, !dbg !113, !tbaa !38 + %349 = xor i32 %348, -1, !dbg !113 + %350 = and i32 %349, %346, !dbg !113 + %351 = icmp eq i32 %350, 0, !dbg !113 + br i1 %351, label %rb_check_arity.1.exit.i.i, label %352, !dbg !113, !prof !31 352: ; preds = %forward_sorbet_rb_array_each_withBlock.6.exit.i - %353 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %344, i64 0, i32 8, !dbg !118 - %354 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %353, align 8, !dbg !118, !tbaa !39 - %355 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %354, i32 noundef 0) #13, !dbg !118 - br label %rb_check_arity.1.exit.i.i, !dbg !118 + %353 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %344, i64 0, i32 8, !dbg !113 + %354 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %353, align 8, !dbg !113, !tbaa !39 + %355 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %354, i32 noundef 0) #13, !dbg !113 + br label %rb_check_arity.1.exit.i.i, !dbg !113 rb_check_arity.1.exit.i.i: ; preds = %352, %forward_sorbet_rb_array_each_withBlock.6.exit.i - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 28), i64** %40, align 8, !dbg !118, !tbaa !15 - %356 = load i64, i64* @"func_.13$block_5_ifunc", align 8, !dbg !125 - %357 = call %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64 %356) #13, !dbg !125 - %358 = bitcast %struct.sorbet_inlineIntrinsicEnv* %7 to i8*, !dbg !125 - call void @llvm.lifetime.start.p0i8(i64 noundef 40, i8* noundef nonnull %358) #13, !dbg !125 - %359 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %7, i64 0, i32 0, !dbg !125 - store i64 %43, i64* %359, align 8, !dbg !125, !tbaa !81 - %360 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %7, i64 0, i32 1, !dbg !125 - store i64 %rubyId_each.i, i64* %360, align 8, !dbg !125, !tbaa !83 - %361 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %7, i64 0, i32 2, !dbg !125 - store i32 0, i32* %361, align 8, !dbg !125, !tbaa !84 - %362 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %7, i64 0, i32 3, !dbg !125 - %363 = bitcast i64** %362 to i8*, !dbg !125 - call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %363, i8 0, i64 16, i1 false) #13, !dbg !125 - %364 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !125, !tbaa !15 - %365 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %364, i64 0, i32 2, !dbg !125 - %366 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %365, align 8, !dbg !125, !tbaa !17 - %367 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %366, i64 0, i32 3, !dbg !125 - %368 = bitcast i64* %367 to %struct.rb_captured_block*, !dbg !125 - %369 = getelementptr inbounds i64, i64* %367, i64 2, !dbg !125 - %370 = bitcast i64* %369 to %struct.vm_ifunc**, !dbg !125 - store %struct.vm_ifunc* %357, %struct.vm_ifunc** %370, align 8, !dbg !125, !tbaa !27 - call void @llvm.experimental.noalias.scope.decl(metadata !126) #13, !dbg !125 - %371 = ptrtoint %struct.rb_captured_block* %368 to i64, !dbg !125 - %372 = or i64 %371, 3, !dbg !125 - %373 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %364, i64 0, i32 17, !dbg !125 - %374 = and i64 %372, -4, !dbg !129 - %375 = inttoptr i64 %374 to %struct.rb_captured_block*, !dbg !129 - store i64 0, i64* %373, align 8, !dbg !129, !tbaa !90 - call void @sorbet_pushBlockFrame(%struct.rb_captured_block* %375) #13, !dbg !129 - %376 = load i64, i64* %65, align 8, !dbg !129, !tbaa !25 - %377 = and i64 %376, 8192, !dbg !129 - %378 = icmp eq i64 %377, 0, !dbg !129 - br i1 %378, label %382, label %379, !dbg !129 + store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 28), i64** %40, align 8, !dbg !113, !tbaa !15 + %356 = load i64, i64* @"func_.13$block_5_ifunc", align 8, !dbg !120 + %357 = call %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64 %356) #13, !dbg !120 + %358 = bitcast %struct.sorbet_inlineIntrinsicEnv* %7 to i8*, !dbg !120 + call void @llvm.lifetime.start.p0i8(i64 noundef 40, i8* noundef nonnull %358) #13, !dbg !120 + %359 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %7, i64 0, i32 0, !dbg !120 + store i64 %43, i64* %359, align 8, !dbg !120, !tbaa !76 + %360 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %7, i64 0, i32 1, !dbg !120 + store i64 %rubyId_each.i, i64* %360, align 8, !dbg !120, !tbaa !78 + %361 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %7, i64 0, i32 2, !dbg !120 + store i32 0, i32* %361, align 8, !dbg !120, !tbaa !79 + %362 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %7, i64 0, i32 3, !dbg !120 + %363 = bitcast i64** %362 to i8*, !dbg !120 + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %363, i8 0, i64 16, i1 false) #13, !dbg !120 + %364 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !120, !tbaa !15 + %365 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %364, i64 0, i32 2, !dbg !120 + %366 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %365, align 8, !dbg !120, !tbaa !17 + %367 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %366, i64 0, i32 3, !dbg !120 + %368 = bitcast i64* %367 to %struct.rb_captured_block*, !dbg !120 + %369 = getelementptr inbounds i64, i64* %367, i64 2, !dbg !120 + %370 = bitcast i64* %369 to %struct.vm_ifunc**, !dbg !120 + store %struct.vm_ifunc* %357, %struct.vm_ifunc** %370, align 8, !dbg !120, !tbaa !27 + call void @llvm.experimental.noalias.scope.decl(metadata !121) #13, !dbg !120 + %371 = ptrtoint %struct.rb_captured_block* %368 to i64, !dbg !120 + %372 = or i64 %371, 3, !dbg !120 + %373 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %364, i64 0, i32 17, !dbg !120 + %374 = and i64 %372, -4, !dbg !124 + %375 = inttoptr i64 %374 to %struct.rb_captured_block*, !dbg !124 + store i64 0, i64* %373, align 8, !dbg !124, !tbaa !85 + call void @sorbet_pushBlockFrame(%struct.rb_captured_block* %375) #13, !dbg !124 + %376 = load i64, i64* %65, align 8, !dbg !124, !tbaa !25 + %377 = and i64 %376, 8192, !dbg !124 + %378 = icmp eq i64 %377, 0, !dbg !124 + br i1 %378, label %382, label %379, !dbg !124 379: ; preds = %rb_check_arity.1.exit.i.i - %380 = lshr i64 %376, 15, !dbg !129 - %381 = and i64 %380, 3, !dbg !129 - br label %rb_array_len.exit1.i.i, !dbg !129 + %380 = lshr i64 %376, 15, !dbg !124 + %381 = and i64 %380, 3, !dbg !124 + br label %rb_array_len.exit1.i.i, !dbg !124 382: ; preds = %rb_check_arity.1.exit.i.i - %383 = inttoptr i64 %43 to %struct.RArray*, !dbg !129 - %384 = getelementptr inbounds %struct.RArray, %struct.RArray* %383, i64 0, i32 1, i32 0, i32 0, !dbg !129 - %385 = load i64, i64* %384, align 8, !dbg !129, !tbaa !27 - br label %rb_array_len.exit1.i.i, !dbg !129 + %383 = inttoptr i64 %43 to %struct.RArray*, !dbg !124 + %384 = getelementptr inbounds %struct.RArray, %struct.RArray* %383, i64 0, i32 1, i32 0, i32 0, !dbg !124 + %385 = load i64, i64* %384, align 8, !dbg !124, !tbaa !27 + br label %rb_array_len.exit1.i.i, !dbg !124 rb_array_len.exit1.i.i: ; preds = %382, %379 - %386 = phi i64 [ %381, %379 ], [ %385, %382 ], !dbg !129 - %387 = icmp sgt i64 %386, 0, !dbg !129 - br i1 %387, label %388, label %forward_sorbet_rb_array_each_withBlock.10.exit.i, !dbg !129 + %386 = phi i64 [ %381, %379 ], [ %385, %382 ], !dbg !124 + %387 = icmp sgt i64 %386, 0, !dbg !124 + br i1 %387, label %388, label %forward_sorbet_rb_array_each_withBlock.10.exit.i, !dbg !124 388: ; preds = %rb_array_len.exit1.i.i - %389 = bitcast i64* %2 to i8*, !dbg !129 - %390 = inttoptr i64 %43 to %struct.RArray*, !dbg !125 - %391 = getelementptr inbounds %struct.RArray, %struct.RArray* %390, i64 0, i32 1, i32 0, i32 0, !dbg !125 - %392 = getelementptr inbounds %struct.RArray, %struct.RArray* %390, i64 0, i32 1, i32 0, i32 2, !dbg !125 - br label %393, !dbg !129 + %389 = bitcast i64* %2 to i8*, !dbg !124 + %390 = inttoptr i64 %43 to %struct.RArray*, !dbg !120 + %391 = getelementptr inbounds %struct.RArray, %struct.RArray* %390, i64 0, i32 1, i32 0, i32 0, !dbg !120 + %392 = getelementptr inbounds %struct.RArray, %struct.RArray* %390, i64 0, i32 1, i32 0, i32 2, !dbg !120 + br label %393, !dbg !124 393: ; preds = %rb_array_len.exit.i.i, %388 - %394 = phi i64 [ 0, %388 ], [ %404, %rb_array_len.exit.i.i ], !dbg !129 - call void @llvm.lifetime.start.p0i8(i64 noundef 8, i8* noundef nonnull align 8 dereferenceable(8) %389) #13, !dbg !129 - %395 = load i64, i64* %65, align 8, !dbg !129, !tbaa !25 - %396 = and i64 %395, 8192, !dbg !129 - %397 = icmp eq i64 %396, 0, !dbg !129 - br i1 %397, label %398, label %rb_array_const_ptr_transient.exit.i.i, !dbg !129 + %394 = phi i64 [ 0, %388 ], [ %404, %rb_array_len.exit.i.i ], !dbg !124 + call void @llvm.lifetime.start.p0i8(i64 noundef 8, i8* noundef nonnull align 8 dereferenceable(8) %389) #13, !dbg !124 + %395 = load i64, i64* %65, align 8, !dbg !124, !tbaa !25 + %396 = and i64 %395, 8192, !dbg !124 + %397 = icmp eq i64 %396, 0, !dbg !124 + br i1 %397, label %398, label %rb_array_const_ptr_transient.exit.i.i, !dbg !124 398: ; preds = %393 - %399 = load i64*, i64** %392, align 8, !dbg !129, !tbaa !27 - br label %rb_array_const_ptr_transient.exit.i.i, !dbg !129 + %399 = load i64*, i64** %392, align 8, !dbg !124, !tbaa !27 + br label %rb_array_const_ptr_transient.exit.i.i, !dbg !124 rb_array_const_ptr_transient.exit.i.i: ; preds = %398, %393 - %400 = phi i64* [ %399, %398 ], [ %391, %393 ], !dbg !129 - %401 = getelementptr inbounds i64, i64* %400, i64 %394, !dbg !129 - %402 = load i64, i64* %401, align 8, !dbg !129, !tbaa !6 - store i64 %402, i64* %2, align 8, !dbg !129, !tbaa !6 - %403 = call i64 @"func_.13$block_5"(i64 undef, i64 undef, i32 noundef 1, i64* noalias nocapture noundef nonnull readonly align 8 dereferenceable(8) %2, i64 undef) #13, !dbg !129 - call void @llvm.lifetime.end.p0i8(i64 noundef 8, i8* noundef nonnull %389) #13, !dbg !129 - %404 = add nuw nsw i64 %394, 1, !dbg !129 - %405 = load i64, i64* %65, align 8, !dbg !129, !tbaa !25 - %406 = and i64 %405, 8192, !dbg !129 - %407 = icmp eq i64 %406, 0, !dbg !129 - br i1 %407, label %411, label %408, !dbg !129 + %400 = phi i64* [ %399, %398 ], [ %391, %393 ], !dbg !124 + %401 = getelementptr inbounds i64, i64* %400, i64 %394, !dbg !124 + %402 = load i64, i64* %401, align 8, !dbg !124, !tbaa !6 + store i64 %402, i64* %2, align 8, !dbg !124, !tbaa !6 + %403 = call i64 @"func_.13$block_5"(i64 undef, i64 undef, i32 noundef 1, i64* noalias nocapture noundef nonnull readonly align 8 dereferenceable(8) %2, i64 undef) #13, !dbg !124 + call void @llvm.lifetime.end.p0i8(i64 noundef 8, i8* noundef nonnull %389) #13, !dbg !124 + %404 = add nuw nsw i64 %394, 1, !dbg !124 + %405 = load i64, i64* %65, align 8, !dbg !124, !tbaa !25 + %406 = and i64 %405, 8192, !dbg !124 + %407 = icmp eq i64 %406, 0, !dbg !124 + br i1 %407, label %411, label %408, !dbg !124 408: ; preds = %rb_array_const_ptr_transient.exit.i.i - %409 = lshr i64 %405, 15, !dbg !129 - %410 = and i64 %409, 3, !dbg !129 - br label %rb_array_len.exit.i.i, !dbg !129 + %409 = lshr i64 %405, 15, !dbg !124 + %410 = and i64 %409, 3, !dbg !124 + br label %rb_array_len.exit.i.i, !dbg !124 411: ; preds = %rb_array_const_ptr_transient.exit.i.i - %412 = load i64, i64* %391, align 8, !dbg !129, !tbaa !27 - br label %rb_array_len.exit.i.i, !dbg !129 + %412 = load i64, i64* %391, align 8, !dbg !124, !tbaa !27 + br label %rb_array_len.exit.i.i, !dbg !124 rb_array_len.exit.i.i: ; preds = %411, %408 - %413 = phi i64 [ %410, %408 ], [ %412, %411 ], !dbg !129 - %414 = icmp sgt i64 %413, %404, !dbg !129 - br i1 %414, label %393, label %forward_sorbet_rb_array_each_withBlock.10.exit.i, !dbg !129, !llvm.loop !131 + %413 = phi i64 [ %410, %408 ], [ %412, %411 ], !dbg !124 + %414 = icmp sgt i64 %413, %404, !dbg !124 + br i1 %414, label %393, label %forward_sorbet_rb_array_each_withBlock.10.exit.i, !dbg !124, !llvm.loop !126 forward_sorbet_rb_array_each_withBlock.10.exit.i: ; preds = %rb_array_len.exit.i.i, %rb_array_len.exit1.i.i - call void @sorbet_popFrame() #13, !dbg !129 - call void @llvm.lifetime.end.p0i8(i64 noundef 40, i8* noundef nonnull %358) #13, !dbg !125 - %415 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !125, !tbaa !15 - %416 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %415, i64 0, i32 5, !dbg !125 - %417 = load i32, i32* %416, align 8, !dbg !125, !tbaa !37 - %418 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %415, i64 0, i32 6, !dbg !125 - %419 = load i32, i32* %418, align 4, !dbg !125, !tbaa !38 - %420 = xor i32 %419, -1, !dbg !125 - %421 = and i32 %420, %417, !dbg !125 - %422 = icmp eq i32 %421, 0, !dbg !125 - br i1 %422, label %"func_.13.exit", label %423, !dbg !125, !prof !31 + call void @sorbet_popFrame() #13, !dbg !124 + call void @llvm.lifetime.end.p0i8(i64 noundef 40, i8* noundef nonnull %358) #13, !dbg !120 + %415 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !120, !tbaa !15 + %416 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %415, i64 0, i32 5, !dbg !120 + %417 = load i32, i32* %416, align 8, !dbg !120, !tbaa !37 + %418 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %415, i64 0, i32 6, !dbg !120 + %419 = load i32, i32* %418, align 4, !dbg !120, !tbaa !38 + %420 = xor i32 %419, -1, !dbg !120 + %421 = and i32 %420, %417, !dbg !120 + %422 = icmp eq i32 %421, 0, !dbg !120 + br i1 %422, label %"func_.13.exit", label %423, !dbg !120, !prof !31 423: ; preds = %forward_sorbet_rb_array_each_withBlock.10.exit.i - %424 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %415, i64 0, i32 8, !dbg !125 - %425 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %424, align 8, !dbg !125, !tbaa !39 - %426 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %425, i32 noundef 0) #13, !dbg !125 - br label %"func_.13.exit", !dbg !125 + %424 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %415, i64 0, i32 8, !dbg !120 + %425 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %424, align 8, !dbg !120, !tbaa !39 + %426 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %425, i32 noundef 0) #13, !dbg !120 + br label %"func_.13.exit", !dbg !120 "func_.13.exit": ; preds = %forward_sorbet_rb_array_each_withBlock.10.exit.i, %423 store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 28), i64** %40, align 8, !tbaa !15 @@ -1467,10 +1460,10 @@ forward_sorbet_rb_array_each_withBlock.10.exit.i: ; preds = %rb_array_len.exit.i declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #10 ; Function Attrs: cold minsize noreturn ssp -define internal fastcc void @"func_.13$block_1.cold.1"(i64 %el2.sroa.0.0) unnamed_addr #11 !dbg !132 { +define internal fastcc void @"func_.13$block_1.cold.1"(i64 %el2.sroa.0.0) unnamed_addr #11 !dbg !127 { newFuncRoot: - tail call void @sorbet_cast_failure(i64 %el2.sroa.0.0, i8* getelementptr inbounds ([163 x i8], [163 x i8]* @sorbet_moduleStringTable, i64 0, i64 118), i8* getelementptr inbounds ([163 x i8], [163 x i8]* @sorbet_moduleStringTable, i64 0, i64 124)) #17, !dbg !134 - unreachable, !dbg !134 + tail call void @sorbet_cast_failure(i64 %el2.sroa.0.0, i8* getelementptr inbounds ([163 x i8], [163 x i8]* @sorbet_moduleStringTable, i64 0, i64 118), i8* getelementptr inbounds ([163 x i8], [163 x i8]* @sorbet_moduleStringTable, i64 0, i64 124)) #17, !dbg !129 + unreachable, !dbg !129 } attributes #0 = { nounwind readnone willreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } @@ -1535,98 +1528,93 @@ attributes #17 = { noreturn } !37 = !{!18, !19, i64 40} !38 = !{!18, !19, i64 44} !39 = !{!18, !16, i64 56} -!40 = !DILocation(line: 8, column: 1, scope: !10) -!41 = distinct !DISubprogram(name: ".", linkageName: "func_.13$block_2", scope: !11, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!42 = !{!22, !7, i64 24} -!43 = !DILocation(line: 5, column: 1, scope: !41) -!44 = !DILocation(line: 13, column: 12, scope: !41) -!45 = !DILocation(line: 14, column: 3, scope: !41) -!46 = !DILocation(line: 13, column: 1, scope: !41) -!47 = distinct !DISubprogram(name: ".", linkageName: "func_.13$block_3", scope: !11, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!48 = !DILocation(line: 5, column: 1, scope: !47) -!49 = !DILocation(line: 18, column: 12, scope: !47) -!50 = !DILocation(line: 18, column: 18, scope: !47) -!51 = !DILocation(line: 0, scope: !47) -!52 = !DILocation(line: 19, column: 3, scope: !47) -!53 = !DILocation(line: 18, column: 1, scope: !47) -!54 = distinct !DISubprogram(name: ".", linkageName: "func_.13$block_4", scope: !11, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!55 = !DILocation(line: 5, column: 1, scope: !54) -!56 = !DILocation(line: 23, column: 14, scope: !54) -!57 = !DILocation(line: 23, column: 24, scope: !54) -!58 = !DILocation(line: 23, column: 26, scope: !54) -!59 = !DILocation(line: 0, scope: !54) -!60 = !DILocation(line: 24, column: 3, scope: !54) -!61 = !DILocation(line: 25, column: 3, scope: !54) -!62 = !DILocation(line: 23, column: 1, scope: !54) -!63 = distinct !DISubprogram(name: ".", linkageName: "func_.13$block_5", scope: !11, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!64 = !DILocation(line: 5, column: 1, scope: !63) -!65 = !DILocation(line: 28, column: 15, scope: !63) -!66 = !DILocation(line: 28, column: 17, scope: !63) -!67 = !DILocation(line: 0, scope: !63) -!68 = !DILocation(line: 29, column: 3, scope: !63) -!69 = !DILocation(line: 30, column: 3, scope: !63) -!70 = !DILocation(line: 28, column: 1, scope: !63) -!71 = !DILocation(line: 0, scope: !11) -!72 = !DILocation(line: 5, column: 6, scope: !11) -!73 = !{!74} -!74 = distinct !{!74, !75, !"sorbet_buildArrayIntrinsic: argument 0"} -!75 = distinct !{!75, !"sorbet_buildArrayIntrinsic"} -!76 = !DILocation(line: 5, column: 5, scope: !11) -!77 = !{!78} -!78 = distinct !{!78, !79, !"sorbet_buildArrayIntrinsic: argument 0"} -!79 = distinct !{!79, !"sorbet_buildArrayIntrinsic"} -!80 = !DILocation(line: 8, column: 1, scope: !11) -!81 = !{!82, !7, i64 0} -!82 = !{!"sorbet_inlineIntrinsicEnv", !7, i64 0, !7, i64 8, !19, i64 16, !16, i64 24, !7, i64 32} -!83 = !{!82, !7, i64 8} -!84 = !{!82, !19, i64 16} -!85 = !{!86} -!86 = distinct !{!86, !87, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} -!87 = distinct !{!87, !"VM_BH_FROM_IFUNC_BLOCK"} -!88 = !DILocation(line: 8, column: 1, scope: !11, inlinedAt: !89) -!89 = distinct !DILocation(line: 8, column: 1, scope: !11) -!90 = !{!18, !7, i64 128} -!91 = distinct !{!91, !92} -!92 = !{!"llvm.loop.unroll.disable"} -!93 = !DILocation(line: 13, column: 1, scope: !11) +!40 = distinct !DISubprogram(name: ".", linkageName: "func_.13$block_2", scope: !11, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) +!41 = !{!22, !7, i64 24} +!42 = !DILocation(line: 5, column: 1, scope: !40) +!43 = !DILocation(line: 13, column: 12, scope: !40) +!44 = !DILocation(line: 14, column: 3, scope: !40) +!45 = distinct !DISubprogram(name: ".", linkageName: "func_.13$block_3", scope: !11, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) +!46 = !DILocation(line: 5, column: 1, scope: !45) +!47 = !DILocation(line: 18, column: 12, scope: !45) +!48 = !DILocation(line: 18, column: 18, scope: !45) +!49 = !DILocation(line: 0, scope: !45) +!50 = !DILocation(line: 19, column: 3, scope: !45) +!51 = distinct !DISubprogram(name: ".", linkageName: "func_.13$block_4", scope: !11, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) +!52 = !DILocation(line: 5, column: 1, scope: !51) +!53 = !DILocation(line: 23, column: 14, scope: !51) +!54 = !DILocation(line: 23, column: 24, scope: !51) +!55 = !DILocation(line: 23, column: 26, scope: !51) +!56 = !DILocation(line: 0, scope: !51) +!57 = !DILocation(line: 24, column: 3, scope: !51) +!58 = !DILocation(line: 25, column: 3, scope: !51) +!59 = distinct !DISubprogram(name: ".", linkageName: "func_.13$block_5", scope: !11, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) +!60 = !DILocation(line: 5, column: 1, scope: !59) +!61 = !DILocation(line: 28, column: 15, scope: !59) +!62 = !DILocation(line: 28, column: 17, scope: !59) +!63 = !DILocation(line: 0, scope: !59) +!64 = !DILocation(line: 29, column: 3, scope: !59) +!65 = !DILocation(line: 30, column: 3, scope: !59) +!66 = !DILocation(line: 0, scope: !11) +!67 = !DILocation(line: 5, column: 6, scope: !11) +!68 = !{!69} +!69 = distinct !{!69, !70, !"sorbet_buildArrayIntrinsic: argument 0"} +!70 = distinct !{!70, !"sorbet_buildArrayIntrinsic"} +!71 = !DILocation(line: 5, column: 5, scope: !11) +!72 = !{!73} +!73 = distinct !{!73, !74, !"sorbet_buildArrayIntrinsic: argument 0"} +!74 = distinct !{!74, !"sorbet_buildArrayIntrinsic"} +!75 = !DILocation(line: 8, column: 1, scope: !11) +!76 = !{!77, !7, i64 0} +!77 = !{!"sorbet_inlineIntrinsicEnv", !7, i64 0, !7, i64 8, !19, i64 16, !16, i64 24, !7, i64 32} +!78 = !{!77, !7, i64 8} +!79 = !{!77, !19, i64 16} +!80 = !{!81} +!81 = distinct !{!81, !82, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} +!82 = distinct !{!82, !"VM_BH_FROM_IFUNC_BLOCK"} +!83 = !DILocation(line: 8, column: 1, scope: !11, inlinedAt: !84) +!84 = distinct !DILocation(line: 8, column: 1, scope: !11) +!85 = !{!18, !7, i64 128} +!86 = distinct !{!86, !87} +!87 = !{!"llvm.loop.unroll.disable"} +!88 = !DILocation(line: 13, column: 1, scope: !11) +!89 = !{!90} +!90 = distinct !{!90, !91, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} +!91 = distinct !{!91, !"VM_BH_FROM_IFUNC_BLOCK"} +!92 = !DILocation(line: 13, column: 1, scope: !11, inlinedAt: !93) +!93 = distinct !DILocation(line: 13, column: 1, scope: !11) !94 = !{!95} -!95 = distinct !{!95, !96, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} -!96 = distinct !{!96, !"VM_BH_FROM_IFUNC_BLOCK"} -!97 = !DILocation(line: 13, column: 1, scope: !11, inlinedAt: !98) -!98 = distinct !DILocation(line: 13, column: 1, scope: !11) -!99 = !{!100} -!100 = distinct !{!100, !101, !"func_.13$block_2: %argArray"} -!101 = distinct !{!101, !"func_.13$block_2"} -!102 = !DILocation(line: 13, column: 12, scope: !41, inlinedAt: !103) -!103 = distinct !DILocation(line: 13, column: 1, scope: !11, inlinedAt: !98) -!104 = !DILocation(line: 14, column: 3, scope: !41, inlinedAt: !103) -!105 = distinct !{!105, !92} -!106 = !DILocation(line: 18, column: 1, scope: !11) +!95 = distinct !{!95, !96, !"func_.13$block_2: %argArray"} +!96 = distinct !{!96, !"func_.13$block_2"} +!97 = !DILocation(line: 13, column: 12, scope: !40, inlinedAt: !98) +!98 = distinct !DILocation(line: 13, column: 1, scope: !11, inlinedAt: !93) +!99 = !DILocation(line: 14, column: 3, scope: !40, inlinedAt: !98) +!100 = distinct !{!100, !87} +!101 = !DILocation(line: 18, column: 1, scope: !11) +!102 = !{!103} +!103 = distinct !{!103, !104, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} +!104 = distinct !{!104, !"VM_BH_FROM_IFUNC_BLOCK"} +!105 = !DILocation(line: 18, column: 1, scope: !11, inlinedAt: !106) +!106 = distinct !DILocation(line: 18, column: 1, scope: !11) !107 = !{!108} -!108 = distinct !{!108, !109, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} -!109 = distinct !{!109, !"VM_BH_FROM_IFUNC_BLOCK"} -!110 = !DILocation(line: 18, column: 1, scope: !11, inlinedAt: !111) -!111 = distinct !DILocation(line: 18, column: 1, scope: !11) -!112 = !{!113} -!113 = distinct !{!113, !114, !"func_.13$block_3: %argArray"} -!114 = distinct !{!114, !"func_.13$block_3"} -!115 = !DILocation(line: 19, column: 3, scope: !47, inlinedAt: !116) -!116 = distinct !DILocation(line: 18, column: 1, scope: !11, inlinedAt: !111) -!117 = distinct !{!117, !92} -!118 = !DILocation(line: 23, column: 1, scope: !11) -!119 = !{!120} -!120 = distinct !{!120, !121, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} -!121 = distinct !{!121, !"VM_BH_FROM_IFUNC_BLOCK"} -!122 = !DILocation(line: 23, column: 1, scope: !11, inlinedAt: !123) -!123 = distinct !DILocation(line: 23, column: 1, scope: !11) -!124 = distinct !{!124, !92} -!125 = !DILocation(line: 28, column: 1, scope: !11) -!126 = !{!127} -!127 = distinct !{!127, !128, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} -!128 = distinct !{!128, !"VM_BH_FROM_IFUNC_BLOCK"} -!129 = !DILocation(line: 28, column: 1, scope: !11, inlinedAt: !130) -!130 = distinct !DILocation(line: 28, column: 1, scope: !11) -!131 = distinct !{!131, !92} -!132 = distinct !DISubprogram(name: "func_.13$block_1.cold.1", linkageName: "func_.13$block_1.cold.1", scope: null, file: !4, type: !133, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) -!133 = !DISubroutineType(types: !5) -!134 = !DILocation(line: 9, column: 25, scope: !132) +!108 = distinct !{!108, !109, !"func_.13$block_3: %argArray"} +!109 = distinct !{!109, !"func_.13$block_3"} +!110 = !DILocation(line: 19, column: 3, scope: !45, inlinedAt: !111) +!111 = distinct !DILocation(line: 18, column: 1, scope: !11, inlinedAt: !106) +!112 = distinct !{!112, !87} +!113 = !DILocation(line: 23, column: 1, scope: !11) +!114 = !{!115} +!115 = distinct !{!115, !116, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} +!116 = distinct !{!116, !"VM_BH_FROM_IFUNC_BLOCK"} +!117 = !DILocation(line: 23, column: 1, scope: !11, inlinedAt: !118) +!118 = distinct !DILocation(line: 23, column: 1, scope: !11) +!119 = distinct !{!119, !87} +!120 = !DILocation(line: 28, column: 1, scope: !11) +!121 = !{!122} +!122 = distinct !{!122, !123, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} +!123 = distinct !{!123, !"VM_BH_FROM_IFUNC_BLOCK"} +!124 = !DILocation(line: 28, column: 1, scope: !11, inlinedAt: !125) +!125 = distinct !DILocation(line: 28, column: 1, scope: !11) +!126 = distinct !{!126, !87} +!127 = distinct !DISubprogram(name: "func_.13$block_1.cold.1", linkageName: "func_.13$block_1.cold.1", scope: null, file: !4, type: !128, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) +!128 = !DISubroutineType(types: !5) +!129 = !DILocation(line: 9, column: 25, scope: !127) diff --git a/test/testdata/compiler/block_args.opt.ll.exp b/test/testdata/compiler/block_args.opt.ll.exp index 3596459bca..c8f2e64858 100644 --- a/test/testdata/compiler/block_args.opt.ll.exp +++ b/test/testdata/compiler/block_args.opt.ll.exp @@ -2,16 +2,17 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -27,34 +28,34 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.rb_fiber_struct = type opaque -%struct.anon.3 = type { [65 x i64] } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -68,21 +69,20 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } +%struct.anon.6 = type { i64, i64, i64, i64 } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } %struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } %struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } %struct.rb_call_info_kw_arg = type { i32, [1 x i64] } -%struct.rb_captured_block = type { i64, i64*, %union.anon.17 } -%union.anon.17 = type { %struct.rb_iseq_struct* } +%struct.rb_captured_block = type { i64, i64*, %union.anon.20 } +%union.anon.20 = type { %struct.rb_iseq_struct* } %struct.vm_ifunc = type { i64, i64, i64 (i64, i64, i32, i64*, i64)*, i8*, %struct.rb_code_position_struct } %struct.iseq_inline_iv_cache_entry = type { i64, i64 } %struct.sorbet_inlineIntrinsicEnv = type { i64, i64, i32, i64*, i64 } -%struct.RArray = type { %struct.iseq_inline_iv_cache_entry, %union.anon.25 } -%union.anon.25 = type { %struct.anon.26 } -%struct.anon.26 = type { i64, %union.anon.27, i64* } -%union.anon.27 = type { i64 } +%struct.RArray = type { %struct.iseq_inline_iv_cache_entry, %union.anon.28 } +%union.anon.28 = type { %struct.anon.29 } +%struct.anon.29 = type { i64, %union.anon, i64* } @ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 @ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 @@ -213,7 +213,7 @@ sorbet_isa_Integer.exit: ; preds = %fillFromDefaultBloc afterSend: ; preds = %46, %sorbet_rb_int_plus.exit, %"alternativeCallIntrinsic_Integer_+" %"symIntrinsicRawPhi_+" = phi i64 [ %send, %"alternativeCallIntrinsic_Integer_+" ], [ %37, %sorbet_rb_int_plus.exit ], [ %37, %46 ], !dbg !26 - ret i64 %"symIntrinsicRawPhi_+", !dbg !30 + ret i64 %"symIntrinsicRawPhi_+", !dbg !26 "alternativeCallIntrinsic_Integer_+": ; preds = %fillFromDefaultBlockDone1.thread, %sorbet_isa_Integer.exit %x.sroa.0.018 = phi i64 [ %x.sroa.0.017, %fillFromDefaultBlockDone1.thread ], [ %x.sroa.0.017, %sorbet_isa_Integer.exit ] @@ -229,10 +229,10 @@ afterSend: ; preds = %46, %sorbet_rb_int_ "fastSymCallIntrinsic_Integer_+": ; preds = %fillFromDefaultBlockDone1, %sorbet_isa_Integer.exit %x.sroa.0.016 = phi i64 [ %rawArg_x, %fillFromDefaultBlockDone1 ], [ %x.sroa.0.017, %sorbet_isa_Integer.exit ] - tail call void @llvm.experimental.noalias.scope.decl(metadata !31), !dbg !26 + tail call void @llvm.experimental.noalias.scope.decl(metadata !30), !dbg !26 %25 = and i64 %x.sroa.0.016, 1, !dbg !26 %26 = icmp eq i64 %25, 0, !dbg !26 - br i1 %26, label %35, label %27, !dbg !26, !prof !34 + br i1 %26, label %35, label %27, !dbg !26, !prof !33 27: ; preds = %"fastSymCallIntrinsic_Integer_+" %28 = tail call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %x.sroa.0.016, i64 noundef 2) #10, !dbg !26 @@ -247,16 +247,16 @@ afterSend: ; preds = %46, %sorbet_rb_int_ br label %sorbet_rb_int_plus.exit, !dbg !26 35: ; preds = %"fastSymCallIntrinsic_Integer_+" - %36 = tail call i64 @sorbet_rb_int_plus_slowpath(i64 %x.sroa.0.016, i64 noundef 3) #11, !dbg !26, !noalias !31 + %36 = tail call i64 @sorbet_rb_int_plus_slowpath(i64 %x.sroa.0.016, i64 noundef 3) #11, !dbg !26, !noalias !30 br label %sorbet_rb_int_plus.exit, !dbg !26 sorbet_rb_int_plus.exit: ; preds = %31, %27, %35 %37 = phi i64 [ %36, %35 ], [ %34, %31 ], [ %30, %27 ], !dbg !26 %38 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !26, !tbaa !15 %39 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %38, i64 0, i32 5, !dbg !26 - %40 = load i32, i32* %39, align 8, !dbg !26, !tbaa !35 + %40 = load i32, i32* %39, align 8, !dbg !26, !tbaa !34 %41 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %38, i64 0, i32 6, !dbg !26 - %42 = load i32, i32* %41, align 4, !dbg !26, !tbaa !36 + %42 = load i32, i32* %41, align 4, !dbg !26, !tbaa !35 %43 = xor i32 %42, -1, !dbg !26 %44 = and i32 %43, %40, !dbg !26 %45 = icmp eq i32 %44, 0, !dbg !26 @@ -264,7 +264,7 @@ sorbet_rb_int_plus.exit: ; preds = %31, %27, %35 46: ; preds = %sorbet_rb_int_plus.exit %47 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %38, i64 0, i32 8, !dbg !26 - %48 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %47, align 8, !dbg !26, !tbaa !37 + %48 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %47, align 8, !dbg !26, !tbaa !36 %49 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %48, i32 noundef 0) #11, !dbg !26 br label %afterSend, !dbg !26 } @@ -295,11 +295,11 @@ entry: store i64 %6, i64* @"func_.13$block_1_ifunc", align 8 %"rubyId_+.i" = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !dbg !26, !invariant.load !5 call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_+", i64 %"rubyId_+.i", i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !26 - %rubyId_puts.i = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i64 0, i64 5), align 8, !dbg !38, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !38 + %rubyId_puts.i = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i64 0, i64 5), align 8, !dbg !37, !invariant.load !5 + call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !37 %7 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !15 %8 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %7, i64 0, i32 18 - %9 = load i64, i64* %8, align 8, !tbaa !39 + %9 = load i64, i64* %8, align 8, !tbaa !38 %10 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 %11 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %10, i64 0, i32 2 %12 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %11, align 8, !tbaa !17 @@ -316,160 +316,160 @@ entry: store i64 %19, i64* %17, align 8, !tbaa !6 call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %10, %struct.rb_control_frame_struct* %14, %struct.rb_iseq_struct* %stackFrame.i) #11 %20 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %12, i64 0, i32 0 - store i64* getelementptr inbounds ([5 x i64], [5 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %20, align 8, !dbg !48, !tbaa !15 - %callArgs0Addr.i = getelementptr [3 x i64], [3 x i64]* %callArgs.i, i64 0, i64 0, !dbg !49 - %21 = bitcast i64* %callArgs0Addr.i to <2 x i64>*, !dbg !49 - store <2 x i64> , <2 x i64>* %21, align 8, !dbg !49 - call void @llvm.experimental.noalias.scope.decl(metadata !50) #11, !dbg !49 - %22 = call i64 @rb_ary_new_from_values(i64 noundef 2, i64* noundef nonnull align 8 %callArgs0Addr.i) #11, !dbg !49 - %rubyId_map.i = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !dbg !49, !invariant.load !5 - %23 = load i64, i64* @"func_.13$block_1_ifunc", align 8, !dbg !49 - %24 = call %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64 %23) #11, !dbg !49 - %25 = bitcast %struct.sorbet_inlineIntrinsicEnv* %1 to i8*, !dbg !49 - call void @llvm.lifetime.start.p0i8(i64 noundef 40, i8* noundef nonnull %25) #11, !dbg !49 - %26 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %1, i64 0, i32 0, !dbg !49 - store i64 %22, i64* %26, align 8, !dbg !49, !tbaa !53 - %27 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %1, i64 0, i32 1, !dbg !49 - store i64 %rubyId_map.i, i64* %27, align 8, !dbg !49, !tbaa !55 - %28 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %1, i64 0, i32 2, !dbg !49 - store i32 0, i32* %28, align 8, !dbg !49, !tbaa !56 - %29 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %1, i64 0, i32 3, !dbg !49 - %30 = bitcast i64** %29 to i8*, !dbg !49 - call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %30, i8 0, i64 16, i1 false) #11, !dbg !49 - %31 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !49, !tbaa !15 - %32 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %31, i64 0, i32 2, !dbg !49 - %33 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %32, align 8, !dbg !49, !tbaa !17 - %34 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %33, i64 0, i32 3, !dbg !49 - %35 = bitcast i64* %34 to %struct.rb_captured_block*, !dbg !49 - %36 = getelementptr inbounds i64, i64* %34, i64 2, !dbg !49 - %37 = bitcast i64* %36 to %struct.vm_ifunc**, !dbg !49 - store %struct.vm_ifunc* %24, %struct.vm_ifunc** %37, align 8, !dbg !49, !tbaa !57 - call void @llvm.experimental.noalias.scope.decl(metadata !58) #11, !dbg !49 - %38 = ptrtoint %struct.rb_captured_block* %35 to i64, !dbg !49 - %39 = or i64 %38, 3, !dbg !49 - %40 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %31, i64 0, i32 17, !dbg !49 - %41 = and i64 %39, -4, !dbg !61 - %42 = inttoptr i64 %41 to %struct.rb_captured_block*, !dbg !61 - store i64 0, i64* %40, align 8, !dbg !61, !tbaa !63 - call void @sorbet_pushBlockFrame(%struct.rb_captured_block* %42) #11, !dbg !61 - %43 = inttoptr i64 %22 to %struct.iseq_inline_iv_cache_entry*, !dbg !61 - %44 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %43, i64 0, i32 0, !dbg !61 - %45 = load i64, i64* %44, align 8, !dbg !61, !tbaa !27 - %46 = and i64 %45, 8192, !dbg !61 - %47 = icmp eq i64 %46, 0, !dbg !61 - br i1 %47, label %51, label %48, !dbg !61 + store i64* getelementptr inbounds ([5 x i64], [5 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %20, align 8, !dbg !47, !tbaa !15 + %callArgs0Addr.i = getelementptr [3 x i64], [3 x i64]* %callArgs.i, i64 0, i64 0, !dbg !48 + %21 = bitcast i64* %callArgs0Addr.i to <2 x i64>*, !dbg !48 + store <2 x i64> , <2 x i64>* %21, align 8, !dbg !48 + call void @llvm.experimental.noalias.scope.decl(metadata !49) #11, !dbg !48 + %22 = call i64 @rb_ary_new_from_values(i64 noundef 2, i64* noundef nonnull align 8 %callArgs0Addr.i) #11, !dbg !48 + %rubyId_map.i = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !dbg !48, !invariant.load !5 + %23 = load i64, i64* @"func_.13$block_1_ifunc", align 8, !dbg !48 + %24 = call %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64 %23) #11, !dbg !48 + %25 = bitcast %struct.sorbet_inlineIntrinsicEnv* %1 to i8*, !dbg !48 + call void @llvm.lifetime.start.p0i8(i64 noundef 40, i8* noundef nonnull %25) #11, !dbg !48 + %26 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %1, i64 0, i32 0, !dbg !48 + store i64 %22, i64* %26, align 8, !dbg !48, !tbaa !52 + %27 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %1, i64 0, i32 1, !dbg !48 + store i64 %rubyId_map.i, i64* %27, align 8, !dbg !48, !tbaa !54 + %28 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %1, i64 0, i32 2, !dbg !48 + store i32 0, i32* %28, align 8, !dbg !48, !tbaa !55 + %29 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %1, i64 0, i32 3, !dbg !48 + %30 = bitcast i64** %29 to i8*, !dbg !48 + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %30, i8 0, i64 16, i1 false) #11, !dbg !48 + %31 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !48, !tbaa !15 + %32 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %31, i64 0, i32 2, !dbg !48 + %33 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %32, align 8, !dbg !48, !tbaa !17 + %34 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %33, i64 0, i32 3, !dbg !48 + %35 = bitcast i64* %34 to %struct.rb_captured_block*, !dbg !48 + %36 = getelementptr inbounds i64, i64* %34, i64 2, !dbg !48 + %37 = bitcast i64* %36 to %struct.vm_ifunc**, !dbg !48 + store %struct.vm_ifunc* %24, %struct.vm_ifunc** %37, align 8, !dbg !48, !tbaa !56 + call void @llvm.experimental.noalias.scope.decl(metadata !57) #11, !dbg !48 + %38 = ptrtoint %struct.rb_captured_block* %35 to i64, !dbg !48 + %39 = or i64 %38, 3, !dbg !48 + %40 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %31, i64 0, i32 17, !dbg !48 + %41 = and i64 %39, -4, !dbg !60 + %42 = inttoptr i64 %41 to %struct.rb_captured_block*, !dbg !60 + store i64 0, i64* %40, align 8, !dbg !60, !tbaa !62 + call void @sorbet_pushBlockFrame(%struct.rb_captured_block* %42) #11, !dbg !60 + %43 = inttoptr i64 %22 to %struct.iseq_inline_iv_cache_entry*, !dbg !60 + %44 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %43, i64 0, i32 0, !dbg !60 + %45 = load i64, i64* %44, align 8, !dbg !60, !tbaa !27 + %46 = and i64 %45, 8192, !dbg !60 + %47 = icmp eq i64 %46, 0, !dbg !60 + br i1 %47, label %51, label %48, !dbg !60 48: ; preds = %entry - %49 = lshr i64 %45, 15, !dbg !61 - %50 = and i64 %49, 3, !dbg !61 - br label %rb_array_len.exit2.i.i, !dbg !61 + %49 = lshr i64 %45, 15, !dbg !60 + %50 = and i64 %49, 3, !dbg !60 + br label %rb_array_len.exit2.i.i, !dbg !60 51: ; preds = %entry - %52 = inttoptr i64 %22 to %struct.RArray*, !dbg !61 - %53 = getelementptr inbounds %struct.RArray, %struct.RArray* %52, i64 0, i32 1, i32 0, i32 0, !dbg !61 - %54 = load i64, i64* %53, align 8, !dbg !61, !tbaa !57 - br label %rb_array_len.exit2.i.i, !dbg !61 + %52 = inttoptr i64 %22 to %struct.RArray*, !dbg !60 + %53 = getelementptr inbounds %struct.RArray, %struct.RArray* %52, i64 0, i32 1, i32 0, i32 0, !dbg !60 + %54 = load i64, i64* %53, align 8, !dbg !60, !tbaa !56 + br label %rb_array_len.exit2.i.i, !dbg !60 rb_array_len.exit2.i.i: ; preds = %51, %48 - %55 = phi i64 [ %50, %48 ], [ %54, %51 ], !dbg !61 - %56 = call i64 @rb_ary_new_capa(i64 %55) #11, !dbg !61 - %57 = load i64, i64* %44, align 8, !dbg !61, !tbaa !27 - %58 = and i64 %57, 8192, !dbg !61 - %59 = icmp eq i64 %58, 0, !dbg !61 - br i1 %59, label %63, label %60, !dbg !61 + %55 = phi i64 [ %50, %48 ], [ %54, %51 ], !dbg !60 + %56 = call i64 @rb_ary_new_capa(i64 %55) #11, !dbg !60 + %57 = load i64, i64* %44, align 8, !dbg !60, !tbaa !27 + %58 = and i64 %57, 8192, !dbg !60 + %59 = icmp eq i64 %58, 0, !dbg !60 + br i1 %59, label %63, label %60, !dbg !60 60: ; preds = %rb_array_len.exit2.i.i - %61 = lshr i64 %57, 15, !dbg !61 - %62 = and i64 %61, 3, !dbg !61 - br label %rb_array_len.exit1.i.i, !dbg !61 + %61 = lshr i64 %57, 15, !dbg !60 + %62 = and i64 %61, 3, !dbg !60 + br label %rb_array_len.exit1.i.i, !dbg !60 63: ; preds = %rb_array_len.exit2.i.i - %64 = inttoptr i64 %22 to %struct.RArray*, !dbg !61 - %65 = getelementptr inbounds %struct.RArray, %struct.RArray* %64, i64 0, i32 1, i32 0, i32 0, !dbg !61 - %66 = load i64, i64* %65, align 8, !dbg !61, !tbaa !57 - br label %rb_array_len.exit1.i.i, !dbg !61 + %64 = inttoptr i64 %22 to %struct.RArray*, !dbg !60 + %65 = getelementptr inbounds %struct.RArray, %struct.RArray* %64, i64 0, i32 1, i32 0, i32 0, !dbg !60 + %66 = load i64, i64* %65, align 8, !dbg !60, !tbaa !56 + br label %rb_array_len.exit1.i.i, !dbg !60 rb_array_len.exit1.i.i: ; preds = %63, %60 - %67 = phi i64 [ %62, %60 ], [ %66, %63 ], !dbg !61 - %68 = icmp sgt i64 %67, 0, !dbg !61 - br i1 %68, label %69, label %forward_sorbet_rb_array_collect_withBlock.exit.i, !dbg !61 + %67 = phi i64 [ %62, %60 ], [ %66, %63 ], !dbg !60 + %68 = icmp sgt i64 %67, 0, !dbg !60 + br i1 %68, label %69, label %forward_sorbet_rb_array_collect_withBlock.exit.i, !dbg !60 69: ; preds = %rb_array_len.exit1.i.i - %70 = bitcast i64* %0 to i8*, !dbg !61 - %71 = inttoptr i64 %22 to %struct.RArray*, !dbg !49 - %72 = getelementptr inbounds %struct.RArray, %struct.RArray* %71, i64 0, i32 1, i32 0, i32 0, !dbg !49 - %73 = getelementptr inbounds %struct.RArray, %struct.RArray* %71, i64 0, i32 1, i32 0, i32 2, !dbg !49 - br label %74, !dbg !61 + %70 = bitcast i64* %0 to i8*, !dbg !60 + %71 = inttoptr i64 %22 to %struct.RArray*, !dbg !48 + %72 = getelementptr inbounds %struct.RArray, %struct.RArray* %71, i64 0, i32 1, i32 0, i32 0, !dbg !48 + %73 = getelementptr inbounds %struct.RArray, %struct.RArray* %71, i64 0, i32 1, i32 0, i32 2, !dbg !48 + br label %74, !dbg !60 74: ; preds = %rb_array_len.exit.i.i, %69 - %75 = phi i64 [ 0, %69 ], [ %86, %rb_array_len.exit.i.i ], !dbg !61 - call void @llvm.lifetime.start.p0i8(i64 noundef 8, i8* noundef nonnull align 8 dereferenceable(8) %70) #11, !dbg !61 - %76 = load i64, i64* %44, align 8, !dbg !61, !tbaa !27 - %77 = and i64 %76, 8192, !dbg !61 - %78 = icmp eq i64 %77, 0, !dbg !61 - br i1 %78, label %79, label %rb_array_const_ptr_transient.exit.i.i, !dbg !61 + %75 = phi i64 [ 0, %69 ], [ %86, %rb_array_len.exit.i.i ], !dbg !60 + call void @llvm.lifetime.start.p0i8(i64 noundef 8, i8* noundef nonnull align 8 dereferenceable(8) %70) #11, !dbg !60 + %76 = load i64, i64* %44, align 8, !dbg !60, !tbaa !27 + %77 = and i64 %76, 8192, !dbg !60 + %78 = icmp eq i64 %77, 0, !dbg !60 + br i1 %78, label %79, label %rb_array_const_ptr_transient.exit.i.i, !dbg !60 79: ; preds = %74 - %80 = load i64*, i64** %73, align 8, !dbg !61, !tbaa !57 - br label %rb_array_const_ptr_transient.exit.i.i, !dbg !61 + %80 = load i64*, i64** %73, align 8, !dbg !60, !tbaa !56 + br label %rb_array_const_ptr_transient.exit.i.i, !dbg !60 rb_array_const_ptr_transient.exit.i.i: ; preds = %79, %74 - %81 = phi i64* [ %80, %79 ], [ %72, %74 ], !dbg !61 - %82 = getelementptr inbounds i64, i64* %81, i64 %75, !dbg !61 - %83 = load i64, i64* %82, align 8, !dbg !61, !tbaa !6 - store i64 %83, i64* %0, align 8, !dbg !61, !tbaa !6 - %84 = call i64 @"func_.13$block_1"(i64 undef, i64 undef, i32 noundef 1, i64* noalias nocapture noundef nonnull readonly align 8 dereferenceable(8) %0, i64 undef) #11, !dbg !61 - %85 = call i64 @rb_ary_push(i64 %56, i64 %84) #11, !dbg !61 - call void @llvm.lifetime.end.p0i8(i64 noundef 8, i8* noundef nonnull %70) #11, !dbg !61 - %86 = add nuw nsw i64 %75, 1, !dbg !61 - %87 = load i64, i64* %44, align 8, !dbg !61, !tbaa !27 - %88 = and i64 %87, 8192, !dbg !61 - %89 = icmp eq i64 %88, 0, !dbg !61 - br i1 %89, label %93, label %90, !dbg !61 + %81 = phi i64* [ %80, %79 ], [ %72, %74 ], !dbg !60 + %82 = getelementptr inbounds i64, i64* %81, i64 %75, !dbg !60 + %83 = load i64, i64* %82, align 8, !dbg !60, !tbaa !6 + store i64 %83, i64* %0, align 8, !dbg !60, !tbaa !6 + %84 = call i64 @"func_.13$block_1"(i64 undef, i64 undef, i32 noundef 1, i64* noalias nocapture noundef nonnull readonly align 8 dereferenceable(8) %0, i64 undef) #11, !dbg !60 + %85 = call i64 @rb_ary_push(i64 %56, i64 %84) #11, !dbg !60 + call void @llvm.lifetime.end.p0i8(i64 noundef 8, i8* noundef nonnull %70) #11, !dbg !60 + %86 = add nuw nsw i64 %75, 1, !dbg !60 + %87 = load i64, i64* %44, align 8, !dbg !60, !tbaa !27 + %88 = and i64 %87, 8192, !dbg !60 + %89 = icmp eq i64 %88, 0, !dbg !60 + br i1 %89, label %93, label %90, !dbg !60 90: ; preds = %rb_array_const_ptr_transient.exit.i.i - %91 = lshr i64 %87, 15, !dbg !61 - %92 = and i64 %91, 3, !dbg !61 - br label %rb_array_len.exit.i.i, !dbg !61 + %91 = lshr i64 %87, 15, !dbg !60 + %92 = and i64 %91, 3, !dbg !60 + br label %rb_array_len.exit.i.i, !dbg !60 93: ; preds = %rb_array_const_ptr_transient.exit.i.i - %94 = load i64, i64* %72, align 8, !dbg !61, !tbaa !57 - br label %rb_array_len.exit.i.i, !dbg !61 + %94 = load i64, i64* %72, align 8, !dbg !60, !tbaa !56 + br label %rb_array_len.exit.i.i, !dbg !60 rb_array_len.exit.i.i: ; preds = %93, %90 - %95 = phi i64 [ %92, %90 ], [ %94, %93 ], !dbg !61 - %96 = icmp slt i64 %86, %95, !dbg !61 - br i1 %96, label %74, label %forward_sorbet_rb_array_collect_withBlock.exit.i, !dbg !61, !llvm.loop !64 + %95 = phi i64 [ %92, %90 ], [ %94, %93 ], !dbg !60 + %96 = icmp slt i64 %86, %95, !dbg !60 + br i1 %96, label %74, label %forward_sorbet_rb_array_collect_withBlock.exit.i, !dbg !60, !llvm.loop !63 forward_sorbet_rb_array_collect_withBlock.exit.i: ; preds = %rb_array_len.exit.i.i, %rb_array_len.exit1.i.i - call void @sorbet_popFrame() #11, !dbg !61 - call void @llvm.lifetime.end.p0i8(i64 noundef 40, i8* noundef nonnull %25) #11, !dbg !49 - %97 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !49, !tbaa !15 - %98 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %97, i64 0, i32 5, !dbg !49 - %99 = load i32, i32* %98, align 8, !dbg !49, !tbaa !35 - %100 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %97, i64 0, i32 6, !dbg !49 - %101 = load i32, i32* %100, align 4, !dbg !49, !tbaa !36 - %102 = xor i32 %101, -1, !dbg !49 - %103 = and i32 %102, %99, !dbg !49 - %104 = icmp eq i32 %103, 0, !dbg !49 - br i1 %104, label %"func_.13.exit", label %105, !dbg !49, !prof !29 + call void @sorbet_popFrame() #11, !dbg !60 + call void @llvm.lifetime.end.p0i8(i64 noundef 40, i8* noundef nonnull %25) #11, !dbg !48 + %97 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !48, !tbaa !15 + %98 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %97, i64 0, i32 5, !dbg !48 + %99 = load i32, i32* %98, align 8, !dbg !48, !tbaa !34 + %100 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %97, i64 0, i32 6, !dbg !48 + %101 = load i32, i32* %100, align 4, !dbg !48, !tbaa !35 + %102 = xor i32 %101, -1, !dbg !48 + %103 = and i32 %102, %99, !dbg !48 + %104 = icmp eq i32 %103, 0, !dbg !48 + br i1 %104, label %"func_.13.exit", label %105, !dbg !48, !prof !29 105: ; preds = %forward_sorbet_rb_array_collect_withBlock.exit.i - %106 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %97, i64 0, i32 8, !dbg !49 - %107 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %106, align 8, !dbg !49, !tbaa !37 - %108 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %107, i32 noundef 0) #11, !dbg !49 - br label %"func_.13.exit", !dbg !49 + %106 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %97, i64 0, i32 8, !dbg !48 + %107 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %106, align 8, !dbg !48, !tbaa !36 + %108 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %107, i32 noundef 0) #11, !dbg !48 + br label %"func_.13.exit", !dbg !48 "func_.13.exit": ; preds = %forward_sorbet_rb_array_collect_withBlock.exit.i, %105 store i64* getelementptr inbounds ([5 x i64], [5 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %20, align 8, !tbaa !15 - %109 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %12, i64 0, i32 1, !dbg !38 - %110 = load i64*, i64** %109, align 8, !dbg !38 - store i64 %9, i64* %110, align 8, !dbg !38, !tbaa !6 - %111 = getelementptr inbounds i64, i64* %110, i64 1, !dbg !38 - store i64 %56, i64* %111, align 8, !dbg !38, !tbaa !6 - %112 = getelementptr inbounds i64, i64* %111, i64 1, !dbg !38 - store i64* %112, i64** %109, align 8, !dbg !38 - %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !38 + %109 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %12, i64 0, i32 1, !dbg !37 + %110 = load i64*, i64** %109, align 8, !dbg !37 + store i64 %9, i64* %110, align 8, !dbg !37, !tbaa !6 + %111 = getelementptr inbounds i64, i64* %110, i64 1, !dbg !37 + store i64 %56, i64* %111, align 8, !dbg !37, !tbaa !6 + %112 = getelementptr inbounds i64, i64* %111, i64 1, !dbg !37 + store i64* %112, i64** %109, align 8, !dbg !37 + %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !37 call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %13) ret void } @@ -523,39 +523,38 @@ attributes #11 = { nounwind } !27 = !{!28, !7, i64 0} !28 = !{!"RBasic", !7, i64 0, !7, i64 8} !29 = !{!"branch_weights", i32 2000, i32 1} -!30 = !DILocation(line: 4, column: 6, scope: !10) -!31 = !{!32} -!32 = distinct !{!32, !33, !"sorbet_rb_int_plus: argument 0"} -!33 = distinct !{!33, !"sorbet_rb_int_plus"} -!34 = !{!"branch_weights", i32 4001, i32 4000000} -!35 = !{!18, !19, i64 40} -!36 = !{!18, !19, i64 44} -!37 = !{!18, !16, i64 56} -!38 = !DILocation(line: 4, column: 1, scope: !11) -!39 = !{!40, !7, i64 400} -!40 = !{!"rb_vm_struct", !7, i64 0, !41, i64 8, !16, i64 192, !16, i64 200, !16, i64 208, !44, i64 216, !8, i64 224, !42, i64 264, !42, i64 280, !42, i64 296, !42, i64 312, !7, i64 328, !19, i64 336, !19, i64 340, !19, i64 344, !19, i64 344, !19, i64 344, !19, i64 344, !19, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !16, i64 456, !16, i64 464, !45, i64 472, !46, i64 992, !16, i64 1016, !16, i64 1024, !19, i64 1032, !19, i64 1036, !42, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !19, i64 1136, !16, i64 1144, !16, i64 1152, !16, i64 1160, !16, i64 1168, !16, i64 1176, !16, i64 1184, !19, i64 1192, !47, i64 1200, !8, i64 1232} -!41 = !{!"rb_global_vm_lock_struct", !16, i64 0, !8, i64 8, !42, i64 48, !16, i64 64, !19, i64 72, !8, i64 80, !8, i64 128, !19, i64 176, !19, i64 180} -!42 = !{!"list_head", !43, i64 0} -!43 = !{!"list_node", !16, i64 0, !16, i64 8} -!44 = !{!"long long", !8, i64 0} -!45 = !{!"", !8, i64 0} -!46 = !{!"rb_hook_list_struct", !16, i64 0, !19, i64 8, !19, i64 12, !19, i64 16} -!47 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!48 = !DILocation(line: 0, scope: !11) -!49 = !DILocation(line: 4, column: 6, scope: !11) -!50 = !{!51} -!51 = distinct !{!51, !52, !"sorbet_buildArrayIntrinsic: argument 0"} -!52 = distinct !{!52, !"sorbet_buildArrayIntrinsic"} -!53 = !{!54, !7, i64 0} -!54 = !{!"sorbet_inlineIntrinsicEnv", !7, i64 0, !7, i64 8, !19, i64 16, !16, i64 24, !7, i64 32} -!55 = !{!54, !7, i64 8} -!56 = !{!54, !19, i64 16} -!57 = !{!8, !8, i64 0} -!58 = !{!59} -!59 = distinct !{!59, !60, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} -!60 = distinct !{!60, !"VM_BH_FROM_IFUNC_BLOCK"} -!61 = !DILocation(line: 4, column: 6, scope: !11, inlinedAt: !62) -!62 = distinct !DILocation(line: 4, column: 6, scope: !11) -!63 = !{!18, !7, i64 128} -!64 = distinct !{!64, !65} -!65 = !{!"llvm.loop.unroll.disable"} +!30 = !{!31} +!31 = distinct !{!31, !32, !"sorbet_rb_int_plus: argument 0"} +!32 = distinct !{!32, !"sorbet_rb_int_plus"} +!33 = !{!"branch_weights", i32 4001, i32 4000000} +!34 = !{!18, !19, i64 40} +!35 = !{!18, !19, i64 44} +!36 = !{!18, !16, i64 56} +!37 = !DILocation(line: 4, column: 1, scope: !11) +!38 = !{!39, !7, i64 400} +!39 = !{!"rb_vm_struct", !7, i64 0, !40, i64 8, !16, i64 192, !16, i64 200, !16, i64 208, !43, i64 216, !8, i64 224, !41, i64 264, !41, i64 280, !41, i64 296, !41, i64 312, !7, i64 328, !19, i64 336, !19, i64 340, !19, i64 344, !19, i64 344, !19, i64 344, !19, i64 344, !19, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !16, i64 456, !16, i64 464, !44, i64 472, !45, i64 992, !16, i64 1016, !16, i64 1024, !19, i64 1032, !19, i64 1036, !41, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !19, i64 1136, !16, i64 1144, !16, i64 1152, !16, i64 1160, !16, i64 1168, !16, i64 1176, !16, i64 1184, !19, i64 1192, !46, i64 1200, !8, i64 1232} +!40 = !{!"rb_global_vm_lock_struct", !16, i64 0, !8, i64 8, !41, i64 48, !16, i64 64, !19, i64 72, !8, i64 80, !8, i64 128, !19, i64 176, !19, i64 180} +!41 = !{!"list_head", !42, i64 0} +!42 = !{!"list_node", !16, i64 0, !16, i64 8} +!43 = !{!"long long", !8, i64 0} +!44 = !{!"", !8, i64 0} +!45 = !{!"rb_hook_list_struct", !16, i64 0, !19, i64 8, !19, i64 12, !19, i64 16} +!46 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} +!47 = !DILocation(line: 0, scope: !11) +!48 = !DILocation(line: 4, column: 6, scope: !11) +!49 = !{!50} +!50 = distinct !{!50, !51, !"sorbet_buildArrayIntrinsic: argument 0"} +!51 = distinct !{!51, !"sorbet_buildArrayIntrinsic"} +!52 = !{!53, !7, i64 0} +!53 = !{!"sorbet_inlineIntrinsicEnv", !7, i64 0, !7, i64 8, !19, i64 16, !16, i64 24, !7, i64 32} +!54 = !{!53, !7, i64 8} +!55 = !{!53, !19, i64 16} +!56 = !{!8, !8, i64 0} +!57 = !{!58} +!58 = distinct !{!58, !59, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} +!59 = distinct !{!59, !"VM_BH_FROM_IFUNC_BLOCK"} +!60 = !DILocation(line: 4, column: 6, scope: !11, inlinedAt: !61) +!61 = distinct !DILocation(line: 4, column: 6, scope: !11) +!62 = !{!18, !7, i64 128} +!63 = distinct !{!63, !64} +!64 = !{!"llvm.loop.unroll.disable"} diff --git a/test/testdata/compiler/block_no_args.opt.ll.exp b/test/testdata/compiler/block_no_args.opt.ll.exp index c8d4678fe1..d9b1dfaa30 100644 --- a/test/testdata/compiler/block_no_args.opt.ll.exp +++ b/test/testdata/compiler/block_no_args.opt.ll.exp @@ -2,10 +2,10 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -21,27 +21,28 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_fiber_struct = type opaque -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.anon.3 = type { [65 x i64] } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -49,24 +50,24 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_objspace = type opaque %struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.anon.6 = type { i64, i64, i64, i64 } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %union.pthread_mutex_t = type { %struct.__pthread_mutex_s } %struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } %struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } @@ -74,8 +75,8 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } %struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } %struct.rb_call_info_kw_arg = type { i32, [1 x i64] } -%struct.rb_captured_block = type { i64, i64*, %union.anon.17 } -%union.anon.17 = type { %struct.rb_iseq_struct* } +%struct.rb_captured_block = type { i64, i64*, %union.anon.20 } +%union.anon.20 = type { %struct.rb_iseq_struct* } %struct.vm_ifunc = type { i64, i64, i64 (i64, i64, i32, i64*, i64)*, i8*, %struct.rb_code_position_struct } @ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 @@ -166,8 +167,7 @@ functionEntryInitializers: %13 = getelementptr inbounds i64, i64* %12, i64 1, !dbg !26 store i64* %13, i64** %10, align 8, !dbg !26 %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !26 - store i64* getelementptr inbounds ([7 x i64], [7 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %8, align 8, !dbg !26, !tbaa !15 - ret i64 %send, !dbg !24 + ret i64 %send, !dbg !26 } ; Function Attrs: sspreq @@ -252,7 +252,6 @@ entry: %43 = getelementptr inbounds i64, i64* %42, i64 1, !dbg !38 store i64* %43, i64** %40, align 8, !dbg !38 %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !38 - store i64* getelementptr inbounds ([7 x i64], [7 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %39, align 8, !dbg !38, !tbaa !15 %44 = add nuw nsw i64 %30, 1, !dbg !33 %45 = icmp eq i64 %44, 10, !dbg !33 br i1 %45, label %forward_sorbet_rb_int_dotimes_withBlock.exit.i, label %29, !dbg !33, !llvm.loop !39 diff --git a/test/testdata/compiler/block_no_args_capture.opt.ll.exp b/test/testdata/compiler/block_no_args_capture.opt.ll.exp index de9ed01070..c0ec191eb1 100644 --- a/test/testdata/compiler/block_no_args_capture.opt.ll.exp +++ b/test/testdata/compiler/block_no_args_capture.opt.ll.exp @@ -2,10 +2,10 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -21,27 +21,28 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_fiber_struct = type opaque -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.anon.3 = type { [65 x i64] } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -49,24 +50,24 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_objspace = type opaque %struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.anon.6 = type { i64, i64, i64, i64 } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %union.pthread_mutex_t = type { %struct.__pthread_mutex_s } %struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } %struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } @@ -74,8 +75,8 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } %struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } %struct.rb_call_info_kw_arg = type { i32, [1 x i64] } -%struct.rb_captured_block = type { i64, i64*, %union.anon.17 } -%union.anon.17 = type { %struct.rb_iseq_struct* } +%struct.rb_captured_block = type { i64, i64*, %union.anon.20 } +%union.anon.20 = type { %struct.rb_iseq_struct* } %struct.vm_ifunc = type { i64, i64, i64 (i64, i64, i32, i64*, i64)*, i8*, %struct.rb_code_position_struct } %struct.sorbet_inlineIntrinsicEnv = type { i64, i64, i32, i64*, i64 } @@ -180,8 +181,7 @@ vm_get_ep.exit: %19 = getelementptr inbounds i64, i64* %18, i64 1, !dbg !24 store i64* %19, i64** %16, align 8, !dbg !24 %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !24 - store i64* getelementptr inbounds ([8 x i64], [8 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %8, align 8, !dbg !24, !tbaa !15 - ret i64 %send, !dbg !23 + ret i64 %send, !dbg !24 } ; Function Attrs: sspreq @@ -307,7 +307,6 @@ entry: %65 = getelementptr inbounds i64, i64* %64, i64 1, !dbg !43 store i64* %65, i64** %62, align 8, !dbg !43 %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !43 - store i64* getelementptr inbounds ([8 x i64], [8 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %55, align 8, !dbg !43, !tbaa !15 %66 = add nuw nsw i64 %46, 1, !dbg !38 %67 = icmp eq i64 %66, 10, !dbg !38 br i1 %67, label %forward_sorbet_rb_int_dotimes_withBlock.exit.i, label %45, !dbg !38, !llvm.loop !44 diff --git a/test/testdata/compiler/block_no_args_captures_constant.opt.ll.exp b/test/testdata/compiler/block_no_args_captures_constant.opt.ll.exp index edd67510b1..1daf688dbe 100644 --- a/test/testdata/compiler/block_no_args_captures_constant.opt.ll.exp +++ b/test/testdata/compiler/block_no_args_captures_constant.opt.ll.exp @@ -2,16 +2,17 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -27,34 +28,34 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.rb_fiber_struct = type opaque -%struct.anon.3 = type { [65 x i64] } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -68,14 +69,14 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } +%struct.anon.6 = type { i64, i64, i64, i64 } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } %struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } %struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } %struct.rb_call_info_kw_arg = type { i32, [1 x i64] } -%struct.rb_captured_block = type { i64, i64*, %union.anon.17 } -%union.anon.17 = type { %struct.rb_iseq_struct* } +%struct.rb_captured_block = type { i64, i64*, %union.anon.20 } +%union.anon.20 = type { %struct.rb_iseq_struct* } %struct.vm_ifunc = type { i64, i64, i64 (i64, i64, i32, i64*, i64)*, i8*, %struct.rb_code_position_struct } @ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 @@ -242,7 +243,6 @@ argCountFailBlock: ; preds = %functionEntryInitia %42 = getelementptr inbounds i64, i64* %41, i64 1, !dbg !41 store i64* %42, i64** %39, align 8, !dbg !41 %send23 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.1, i64 0), !dbg !41 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %38, align 8, !dbg !41, !tbaa !14 %43 = add nuw nsw i64 %29, 1, !dbg !32 %44 = icmp eq i64 %43, 10, !dbg !32 br i1 %44, label %forward_sorbet_rb_int_dotimes_withBlock.exit, label %28, !dbg !32, !llvm.loop !42 @@ -310,8 +310,7 @@ functionEntryInitializers: %20 = getelementptr inbounds i64, i64* %19, i64 1, !dbg !49 store i64* %20, i64** %17, align 8, !dbg !49 %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.1, i64 0), !dbg !49 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %8, align 8, !dbg !49, !tbaa !14 - ret i64 %send, !dbg !48 + ret i64 %send, !dbg !49 } ; Function Attrs: sspreq diff --git a/test/testdata/compiler/block_type_checking.opt.ll.exp b/test/testdata/compiler/block_type_checking.opt.ll.exp index 9dc43ec0d4..405c5a01c0 100644 --- a/test/testdata/compiler/block_type_checking.opt.ll.exp +++ b/test/testdata/compiler/block_type_checking.opt.ll.exp @@ -2,16 +2,17 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -27,34 +28,34 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.rb_fiber_struct = type opaque -%struct.anon.3 = type { [65 x i64] } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -68,15 +69,15 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } +%struct.anon.6 = type { i64, i64, i64, i64 } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } %struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } %struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } %struct.rb_call_info_kw_arg = type { i32, [1 x i64] } %struct.vm_ifunc = type { i64, i64, i64 (i64, i64, i32, i64*, i64)*, i8*, %struct.rb_code_position_struct } -%struct.rb_captured_block = type { i64, i64*, %union.anon.17 } -%union.anon.17 = type { %struct.rb_iseq_struct* } +%struct.rb_captured_block = type { i64, i64*, %union.anon.20 } +%union.anon.20 = type { %struct.rb_iseq_struct* } %struct.iseq_inline_iv_cache_entry = type { i64, i64 } @ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 @@ -222,7 +223,7 @@ functionEntryInitializers: %7 = and i64 %6, -129 store i64 %7, i64* %5, align 8, !tbaa !6 %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([19 x i64], [19 x i64]* @iseqEncodedArray, i64 0, i64 15), i64** %8, align 8, !dbg !24, !tbaa !15 + store i64* getelementptr inbounds ([19 x i64], [19 x i64]* @iseqEncodedArray, i64 0, i64 16), i64** %8, align 8, !dbg !24, !tbaa !15 ret i64 13, !dbg !25 } @@ -627,24 +628,24 @@ functionEntryInitializers: %34 = getelementptr inbounds i64, i64* %33, i64 1, !dbg !33 store i64* %34, i64** %31, align 8, !dbg !33 %send45 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_returns.1, i64 0), !dbg !33 - ret i64 %send45, !dbg !81 + ret i64 %send45, !dbg !33 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #13 ; Function Attrs: cold minsize noreturn nounwind sspreq uwtable -define internal fastcc void @"func_Foo#3bar.cold.1"(i64 %0) unnamed_addr #14 !dbg !82 { +define internal fastcc void @"func_Foo#3bar.cold.1"(i64 %0) unnamed_addr #14 !dbg !81 { newFuncRoot: tail call void @sorbet_cast_failure(i64 %0, i8* getelementptr inbounds ([247 x i8], [247 x i8]* @sorbet_moduleStringTable, i64 0, i64 152), i8* getelementptr inbounds ([247 x i8], [247 x i8]* @sorbet_moduleStringTable, i64 0, i64 142)) #19 unreachable } ; Function Attrs: cold minsize noreturn nounwind sspreq uwtable -define internal fastcc void @"func_Foo#3bar.cold.2"(i64 %rawArg_x) unnamed_addr #14 !dbg !84 { +define internal fastcc void @"func_Foo#3bar.cold.2"(i64 %rawArg_x) unnamed_addr #14 !dbg !83 { newFuncRoot: - tail call void @sorbet_cast_failure(i64 %rawArg_x, i8* getelementptr inbounds ([247 x i8], [247 x i8]* @sorbet_moduleStringTable, i64 0, i64 128), i8* getelementptr inbounds ([247 x i8], [247 x i8]* @sorbet_moduleStringTable, i64 0, i64 142)) #19, !dbg !85 - unreachable, !dbg !85 + tail call void @sorbet_cast_failure(i64 %rawArg_x, i8* getelementptr inbounds ([247 x i8], [247 x i8]* @sorbet_moduleStringTable, i64 0, i64 128), i8* getelementptr inbounds ([247 x i8], [247 x i8]* @sorbet_moduleStringTable, i64 0, i64 142)) #19, !dbg !84 + unreachable, !dbg !84 } ; Function Attrs: nofree nosync nounwind willreturn @@ -727,8 +728,8 @@ attributes #21 = { noinline } !21 = !{!22, !16, i64 16} !22 = !{!"rb_control_frame_struct", !16, i64 0, !16, i64 8, !16, i64 16, !7, i64 24, !16, i64 32, !16, i64 40, !16, i64 48} !23 = !{!22, !16, i64 32} -!24 = !DILocation(line: 16, column: 3, scope: !10) -!25 = !DILocation(line: 15, column: 10, scope: !10) +!24 = !DILocation(line: 15, column: 10, scope: !10) +!25 = !DILocation(line: 16, column: 3, scope: !10) !26 = !DILocation(line: 9, column: 3, scope: !27, inlinedAt: !28) !27 = distinct !DISubprogram(name: "Foo.", linkageName: "func_Foo.13L62", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) !28 = distinct !DILocation(line: 5, column: 1, scope: !11) @@ -784,8 +785,7 @@ attributes #21 = { noinline } !78 = !{!18, !16, i64 56} !79 = !DILocation(line: 0, scope: !62) !80 = !{!22, !7, i64 24} -!81 = !DILocation(line: 8, column: 3, scope: !32) -!82 = distinct !DISubprogram(name: "func_Foo#3bar.cold.1", linkageName: "func_Foo#3bar.cold.1", scope: null, file: !4, type: !83, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) -!83 = !DISubroutineType(types: !5) -!84 = distinct !DISubprogram(name: "func_Foo#3bar.cold.2", linkageName: "func_Foo#3bar.cold.2", scope: null, file: !4, type: !83, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) -!85 = !DILocation(line: 9, column: 11, scope: !84) +!81 = distinct !DISubprogram(name: "func_Foo#3bar.cold.1", linkageName: "func_Foo#3bar.cold.1", scope: null, file: !4, type: !82, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) +!82 = !DISubroutineType(types: !5) +!83 = distinct !DISubprogram(name: "func_Foo#3bar.cold.2", linkageName: "func_Foo#3bar.cold.2", scope: null, file: !4, type: !82, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) +!84 = !DILocation(line: 9, column: 11, scope: !83) diff --git a/test/testdata/compiler/boolean_ops.opt.ll.exp b/test/testdata/compiler/boolean_ops.opt.ll.exp index 6c97071dce..349e6329a8 100644 --- a/test/testdata/compiler/boolean_ops.opt.ll.exp +++ b/test/testdata/compiler/boolean_ops.opt.ll.exp @@ -2,16 +2,17 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -27,34 +28,34 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.rb_fiber_struct = type opaque -%struct.anon.3 = type { [65 x i64] } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -68,7 +69,7 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } +%struct.anon.6 = type { i64, i64, i64, i64 } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } %struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } diff --git a/test/testdata/compiler/casts.opt.ll.exp b/test/testdata/compiler/casts.opt.ll.exp index eb2598fafb..052223c9b1 100644 --- a/test/testdata/compiler/casts.opt.ll.exp +++ b/test/testdata/compiler/casts.opt.ll.exp @@ -2,16 +2,17 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -27,34 +28,34 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.rb_fiber_struct = type opaque -%struct.anon.3 = type { [65 x i64] } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -68,7 +69,7 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } +%struct.anon.6 = type { i64, i64, i64, i64 } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } %struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } diff --git a/test/testdata/compiler/class.opt.ll.exp b/test/testdata/compiler/class.opt.ll.exp index ed4ed425e1..f3afd47a12 100644 --- a/test/testdata/compiler/class.opt.ll.exp +++ b/test/testdata/compiler/class.opt.ll.exp @@ -2,10 +2,10 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -21,27 +21,28 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_fiber_struct = type opaque -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.anon.3 = type { [65 x i64] } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -49,24 +50,24 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_objspace = type opaque %struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.anon.6 = type { i64, i64, i64, i64 } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %union.pthread_mutex_t = type { %struct.__pthread_mutex_s } %struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } %struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } diff --git a/test/testdata/compiler/classfields.opt.ll.exp b/test/testdata/compiler/classfields.opt.ll.exp index 4806b5e586..df5f241b4d 100644 --- a/test/testdata/compiler/classfields.opt.ll.exp +++ b/test/testdata/compiler/classfields.opt.ll.exp @@ -2,16 +2,17 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -27,34 +28,34 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.rb_fiber_struct = type opaque -%struct.anon.3 = type { [65 x i64] } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -68,7 +69,7 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } +%struct.anon.6 = type { i64, i64, i64, i64 } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } %struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } diff --git a/test/testdata/compiler/constant_cache.opt.ll.exp b/test/testdata/compiler/constant_cache.opt.ll.exp index 83e4889060..1a9c320708 100644 --- a/test/testdata/compiler/constant_cache.opt.ll.exp +++ b/test/testdata/compiler/constant_cache.opt.ll.exp @@ -2,16 +2,17 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -27,34 +28,34 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.rb_fiber_struct = type opaque -%struct.anon.3 = type { [65 x i64] } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -68,7 +69,7 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } +%struct.anon.6 = type { i64, i64, i64, i64 } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } %struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } diff --git a/test/testdata/compiler/custom_plus.opt.ll.exp b/test/testdata/compiler/custom_plus.opt.ll.exp index 7f3e58775f..cd122dbb77 100644 --- a/test/testdata/compiler/custom_plus.opt.ll.exp +++ b/test/testdata/compiler/custom_plus.opt.ll.exp @@ -2,16 +2,17 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -27,34 +28,34 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.rb_fiber_struct = type opaque -%struct.anon.3 = type { [65 x i64] } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -68,7 +69,7 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } +%struct.anon.6 = type { i64, i64, i64, i64 } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } %struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } diff --git a/test/testdata/compiler/direct_call.opt.ll.exp b/test/testdata/compiler/direct_call.opt.ll.exp index 52b37018b6..dc1b8223b5 100644 --- a/test/testdata/compiler/direct_call.opt.ll.exp +++ b/test/testdata/compiler/direct_call.opt.ll.exp @@ -2,16 +2,17 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -27,34 +28,34 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.rb_fiber_struct = type opaque -%struct.anon.3 = type { [65 x i64] } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -68,7 +69,7 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } +%struct.anon.6 = type { i64, i64, i64, i64 } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } %struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } diff --git a/test/testdata/compiler/exceptions/basic.opt.ll.exp b/test/testdata/compiler/exceptions/basic.opt.ll.exp index 02652515c1..33771c3ad3 100644 --- a/test/testdata/compiler/exceptions/basic.opt.ll.exp +++ b/test/testdata/compiler/exceptions/basic.opt.ll.exp @@ -2,16 +2,17 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -27,34 +28,34 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.rb_fiber_struct = type opaque -%struct.anon.3 = type { [65 x i64] } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -68,7 +69,7 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } +%struct.anon.6 = type { i64, i64, i64, i64 } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } %struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } @@ -96,100 +97,104 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 @ic_puts.3 = internal global %struct.FunctionInlineCache zeroinitializer @ic_puts.4 = internal global %struct.FunctionInlineCache zeroinitializer @ic_raise = internal global %struct.FunctionInlineCache zeroinitializer -@"ic_is_a?" = internal global %struct.FunctionInlineCache zeroinitializer +@"ic_===" = internal global %struct.FunctionInlineCache zeroinitializer @ic_puts.5 = internal global %struct.FunctionInlineCache zeroinitializer @ic_puts.6 = internal global %struct.FunctionInlineCache zeroinitializer @ic_puts.7 = internal global %struct.FunctionInlineCache zeroinitializer @"stackFramePrecomputed_func_A.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@sorbet_moduleStringTable = internal unnamed_addr constant [273 x i8] c"\00test/testdata/compiler/exceptions/basic.rb\00A\00Object\00=== no-raise ===\00puts\00test\00=== raise ===\00master\00\00x\00\00\00\00rescue in test\00ensure in test\00begin\00foo\00raise\00StandardError\00is_a?\00else\00ensure\00\00normal\00", align 1 +@sorbet_moduleStringTable = internal unnamed_addr constant [278 x i8] c"\00test/testdata/compiler/exceptions/basic.rb\00A\00Object\00=== no-raise ===\00puts\00test\00=== raise ===\00master\00\00x\00\00\00\00rescue in test\00ensure in test\00begin\00foo\00raise\00StandardError\00Module\00===\00else\00ensure\00\00normal\00", align 1 @sorbet_moduleIDTable = internal unnamed_addr global [14 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [14 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 86, i32 4 }, %struct.rb_code_position_struct { i32 91, i32 4 }, %struct.rb_code_position_struct { i32 117, i32 16 }, %struct.rb_code_position_struct { i32 134, i32 1 }, %struct.rb_code_position_struct { i32 136, i32 18 }, %struct.rb_code_position_struct { i32 155, i32 7 }, %struct.rb_code_position_struct { i32 163, i32 14 }, %struct.rb_code_position_struct { i32 178, i32 14 }, %struct.rb_code_position_struct { i32 193, i32 14 }, %struct.rb_code_position_struct { i32 218, i32 5 }, %struct.rb_code_position_struct { i32 238, i32 5 }, %struct.rb_code_position_struct { i32 256, i32 9 }, %struct.rb_code_position_struct { i32 266, i32 6 }], align 8 +@sorbet_moduleIDDescriptors = internal unnamed_addr constant [14 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 86, i32 4 }, %struct.rb_code_position_struct { i32 91, i32 4 }, %struct.rb_code_position_struct { i32 117, i32 16 }, %struct.rb_code_position_struct { i32 134, i32 1 }, %struct.rb_code_position_struct { i32 136, i32 18 }, %struct.rb_code_position_struct { i32 155, i32 7 }, %struct.rb_code_position_struct { i32 163, i32 14 }, %struct.rb_code_position_struct { i32 178, i32 14 }, %struct.rb_code_position_struct { i32 193, i32 14 }, %struct.rb_code_position_struct { i32 218, i32 5 }, %struct.rb_code_position_struct { i32 245, i32 3 }, %struct.rb_code_position_struct { i32 261, i32 9 }, %struct.rb_code_position_struct { i32 271, i32 6 }], align 8 @sorbet_moduleRubyStringTable = internal unnamed_addr global [12 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [12 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 42 }, %struct.rb_code_position_struct { i32 69, i32 16 }, %struct.rb_code_position_struct { i32 96, i32 13 }, %struct.rb_code_position_struct { i32 91, i32 4 }, %struct.rb_code_position_struct { i32 178, i32 14 }, %struct.rb_code_position_struct { i32 193, i32 14 }, %struct.rb_code_position_struct { i32 208, i32 5 }, %struct.rb_code_position_struct { i32 214, i32 3 }, %struct.rb_code_position_struct { i32 244, i32 4 }, %struct.rb_code_position_struct { i32 249, i32 6 }, %struct.rb_code_position_struct { i32 256, i32 9 }], align 8 +@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [12 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 42 }, %struct.rb_code_position_struct { i32 69, i32 16 }, %struct.rb_code_position_struct { i32 96, i32 13 }, %struct.rb_code_position_struct { i32 91, i32 4 }, %struct.rb_code_position_struct { i32 178, i32 14 }, %struct.rb_code_position_struct { i32 193, i32 14 }, %struct.rb_code_position_struct { i32 208, i32 5 }, %struct.rb_code_position_struct { i32 214, i32 3 }, %struct.rb_code_position_struct { i32 249, i32 4 }, %struct.rb_code_position_struct { i32 254, i32 6 }, %struct.rb_code_position_struct { i32 261, i32 9 }], align 8 @rb_cObject = external local_unnamed_addr constant i64 @guard_epoch_A = linkonce local_unnamed_addr global i64 0 @guarded_const_A = linkonce local_unnamed_addr global i64 0 @rb_eStandardError = external local_unnamed_addr constant i64 +@rb_cModule = external local_unnamed_addr constant i64 -; Function Attrs: noreturn -declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #0 +; Function Attrs: nounwind readnone willreturn +declare i64 @rb_obj_is_kind_of(i64, i64) local_unnamed_addr #0 -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #1 +; Function Attrs: noreturn +declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #1 -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #1 +declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #2 -declare i64 @sorbet_getConstant(i8*, i64) local_unnamed_addr #1 +declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #2 -declare i64 @sorbet_readRealpath() local_unnamed_addr #1 +declare i64 @sorbet_getConstant(i8*, i64) local_unnamed_addr #2 -declare %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64) local_unnamed_addr #1 +declare i64 @sorbet_readRealpath() local_unnamed_addr #2 -declare void @sorbet_popFrame() local_unnamed_addr #1 +declare %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64) local_unnamed_addr #2 -declare void @sorbet_vm_env_write_slowpath(i64*, i32, i64) local_unnamed_addr #1 +declare void @sorbet_popFrame() local_unnamed_addr #2 -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #1 +declare void @sorbet_vm_env_write_slowpath(i64*, i32, i64) local_unnamed_addr #2 -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #1 +declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #2 -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #1 +declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #2 -declare void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #1 +declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #2 -declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #1 +declare void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #2 -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #1 +declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #2 -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #1 +declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #2 -declare i64 @sorbet_vm_isa_p(%struct.FunctionInlineCache*, %struct.rb_control_frame_struct*, i64, i64) local_unnamed_addr #1 +declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #2 -declare i64 @sorbet_run_exception_handling(%struct.rb_execution_context_struct*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64**, i64, %struct.rb_control_frame_struct*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64, i64, i64) local_unnamed_addr #1 +declare i64 @sorbet_run_exception_handling(%struct.rb_execution_context_struct*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64**, i64, %struct.rb_control_frame_struct*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64, i64, i64) local_unnamed_addr #2 -declare i64 @rb_define_class(i8*, i64) local_unnamed_addr #1 +declare i64 @rb_define_class(i8*, i64) local_unnamed_addr #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #2 +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #3 ; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #2 +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #3 ; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #0 +declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #1 + +declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #2 ; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #3 +declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #4 ; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xmalloc2(i64, i64) local_unnamed_addr #3 +declare noalias nonnull i8* @ruby_xmalloc2(i64, i64) local_unnamed_addr #4 ; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #2 +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #3 ; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #4 { +define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #5 { %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #10 + tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #11 unreachable } ; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #4 { +define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #5 { %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #10 + tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #11 unreachable } ; Function Attrs: sspreq -define void @Init_basic() local_unnamed_addr #5 { +define void @Init_basic() local_unnamed_addr #6 { entry: %positional_table.i.i = alloca i64, align 8, !dbg !10 %locals.i14.i = alloca i64, i32 0, align 8 %locals.i9.i = alloca i64, i32 5, align 8 %locals.i.i = alloca i64, i32 0, align 8 %realpath = tail call i64 @sorbet_readRealpath() - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([14 x %struct.rb_code_position_struct], [14 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 14, i8* noundef getelementptr inbounds ([273 x i8], [273 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([12 x i64], [12 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([12 x %struct.rb_code_position_struct], [12 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 12, i8* noundef getelementptr inbounds ([273 x i8], [273 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) + tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([14 x %struct.rb_code_position_struct], [14 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 14, i8* noundef getelementptr inbounds ([278 x i8], [278 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) + tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([12 x i64], [12 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([12 x %struct.rb_code_position_struct], [12 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 12, i8* noundef getelementptr inbounds ([278 x i8], [278 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 28) %"rubyId_.i.i" = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 %"rubyStr_.i.i" = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 @@ -223,14 +228,14 @@ entry: %"rubyStr_ensure in test.i.i" = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 6), align 8, !invariant.load !5 %7 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_ensure in test.i.i", i64 %"rubyId_ensure in test.i.i", i64 %"rubyStr_test/testdata/compiler/exceptions/basic.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* %stackFrame.i11.i, i32 noundef 5, i32 noundef 6, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 2) store %struct.rb_iseq_struct* %7, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.4test$block_3", align 8 - %8 = call i64 @sorbet_getConstant(i8* noundef getelementptr inbounds ([25 x i8], [25 x i8]* @sorbet_getTRetry.retry, i64 0, i64 0), i64 noundef 25) #11 + %8 = call i64 @sorbet_getConstant(i8* noundef getelementptr inbounds ([25 x i8], [25 x i8]* @sorbet_getTRetry.retry, i64 0, i64 0), i64 noundef 25) #12 store i64 %8, i64* @"", align 8 call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.3, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !21 call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.4, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !24 %rubyId_raise.i = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 10), align 8, !dbg !25, !invariant.load !5 call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_raise, i64 %rubyId_raise.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !25 - %"rubyId_is_a?.i" = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 11), align 8, !dbg !26, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_is_a?", i64 %"rubyId_is_a?.i", i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !26 + %"rubyId_===.i" = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 11), align 8, !dbg !26, !invariant.load !5 + call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_===", i64 %"rubyId_===.i", i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !26 call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.5, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !28 call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.6, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !29 call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.7, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !31 @@ -252,14 +257,14 @@ entry: %19 = load i64, i64* %18, align 8, !tbaa !6 %20 = and i64 %19, -33 store i64 %20, i64* %18, align 8, !tbaa !6 - call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %13, %struct.rb_control_frame_struct* %15, %struct.rb_iseq_struct* %stackFrame.i) #11 + call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %13, %struct.rb_control_frame_struct* %15, %struct.rb_iseq_struct* %stackFrame.i) #12 %21 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %15, i64 0, i32 0 store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %21, align 8, !dbg !51, !tbaa !33 %22 = load i64, i64* @rb_cObject, align 8, !dbg !52 - %23 = call i64 @rb_define_class(i8* getelementptr inbounds ([273 x i8], [273 x i8]* @sorbet_moduleStringTable, i64 0, i64 60), i64 %22) #11, !dbg !52 - %24 = call %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64 %23) #11, !dbg !52 + %23 = call i64 @rb_define_class(i8* getelementptr inbounds ([278 x i8], [278 x i8]* @sorbet_moduleStringTable, i64 0, i64 60), i64 %22) #12, !dbg !52 + %24 = call %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64 %23) #12, !dbg !52 %25 = bitcast i64* %positional_table.i.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %25) #11 + call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %25) #12 %stackFrame.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.13", align 8 %26 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !33 %27 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %26, i64 0, i32 2 @@ -271,7 +276,7 @@ entry: %32 = load i64, i64* %31, align 8, !tbaa !6 %33 = and i64 %32, -33 store i64 %33, i64* %31, align 8, !tbaa !6 - call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %26, %struct.rb_control_frame_struct* %28, %struct.rb_iseq_struct* %stackFrame.i.i) #11 + call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %26, %struct.rb_control_frame_struct* %28, %struct.rb_iseq_struct* %stackFrame.i.i) #12 %34 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %24, i64 0, i32 0 store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %34, align 8, !dbg !53, !tbaa !33 %35 = load i64, i64* @guard_epoch_A, align 8, !dbg !10 @@ -290,7 +295,7 @@ entry: %guardUpdated = icmp eq i64 %40, %41, !dbg !10 call void @llvm.assume(i1 %guardUpdated), !dbg !10 %stackFrame7.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @stackFramePrecomputed_func_A.4test, align 8, !dbg !10 - %42 = call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #12, !dbg !10 + %42 = call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #13, !dbg !10 %43 = bitcast i8* %42 to i16*, !dbg !10 %44 = load i16, i16* %43, align 8, !dbg !10 %45 = and i16 %44, -384, !dbg !10 @@ -302,18 +307,18 @@ entry: %49 = getelementptr inbounds i8, i8* %42, i64 12, !dbg !10 %50 = getelementptr inbounds i8, i8* %42, i64 4, !dbg !10 %51 = bitcast i8* %50 to i32*, !dbg !10 - call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %49, i8 0, i64 20, i1 false) #11, !dbg !10 + call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %49, i8 0, i64 20, i1 false) #12, !dbg !10 store i32 1, i32* %51, align 4, !dbg !10, !tbaa !59 %52 = extractelement <4 x i64> %2, i32 1, !dbg !10 store i64 %52, i64* %positional_table.i.i, align 8, !dbg !10 - %53 = call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 1, i64 noundef 8) #12, !dbg !10 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %53, i8* nocapture noundef nonnull readonly align 8 dereferenceable(8) %25, i64 noundef 8, i1 noundef false) #11, !dbg !10 + %53 = call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 1, i64 noundef 8) #13, !dbg !10 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %53, i8* nocapture noundef nonnull readonly align 8 dereferenceable(8) %25, i64 noundef 8, i1 noundef false) #12, !dbg !10 %54 = getelementptr inbounds i8, i8* %42, i64 32, !dbg !10 %55 = bitcast i8* %54 to i8**, !dbg !10 store i8* %53, i8** %55, align 8, !dbg !10, !tbaa !60 - call void @sorbet_vm_define_method(i64 %39, i8* getelementptr inbounds ([273 x i8], [273 x i8]* @sorbet_moduleStringTable, i64 0, i64 91), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @func_A.4test, i8* nonnull %42, %struct.rb_iseq_struct* %stackFrame7.i.i, i1 noundef zeroext true) #11, !dbg !10 - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %25) #11 - call void @sorbet_popFrame() #11, !dbg !52 + call void @sorbet_vm_define_method(i64 %39, i8* getelementptr inbounds ([278 x i8], [278 x i8]* @sorbet_moduleStringTable, i64 0, i64 91), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @func_A.4test, i8* nonnull %42, %struct.rb_iseq_struct* %stackFrame7.i.i, i1 noundef zeroext true) #12, !dbg !10 + call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %25) #12 + call void @sorbet_popFrame() #12, !dbg !52 store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 23), i64** %21, align 8, !dbg !52, !tbaa !33 %"rubyStr_=== no-raise ===.i" = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !dbg !61, !invariant.load !5 %56 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %15, i64 0, i32 1, !dbg !17 @@ -356,7 +361,7 @@ entry: } ; Function Attrs: nounwind sspreq uwtable -define internal i64 @func_A.4test(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #6 !dbg !23 { +define internal i64 @func_A.4test(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #7 !dbg !23 { functionEntryInitializers: %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %0, align 8, !tbaa !33 @@ -370,7 +375,7 @@ postProcess: ; preds = %sorbet_writeLocal.e ret i64 %".sroa.0.0" argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 1, i32 noundef 1) #13, !dbg !63 + tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 1, i32 noundef 1) #14, !dbg !63 unreachable, !dbg !63 fillRequiredArgs: ; preds = %functionEntryInitializers @@ -388,11 +393,11 @@ fillRequiredArgs: ; preds = %functionEntryInitia br label %sorbet_writeLocal.exit, !dbg !63 8: ; preds = %fillRequiredArgs - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %2, i32 noundef -4, i64 %rawArg_x) #11, !dbg !63 + tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %2, i32 noundef -4, i64 %rawArg_x) #12, !dbg !63 br label %sorbet_writeLocal.exit, !dbg !63 sorbet_writeLocal.exit: ; preds = %6, %8 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %0, align 8, !dbg !65, !tbaa !33 + store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %0, align 8, !dbg !65, !tbaa !33 %9 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !67, !tbaa !33 %"" = load i64, i64* @"", align 8, !dbg !67 %10 = tail call i64 @sorbet_run_exception_handling(%struct.rb_execution_context_struct* %9, i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_A.4test$block_1", i64** nonnull %0, i64 noundef 0, %struct.rb_control_frame_struct* nonnull %cfp, i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_A.4test$block_2", i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_A.4test$block_4", i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_A.4test$block_3", i64 %"", i64 noundef 0, i64 noundef 0), !dbg !67 @@ -408,7 +413,7 @@ exception-continue: ; preds = %sorbet_writeLocal.e } ; Function Attrs: ssp -define internal noundef i64 @"func_A.4test$block_1"(i64** nocapture nonnull writeonly align 8 dereferenceable(8) %pc, i64 %localsOffset, %struct.rb_control_frame_struct* %cfp) #7 !dbg !22 { +define internal noundef i64 @"func_A.4test$block_1"(i64** nocapture nonnull writeonly align 8 dereferenceable(8) %pc, i64 %localsOffset, %struct.rb_control_frame_struct* %cfp) #8 !dbg !22 { functionEntryInitializers: %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !33 %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 @@ -468,24 +473,24 @@ BB5: ; preds = %functionEntryInitia br label %BB7, !dbg !25 32: ; preds = %BB5 - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %26, i32 noundef -5, i64 %send27) #11, !dbg !25 + tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %26, i32 noundef -5, i64 %send27) #12, !dbg !25 br label %BB7, !dbg !25 BB7: ; preds = %32, %30, %functionEntryInitializers - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %pc, align 8, !tbaa !33 + store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %pc, align 8, !tbaa !33 ret i64 52 } ; Function Attrs: ssp -define internal noundef i64 @"func_A.4test$block_2"(i64** nocapture nofree readnone %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #7 !dbg !27 { -vm_get_ep.exit34: +define internal noundef i64 @"func_A.4test$block_2"(i64** nocapture nofree readnone %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #8 !dbg !27 { +vm_get_ep.exit37: %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !33 %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !45 %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 %4 = load i64, i64* %3, align 8, !tbaa !69 %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.4test$block_2", align 8 - tail call void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #11 + tail call void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #12 %5 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !33 %6 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %5, i64 0, i32 2 %7 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %6, align 8, !tbaa !45 @@ -503,103 +508,122 @@ vm_get_ep.exit34: %18 = getelementptr inbounds i64, i64* %17, i64 -3, !dbg !26 %19 = load i64, i64* %18, align 8, !dbg !26, !tbaa !6 %20 = load i64, i64* @rb_eStandardError, align 8, !dbg !26 - %21 = tail call i64 @sorbet_vm_isa_p(%struct.FunctionInlineCache* noundef @"ic_is_a?", %struct.rb_control_frame_struct* %11, i64 %19, i64 %20), !dbg !26 - %22 = and i64 %21, -9, !dbg !26 - %23 = icmp ne i64 %22, 0, !dbg !26 - br i1 %23, label %vm_get_ep.exit32, label %vm_get_ep.exit, !dbg !26 - -blockExit: ; preds = %75, %73, %60, %58 + %21 = load i64, i64* @rb_cModule, align 8, !dbg !26 + %22 = tail call i64 @rb_obj_is_kind_of(i64 %19, i64 %20), !dbg !26 + %23 = icmp eq i64 %22, 20, !dbg !26 + %24 = select i1 %23, i64 20, i64 0, !dbg !26 + %25 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 5, !dbg !26 + %26 = load i32, i32* %25, align 8, !dbg !26, !tbaa !72 + %27 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 6, !dbg !26 + %28 = load i32, i32* %27, align 4, !dbg !26, !tbaa !73 + %29 = xor i32 %28, -1, !dbg !26 + %30 = and i32 %29, %26, !dbg !26 + %31 = icmp eq i32 %30, 0, !dbg !26 + br i1 %31, label %afterSend, label %86, !dbg !26, !prof !66 + +blockExit: ; preds = %83, %81, %68, %66 tail call void @sorbet_popFrame() ret i64 52 -vm_get_ep.exit32: ; preds = %vm_get_ep.exit34 - %24 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !72, !tbaa !33 - %25 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %24, i64 0, i32 2, !dbg !72 - %26 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %25, align 8, !dbg !72, !tbaa !45 - %27 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %26, i64 0, i32 4, !dbg !72 - %28 = load i64*, i64** %27, align 8, !dbg !72 - %29 = getelementptr inbounds i64, i64* %28, i64 -1, !dbg !72 - %30 = load i64, i64* %29, align 8, !dbg !72, !tbaa !6 - %31 = and i64 %30, -4, !dbg !72 - %32 = inttoptr i64 %31 to i64*, !dbg !72 - %33 = load i64, i64* %32, align 8, !dbg !72, !tbaa !6 - %34 = and i64 %33, 8, !dbg !72 - %35 = icmp eq i64 %34, 0, !dbg !72 - br i1 %35, label %36, label %38, !dbg !72, !prof !66 - -36: ; preds = %vm_get_ep.exit32 - %37 = getelementptr inbounds i64, i64* %32, i64 -3, !dbg !72 - store i64 8, i64* %37, align 8, !dbg !72, !tbaa !6 - br label %vm_get_ep.exit30, !dbg !72 - -38: ; preds = %vm_get_ep.exit32 - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %32, i32 noundef -3, i64 noundef 8) #11, !dbg !72 - br label %vm_get_ep.exit30, !dbg !72 - -vm_get_ep.exit30: ; preds = %36, %38 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %8, align 8, !dbg !73, !tbaa !33 - %39 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !28, !tbaa !33 - %40 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %39, i64 0, i32 2, !dbg !28 - %41 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %40, align 8, !dbg !28, !tbaa !45 - %42 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %41, i64 0, i32 1, !dbg !28 - %43 = load i64*, i64** %42, align 8, !dbg !28 - store i64 %4, i64* %43, align 8, !dbg !28, !tbaa !6 - %44 = getelementptr inbounds i64, i64* %43, i64 1, !dbg !28 - store i64 %19, i64* %44, align 8, !dbg !28, !tbaa !6 - %45 = getelementptr inbounds i64, i64* %44, i64 1, !dbg !28 - store i64* %45, i64** %42, align 8, !dbg !28 +vm_get_ep.exit35: ; preds = %afterSend + %32 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !74, !tbaa !33 + %33 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %32, i64 0, i32 2, !dbg !74 + %34 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %33, align 8, !dbg !74, !tbaa !45 + %35 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %34, i64 0, i32 4, !dbg !74 + %36 = load i64*, i64** %35, align 8, !dbg !74 + %37 = getelementptr inbounds i64, i64* %36, i64 -1, !dbg !74 + %38 = load i64, i64* %37, align 8, !dbg !74, !tbaa !6 + %39 = and i64 %38, -4, !dbg !74 + %40 = inttoptr i64 %39 to i64*, !dbg !74 + %41 = load i64, i64* %40, align 8, !dbg !74, !tbaa !6 + %42 = and i64 %41, 8, !dbg !74 + %43 = icmp eq i64 %42, 0, !dbg !74 + br i1 %43, label %44, label %46, !dbg !74, !prof !66 + +44: ; preds = %vm_get_ep.exit35 + %45 = getelementptr inbounds i64, i64* %40, i64 -3, !dbg !74 + store i64 8, i64* %45, align 8, !dbg !74, !tbaa !6 + br label %vm_get_ep.exit33, !dbg !74 + +46: ; preds = %vm_get_ep.exit35 + tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %40, i32 noundef -3, i64 noundef 8) #12, !dbg !74 + br label %vm_get_ep.exit33, !dbg !74 + +vm_get_ep.exit33: ; preds = %44, %46 + store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %8, align 8, !dbg !75, !tbaa !33 + %47 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !28, !tbaa !33 + %48 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %47, i64 0, i32 2, !dbg !28 + %49 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %48, align 8, !dbg !28, !tbaa !45 + %50 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %49, i64 0, i32 1, !dbg !28 + %51 = load i64*, i64** %50, align 8, !dbg !28 + store i64 %4, i64* %51, align 8, !dbg !28, !tbaa !6 + %52 = getelementptr inbounds i64, i64* %51, i64 1, !dbg !28 + store i64 %19, i64* %52, align 8, !dbg !28, !tbaa !6 + %53 = getelementptr inbounds i64, i64* %52, i64 1, !dbg !28 + store i64* %53, i64** %50, align 8, !dbg !28 %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.5, i64 0), !dbg !28 - %46 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !28, !tbaa !33 - %47 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %46, i64 0, i32 2, !dbg !28 - %48 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %47, align 8, !dbg !28, !tbaa !45 - %49 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %48, i64 0, i32 4, !dbg !28 - %50 = load i64*, i64** %49, align 8, !dbg !28 - %51 = getelementptr inbounds i64, i64* %50, i64 -1, !dbg !28 - %52 = load i64, i64* %51, align 8, !dbg !28, !tbaa !6 - %53 = and i64 %52, -4, !dbg !28 - %54 = inttoptr i64 %53 to i64*, !dbg !28 - %55 = load i64, i64* %54, align 8, !dbg !28, !tbaa !6 - %56 = and i64 %55, 8, !dbg !28 - %57 = icmp eq i64 %56, 0, !dbg !28 - br i1 %57, label %58, label %60, !dbg !28, !prof !66 - -58: ; preds = %vm_get_ep.exit30 - %59 = getelementptr inbounds i64, i64* %54, i64 -5, !dbg !28 - store i64 %send, i64* %59, align 8, !dbg !28, !tbaa !6 + %54 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !28, !tbaa !33 + %55 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %54, i64 0, i32 2, !dbg !28 + %56 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %55, align 8, !dbg !28, !tbaa !45 + %57 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %56, i64 0, i32 4, !dbg !28 + %58 = load i64*, i64** %57, align 8, !dbg !28 + %59 = getelementptr inbounds i64, i64* %58, i64 -1, !dbg !28 + %60 = load i64, i64* %59, align 8, !dbg !28, !tbaa !6 + %61 = and i64 %60, -4, !dbg !28 + %62 = inttoptr i64 %61 to i64*, !dbg !28 + %63 = load i64, i64* %62, align 8, !dbg !28, !tbaa !6 + %64 = and i64 %63, 8, !dbg !28 + %65 = icmp eq i64 %64, 0, !dbg !28 + br i1 %65, label %66, label %68, !dbg !28, !prof !66 + +66: ; preds = %vm_get_ep.exit33 + %67 = getelementptr inbounds i64, i64* %62, i64 -5, !dbg !28 + store i64 %send, i64* %67, align 8, !dbg !28, !tbaa !6 br label %blockExit, !dbg !28 -60: ; preds = %vm_get_ep.exit30 - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %54, i32 noundef -5, i64 %send) #11, !dbg !28 +68: ; preds = %vm_get_ep.exit33 + tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %62, i32 noundef -5, i64 %send) #12, !dbg !28 br label %blockExit, !dbg !28 -vm_get_ep.exit: ; preds = %vm_get_ep.exit34 +vm_get_ep.exit: ; preds = %afterSend store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %8, align 8, !tbaa !33 - %61 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !74, !tbaa !33 - %62 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %61, i64 0, i32 2, !dbg !74 - %63 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %62, align 8, !dbg !74, !tbaa !45 - %64 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %63, i64 0, i32 4, !dbg !74 - %65 = load i64*, i64** %64, align 8, !dbg !74 - %66 = getelementptr inbounds i64, i64* %65, i64 -1, !dbg !74 - %67 = load i64, i64* %66, align 8, !dbg !74, !tbaa !6 - %68 = and i64 %67, -4, !dbg !74 - %69 = inttoptr i64 %68 to i64*, !dbg !74 - %70 = load i64, i64* %69, align 8, !dbg !74, !tbaa !6 - %71 = and i64 %70, 8, !dbg !74 - %72 = icmp eq i64 %71, 0, !dbg !74 - br i1 %72, label %73, label %75, !dbg !74, !prof !66 - -73: ; preds = %vm_get_ep.exit - %74 = getelementptr inbounds i64, i64* %69, i64 -7, !dbg !74 - store i64 20, i64* %74, align 8, !dbg !74, !tbaa !6 - br label %blockExit, !dbg !74 - -75: ; preds = %vm_get_ep.exit - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %69, i32 noundef -7, i64 noundef 20) #11, !dbg !74 - br label %blockExit, !dbg !74 + %69 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !76, !tbaa !33 + %70 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %69, i64 0, i32 2, !dbg !76 + %71 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %70, align 8, !dbg !76, !tbaa !45 + %72 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %71, i64 0, i32 4, !dbg !76 + %73 = load i64*, i64** %72, align 8, !dbg !76 + %74 = getelementptr inbounds i64, i64* %73, i64 -1, !dbg !76 + %75 = load i64, i64* %74, align 8, !dbg !76, !tbaa !6 + %76 = and i64 %75, -4, !dbg !76 + %77 = inttoptr i64 %76 to i64*, !dbg !76 + %78 = load i64, i64* %77, align 8, !dbg !76, !tbaa !6 + %79 = and i64 %78, 8, !dbg !76 + %80 = icmp eq i64 %79, 0, !dbg !76 + br i1 %80, label %81, label %83, !dbg !76, !prof !66 + +81: ; preds = %vm_get_ep.exit + %82 = getelementptr inbounds i64, i64* %77, i64 -7, !dbg !76 + store i64 20, i64* %82, align 8, !dbg !76, !tbaa !6 + br label %blockExit, !dbg !76 + +83: ; preds = %vm_get_ep.exit + tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %77, i32 noundef -7, i64 noundef 20) #12, !dbg !76 + br label %blockExit, !dbg !76 + +afterSend: ; preds = %86, %vm_get_ep.exit37 + %84 = and i64 %24, -9, !dbg !26 + %85 = icmp ne i64 %84, 0, !dbg !26 + br i1 %85, label %vm_get_ep.exit35, label %vm_get_ep.exit, !dbg !26 + +86: ; preds = %vm_get_ep.exit37 + %87 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 8, !dbg !26 + %88 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %87, align 8, !dbg !26, !tbaa !77 + %89 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %88, i32 noundef 0) #12, !dbg !26 + br label %afterSend, !dbg !26 } ; Function Attrs: ssp -define internal noundef i64 @"func_A.4test$block_3"(i64** nocapture nofree readnone %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #7 !dbg !32 { +define internal noundef i64 @"func_A.4test$block_3"(i64** nocapture nofree readnone %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #8 !dbg !32 { functionEntryInitializers: %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !33 %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 @@ -607,13 +631,13 @@ functionEntryInitializers: %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 %4 = load i64, i64* %3, align 8, !tbaa !69 %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.4test$block_3", align 8 - tail call void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #11 + tail call void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #12 %5 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !33 %6 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %5, i64 0, i32 2 %7 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %6, align 8, !tbaa !45 %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %7, i64 0, i32 0 store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %8, align 8, !tbaa !33 - %rubyStr_ensure = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 10), align 8, !dbg !75, !invariant.load !5 + %rubyStr_ensure = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 10), align 8, !dbg !78, !invariant.load !5 %9 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !31, !tbaa !33 %10 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 2, !dbg !31 %11 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %10, align 8, !dbg !31, !tbaa !45 @@ -630,7 +654,7 @@ functionEntryInitializers: } ; Function Attrs: ssp -define internal noundef i64 @"func_A.4test$block_4"(i64** nocapture nonnull writeonly align 8 dereferenceable(8) %pc, i64 %localsOffset, %struct.rb_control_frame_struct* %cfp) #7 !dbg !30 { +define internal noundef i64 @"func_A.4test$block_4"(i64** nocapture nonnull writeonly align 8 dereferenceable(8) %pc, i64 %localsOffset, %struct.rb_control_frame_struct* %cfp) #8 !dbg !30 { functionEntryInitializers: %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !33 %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 @@ -638,7 +662,7 @@ functionEntryInitializers: %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 %4 = load i64, i64* %3, align 8, !tbaa !69 store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 16), i64** %pc, align 8, !tbaa !33 - %rubyStr_else = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 9), align 8, !dbg !76, !invariant.load !5 + %rubyStr_else = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 9), align 8, !dbg !79, !invariant.load !5 %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !29 %6 = load i64*, i64** %5, align 8, !dbg !29 store i64 %4, i64* %6, align 8, !dbg !29, !tbaa !6 @@ -660,7 +684,7 @@ functionEntryInitializers: br label %sorbet_writeLocal.exit, !dbg !29 16: ; preds = %functionEntryInitializers - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %10, i32 noundef -5, i64 %send) #11, !dbg !29 + tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %10, i32 noundef -5, i64 %send) #12, !dbg !29 br label %sorbet_writeLocal.exit, !dbg !29 sorbet_writeLocal.exit: ; preds = %14, %16 @@ -668,34 +692,35 @@ sorbet_writeLocal.exit: ; preds = %14, %16 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #8 +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #9 ; Function Attrs: nofree nosync nounwind willreturn -declare void @llvm.assume(i1 noundef) #9 +declare void @llvm.assume(i1 noundef) #10 ; Function Attrs: ssp -define linkonce void @const_recompute_A() local_unnamed_addr #7 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([273 x i8], [273 x i8]* @sorbet_moduleStringTable, i64 0, i64 60), i64 1) +define linkonce void @const_recompute_A() local_unnamed_addr #8 { + %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([278 x i8], [278 x i8]* @sorbet_moduleStringTable, i64 0, i64 60), i64 1) store i64 %1, i64* @guarded_const_A, align 8 %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !54 store i64 %2, i64* @guard_epoch_A, align 8 ret void } -attributes #0 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { argmemonly nofree nosync nounwind willreturn } -attributes #3 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #5 = { sspreq } -attributes #6 = { nounwind sspreq uwtable } -attributes #7 = { ssp } -attributes #8 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #9 = { nofree nosync nounwind willreturn } -attributes #10 = { noreturn nounwind } -attributes #11 = { nounwind } -attributes #12 = { nounwind allocsize(0,1) } -attributes #13 = { noreturn } +attributes #0 = { nounwind readnone willreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { argmemonly nofree nosync nounwind willreturn } +attributes #4 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #5 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #6 = { sspreq } +attributes #7 = { nounwind sspreq uwtable } +attributes #8 = { ssp } +attributes #9 = { argmemonly nofree nosync nounwind willreturn writeonly } +attributes #10 = { nofree nosync nounwind willreturn } +attributes #11 = { noreturn nounwind } +attributes #12 = { nounwind } +attributes #13 = { nounwind allocsize(0,1) } +attributes #14 = { noreturn } !llvm.module.flags = !{!0, !1, !2} !llvm.dbg.cu = !{!3} @@ -767,13 +792,16 @@ attributes #13 = { noreturn } !64 = !{!"branch_weights", i32 4001, i32 4000000} !65 = !DILocation(line: 0, scope: !23) !66 = !{!"branch_weights", i32 2000, i32 1} -!67 = !DILocation(line: 8, column: 7, scope: !23) +!67 = !DILocation(line: 13, column: 5, scope: !23) !68 = !DILocation(line: 20, column: 3, scope: !23) !69 = !{!49, !7, i64 24} !70 = !DILocation(line: 8, column: 12, scope: !22) !71 = !DILocation(line: 11, column: 15, scope: !22) -!72 = !DILocation(line: 0, scope: !27) -!73 = !DILocation(line: 13, column: 5, scope: !27) -!74 = !DILocation(line: 8, column: 7, scope: !27) -!75 = !DILocation(line: 18, column: 12, scope: !32) -!76 = !DILocation(line: 16, column: 12, scope: !30) +!72 = !{!46, !40, i64 40} +!73 = !{!46, !40, i64 44} +!74 = !DILocation(line: 0, scope: !27) +!75 = !DILocation(line: 13, column: 5, scope: !27) +!76 = !DILocation(line: 8, column: 7, scope: !27) +!77 = !{!46, !34, i64 56} +!78 = !DILocation(line: 18, column: 12, scope: !32) +!79 = !DILocation(line: 16, column: 12, scope: !30) diff --git a/test/testdata/compiler/float-intrinsics.opt.ll.exp b/test/testdata/compiler/float-intrinsics.opt.ll.exp index 53fe81def2..4f8f4fa733 100644 --- a/test/testdata/compiler/float-intrinsics.opt.ll.exp +++ b/test/testdata/compiler/float-intrinsics.opt.ll.exp @@ -2,16 +2,17 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -27,34 +28,34 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.rb_fiber_struct = type opaque -%struct.anon.3 = type { [65 x i64] } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -68,7 +69,7 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } +%struct.anon.6 = type { i64, i64, i64, i64 } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } %struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } @@ -711,7 +712,7 @@ functionEntryInitializers: %33 = getelementptr inbounds i64, i64* %32, i64 1, !dbg !18 store i64* %33, i64** %30, align 8, !dbg !18 %send42 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_returns, i64 0), !dbg !18 - ret i64 %send42, !dbg !121 + ret i64 %send42, !dbg !18 } ; Function Attrs: ssp @@ -778,7 +779,7 @@ functionEntryInitializers: %33 = getelementptr inbounds i64, i64* %32, i64 1, !dbg !22 store i64* %33, i64** %30, align 8, !dbg !22 %send42 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_returns.5, i64 0), !dbg !22 - ret i64 %send42, !dbg !122 + ret i64 %send42, !dbg !22 } ; Function Attrs: ssp @@ -854,7 +855,7 @@ functionEntryInitializers: %37 = getelementptr inbounds i64, i64* %36, i64 1, !dbg !26 store i64* %37, i64** %34, align 8, !dbg !26 %send36 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_returns.8, i64 0), !dbg !26 - ret i64 %send36, !dbg !123 + ret i64 %send36, !dbg !26 } ; Function Attrs: ssp @@ -930,7 +931,7 @@ functionEntryInitializers: %37 = getelementptr inbounds i64, i64* %36, i64 1, !dbg !29 store i64* %37, i64** %34, align 8, !dbg !29 %send36 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_returns.11, i64 0), !dbg !29 - ret i64 %send36, !dbg !124 + ret i64 %send36, !dbg !29 } ; Function Attrs: ssp @@ -998,15 +999,15 @@ entryInitializers: ; Function Attrs: sspreq define void @Init_float-intrinsics() local_unnamed_addr #9 { entry: - %positional_table.i = alloca i64, i32 2, align 8, !dbg !125 - %positional_table320.i = alloca i64, i32 2, align 8, !dbg !126 - %positional_table334.i = alloca i64, i32 2, align 8, !dbg !127 - %positional_table348.i = alloca i64, i32 2, align 8, !dbg !128 + %positional_table.i = alloca i64, i32 2, align 8, !dbg !121 + %positional_table320.i = alloca i64, i32 2, align 8, !dbg !122 + %positional_table334.i = alloca i64, i32 2, align 8, !dbg !123 + %positional_table348.i = alloca i64, i32 2, align 8, !dbg !124 %realpath = tail call i64 @sorbet_readRealpath() tail call fastcc void @sorbet_globalConstructors(i64 %realpath) %0 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !72 %1 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %0, i64 0, i32 18 - %2 = load i64, i64* %1, align 8, !tbaa !129 + %2 = load i64, i64* %1, align 8, !tbaa !125 %3 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !72 %4 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %3, i64 0, i32 2 %5 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !113 @@ -1028,23 +1029,23 @@ entry: store i64 %14, i64* %12, align 8, !tbaa !6 tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %3, %struct.rb_control_frame_struct* %5, %struct.rb_iseq_struct* %stackFrame.i) #17 %15 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 0 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %15, align 8, !dbg !137, !tbaa !72 - %rubyId_plus.i = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !dbg !138, !invariant.load !5 - %rawSym.i = tail call i64 @rb_id2sym(i64 %rubyId_plus.i) #17, !dbg !138 - tail call void @sorbet_vm_register_sig(i64 noundef 0, i64 %rawSym.i, i64 %2, i64 noundef 8, i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_1") #17, !dbg !138 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 12), i64** %15, align 8, !dbg !138, !tbaa !72 - %rubyId_minus.i = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !dbg !139, !invariant.load !5 - %rawSym257.i = tail call i64 @rb_id2sym(i64 %rubyId_minus.i) #17, !dbg !139 - tail call void @sorbet_vm_register_sig(i64 noundef 0, i64 %rawSym257.i, i64 %2, i64 noundef 8, i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_2") #17, !dbg !139 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 17), i64** %15, align 8, !dbg !139, !tbaa !72 - %rubyId_lt.i = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !dbg !140, !invariant.load !5 - %rawSym271.i = tail call i64 @rb_id2sym(i64 %rubyId_lt.i) #17, !dbg !140 - tail call void @sorbet_vm_register_sig(i64 noundef 0, i64 %rawSym271.i, i64 %2, i64 noundef 8, i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_3") #17, !dbg !140 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 22), i64** %15, align 8, !dbg !140, !tbaa !72 - %rubyId_lte.i = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 6), align 8, !dbg !141, !invariant.load !5 - %rawSym285.i = tail call i64 @rb_id2sym(i64 %rubyId_lte.i) #17, !dbg !141 - tail call void @sorbet_vm_register_sig(i64 noundef 0, i64 %rawSym285.i, i64 %2, i64 noundef 8, i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_4") #17, !dbg !141 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %15, align 8, !dbg !141, !tbaa !72 + store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %15, align 8, !dbg !133, !tbaa !72 + %rubyId_plus.i = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !dbg !134, !invariant.load !5 + %rawSym.i = tail call i64 @rb_id2sym(i64 %rubyId_plus.i) #17, !dbg !134 + tail call void @sorbet_vm_register_sig(i64 noundef 0, i64 %rawSym.i, i64 %2, i64 noundef 8, i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_1") #17, !dbg !134 + store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 12), i64** %15, align 8, !dbg !134, !tbaa !72 + %rubyId_minus.i = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !dbg !135, !invariant.load !5 + %rawSym257.i = tail call i64 @rb_id2sym(i64 %rubyId_minus.i) #17, !dbg !135 + tail call void @sorbet_vm_register_sig(i64 noundef 0, i64 %rawSym257.i, i64 %2, i64 noundef 8, i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_2") #17, !dbg !135 + store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 17), i64** %15, align 8, !dbg !135, !tbaa !72 + %rubyId_lt.i = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !dbg !136, !invariant.load !5 + %rawSym271.i = tail call i64 @rb_id2sym(i64 %rubyId_lt.i) #17, !dbg !136 + tail call void @sorbet_vm_register_sig(i64 noundef 0, i64 %rawSym271.i, i64 %2, i64 noundef 8, i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_3") #17, !dbg !136 + store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 22), i64** %15, align 8, !dbg !136, !tbaa !72 + %rubyId_lte.i = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 6), align 8, !dbg !137, !invariant.load !5 + %rawSym285.i = tail call i64 @rb_id2sym(i64 %rubyId_lte.i) #17, !dbg !137 + tail call void @sorbet_vm_register_sig(i64 noundef 0, i64 %rawSym285.i, i64 %2, i64 noundef 8, i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_4") #17, !dbg !137 + store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %15, align 8, !dbg !137, !tbaa !72 %16 = load i64, i64* @"guard_epoch_T::Sig", align 8, !dbg !30 %17 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !30, !tbaa !118 %needTakeSlowPath = icmp ne i64 %16, %17, !dbg !30 @@ -1069,109 +1070,109 @@ entry: store i64* %26, i64** %23, align 8, !dbg !30 %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_extend, i64 0), !dbg !30 store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %15, align 8, !dbg !30, !tbaa !72 - %27 = load i64, i64* @rb_cObject, align 8, !dbg !125 - %stackFrame308.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#4plus", align 8, !dbg !125 - %28 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #18, !dbg !125 - %29 = bitcast i8* %28 to i16*, !dbg !125 - %30 = load i16, i16* %29, align 8, !dbg !125 - %31 = and i16 %30, -384, !dbg !125 - %32 = or i16 %31, 1, !dbg !125 - store i16 %32, i16* %29, align 8, !dbg !125 - %33 = getelementptr inbounds i8, i8* %28, i64 8, !dbg !125 - %34 = bitcast i8* %33 to i32*, !dbg !125 - store i32 2, i32* %34, align 8, !dbg !125, !tbaa !142 - %35 = getelementptr inbounds i8, i8* %28, i64 12, !dbg !125 - %36 = getelementptr inbounds i8, i8* %28, i64 4, !dbg !125 - %37 = bitcast i8* %36 to i32*, !dbg !125 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %35, i8 0, i64 20, i1 false) #17, !dbg !125 - store i32 2, i32* %37, align 4, !dbg !125, !tbaa !145 - %rubyId_x.i = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 11), align 8, !dbg !125, !invariant.load !5 - store i64 %rubyId_x.i, i64* %positional_table.i, align 8, !dbg !125 - %rubyId_y.i = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 12), align 8, !dbg !125, !invariant.load !5 - %38 = getelementptr i64, i64* %positional_table.i, i32 1, !dbg !125 - store i64 %rubyId_y.i, i64* %38, align 8, !dbg !125 - %39 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 2, i64 noundef 8) #18, !dbg !125 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %39, i8* nocapture noundef nonnull readonly align 8 dereferenceable(16) %6, i64 noundef 16, i1 noundef false) #17, !dbg !125 - %40 = getelementptr inbounds i8, i8* %28, i64 32, !dbg !125 - %41 = bitcast i8* %40 to i8**, !dbg !125 - store i8* %39, i8** %41, align 8, !dbg !125, !tbaa !146 - tail call void @sorbet_vm_define_method(i64 %27, i8* noundef getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 0), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#4plus", i8* nonnull %28, %struct.rb_iseq_struct* %stackFrame308.i, i1 noundef zeroext false) #17, !dbg !125 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %15, align 8, !dbg !125, !tbaa !72 - %stackFrame318.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#5minus", align 8, !dbg !126 - %42 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #18, !dbg !126 - %43 = bitcast i8* %42 to i16*, !dbg !126 - %44 = load i16, i16* %43, align 8, !dbg !126 - %45 = and i16 %44, -384, !dbg !126 - %46 = or i16 %45, 1, !dbg !126 - store i16 %46, i16* %43, align 8, !dbg !126 - %47 = getelementptr inbounds i8, i8* %42, i64 8, !dbg !126 - %48 = bitcast i8* %47 to i32*, !dbg !126 - store i32 2, i32* %48, align 8, !dbg !126, !tbaa !142 - %49 = getelementptr inbounds i8, i8* %42, i64 12, !dbg !126 - %50 = getelementptr inbounds i8, i8* %42, i64 4, !dbg !126 - %51 = bitcast i8* %50 to i32*, !dbg !126 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %49, i8 0, i64 20, i1 false) #17, !dbg !126 - store i32 2, i32* %51, align 4, !dbg !126, !tbaa !145 - store i64 %rubyId_x.i, i64* %positional_table320.i, align 8, !dbg !126 - %52 = getelementptr i64, i64* %positional_table320.i, i32 1, !dbg !126 - store i64 %rubyId_y.i, i64* %52, align 8, !dbg !126 - %53 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 2, i64 noundef 8) #18, !dbg !126 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %53, i8* nocapture noundef nonnull readonly align 8 dereferenceable(16) %7, i64 noundef 16, i1 noundef false) #17, !dbg !126 - %54 = getelementptr inbounds i8, i8* %42, i64 32, !dbg !126 - %55 = bitcast i8* %54 to i8**, !dbg !126 - store i8* %53, i8** %55, align 8, !dbg !126, !tbaa !146 - tail call void @sorbet_vm_define_method(i64 %27, i8* getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 107), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#5minus", i8* nonnull %42, %struct.rb_iseq_struct* %stackFrame318.i, i1 noundef zeroext false) #17, !dbg !126 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %15, align 8, !dbg !126, !tbaa !72 - %stackFrame332.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#2lt", align 8, !dbg !127 - %56 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #18, !dbg !127 - %57 = bitcast i8* %56 to i16*, !dbg !127 - %58 = load i16, i16* %57, align 8, !dbg !127 - %59 = and i16 %58, -384, !dbg !127 - %60 = or i16 %59, 1, !dbg !127 - store i16 %60, i16* %57, align 8, !dbg !127 - %61 = getelementptr inbounds i8, i8* %56, i64 8, !dbg !127 - %62 = bitcast i8* %61 to i32*, !dbg !127 - store i32 2, i32* %62, align 8, !dbg !127, !tbaa !142 - %63 = getelementptr inbounds i8, i8* %56, i64 12, !dbg !127 - %64 = getelementptr inbounds i8, i8* %56, i64 4, !dbg !127 - %65 = bitcast i8* %64 to i32*, !dbg !127 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %63, i8 0, i64 20, i1 false) #17, !dbg !127 - store i32 2, i32* %65, align 4, !dbg !127, !tbaa !145 - store i64 %rubyId_x.i, i64* %positional_table334.i, align 8, !dbg !127 - %66 = getelementptr i64, i64* %positional_table334.i, i32 1, !dbg !127 - store i64 %rubyId_y.i, i64* %66, align 8, !dbg !127 - %67 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 2, i64 noundef 8) #18, !dbg !127 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %67, i8* nocapture noundef nonnull readonly align 8 dereferenceable(16) %8, i64 noundef 16, i1 noundef false) #17, !dbg !127 - %68 = getelementptr inbounds i8, i8* %56, i64 32, !dbg !127 - %69 = bitcast i8* %68 to i8**, !dbg !127 - store i8* %67, i8** %69, align 8, !dbg !127, !tbaa !146 - tail call void @sorbet_vm_define_method(i64 %27, i8* getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 115), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#2lt", i8* nonnull %56, %struct.rb_iseq_struct* %stackFrame332.i, i1 noundef zeroext false) #17, !dbg !127 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 23), i64** %15, align 8, !dbg !127, !tbaa !72 - %stackFrame346.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#3lte", align 8, !dbg !128 - %70 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #18, !dbg !128 - %71 = bitcast i8* %70 to i16*, !dbg !128 - %72 = load i16, i16* %71, align 8, !dbg !128 - %73 = and i16 %72, -384, !dbg !128 - %74 = or i16 %73, 1, !dbg !128 - store i16 %74, i16* %71, align 8, !dbg !128 - %75 = getelementptr inbounds i8, i8* %70, i64 8, !dbg !128 - %76 = bitcast i8* %75 to i32*, !dbg !128 - store i32 2, i32* %76, align 8, !dbg !128, !tbaa !142 - %77 = getelementptr inbounds i8, i8* %70, i64 12, !dbg !128 - %78 = getelementptr inbounds i8, i8* %70, i64 4, !dbg !128 - %79 = bitcast i8* %78 to i32*, !dbg !128 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %77, i8 0, i64 20, i1 false) #17, !dbg !128 - store i32 2, i32* %79, align 4, !dbg !128, !tbaa !145 - store i64 %rubyId_x.i, i64* %positional_table348.i, align 8, !dbg !128 - %80 = getelementptr i64, i64* %positional_table348.i, i32 1, !dbg !128 - store i64 %rubyId_y.i, i64* %80, align 8, !dbg !128 - %81 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 2, i64 noundef 8) #18, !dbg !128 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %81, i8* nocapture noundef nonnull readonly align 8 dereferenceable(16) %9, i64 noundef 16, i1 noundef false) #17, !dbg !128 - %82 = getelementptr inbounds i8, i8* %70, i64 32, !dbg !128 - %83 = bitcast i8* %82 to i8**, !dbg !128 - store i8* %81, i8** %83, align 8, !dbg !128, !tbaa !146 - tail call void @sorbet_vm_define_method(i64 %27, i8* getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 131), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#3lte", i8* nonnull %70, %struct.rb_iseq_struct* %stackFrame346.i, i1 noundef zeroext false) #17, !dbg !128 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 27), i64** %15, align 8, !dbg !128, !tbaa !72 + %27 = load i64, i64* @rb_cObject, align 8, !dbg !121 + %stackFrame308.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#4plus", align 8, !dbg !121 + %28 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #18, !dbg !121 + %29 = bitcast i8* %28 to i16*, !dbg !121 + %30 = load i16, i16* %29, align 8, !dbg !121 + %31 = and i16 %30, -384, !dbg !121 + %32 = or i16 %31, 1, !dbg !121 + store i16 %32, i16* %29, align 8, !dbg !121 + %33 = getelementptr inbounds i8, i8* %28, i64 8, !dbg !121 + %34 = bitcast i8* %33 to i32*, !dbg !121 + store i32 2, i32* %34, align 8, !dbg !121, !tbaa !138 + %35 = getelementptr inbounds i8, i8* %28, i64 12, !dbg !121 + %36 = getelementptr inbounds i8, i8* %28, i64 4, !dbg !121 + %37 = bitcast i8* %36 to i32*, !dbg !121 + tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %35, i8 0, i64 20, i1 false) #17, !dbg !121 + store i32 2, i32* %37, align 4, !dbg !121, !tbaa !141 + %rubyId_x.i = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 11), align 8, !dbg !121, !invariant.load !5 + store i64 %rubyId_x.i, i64* %positional_table.i, align 8, !dbg !121 + %rubyId_y.i = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 12), align 8, !dbg !121, !invariant.load !5 + %38 = getelementptr i64, i64* %positional_table.i, i32 1, !dbg !121 + store i64 %rubyId_y.i, i64* %38, align 8, !dbg !121 + %39 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 2, i64 noundef 8) #18, !dbg !121 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %39, i8* nocapture noundef nonnull readonly align 8 dereferenceable(16) %6, i64 noundef 16, i1 noundef false) #17, !dbg !121 + %40 = getelementptr inbounds i8, i8* %28, i64 32, !dbg !121 + %41 = bitcast i8* %40 to i8**, !dbg !121 + store i8* %39, i8** %41, align 8, !dbg !121, !tbaa !142 + tail call void @sorbet_vm_define_method(i64 %27, i8* noundef getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 0), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#4plus", i8* nonnull %28, %struct.rb_iseq_struct* %stackFrame308.i, i1 noundef zeroext false) #17, !dbg !121 + store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %15, align 8, !dbg !121, !tbaa !72 + %stackFrame318.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#5minus", align 8, !dbg !122 + %42 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #18, !dbg !122 + %43 = bitcast i8* %42 to i16*, !dbg !122 + %44 = load i16, i16* %43, align 8, !dbg !122 + %45 = and i16 %44, -384, !dbg !122 + %46 = or i16 %45, 1, !dbg !122 + store i16 %46, i16* %43, align 8, !dbg !122 + %47 = getelementptr inbounds i8, i8* %42, i64 8, !dbg !122 + %48 = bitcast i8* %47 to i32*, !dbg !122 + store i32 2, i32* %48, align 8, !dbg !122, !tbaa !138 + %49 = getelementptr inbounds i8, i8* %42, i64 12, !dbg !122 + %50 = getelementptr inbounds i8, i8* %42, i64 4, !dbg !122 + %51 = bitcast i8* %50 to i32*, !dbg !122 + tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %49, i8 0, i64 20, i1 false) #17, !dbg !122 + store i32 2, i32* %51, align 4, !dbg !122, !tbaa !141 + store i64 %rubyId_x.i, i64* %positional_table320.i, align 8, !dbg !122 + %52 = getelementptr i64, i64* %positional_table320.i, i32 1, !dbg !122 + store i64 %rubyId_y.i, i64* %52, align 8, !dbg !122 + %53 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 2, i64 noundef 8) #18, !dbg !122 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %53, i8* nocapture noundef nonnull readonly align 8 dereferenceable(16) %7, i64 noundef 16, i1 noundef false) #17, !dbg !122 + %54 = getelementptr inbounds i8, i8* %42, i64 32, !dbg !122 + %55 = bitcast i8* %54 to i8**, !dbg !122 + store i8* %53, i8** %55, align 8, !dbg !122, !tbaa !142 + tail call void @sorbet_vm_define_method(i64 %27, i8* getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 107), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#5minus", i8* nonnull %42, %struct.rb_iseq_struct* %stackFrame318.i, i1 noundef zeroext false) #17, !dbg !122 + store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %15, align 8, !dbg !122, !tbaa !72 + %stackFrame332.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#2lt", align 8, !dbg !123 + %56 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #18, !dbg !123 + %57 = bitcast i8* %56 to i16*, !dbg !123 + %58 = load i16, i16* %57, align 8, !dbg !123 + %59 = and i16 %58, -384, !dbg !123 + %60 = or i16 %59, 1, !dbg !123 + store i16 %60, i16* %57, align 8, !dbg !123 + %61 = getelementptr inbounds i8, i8* %56, i64 8, !dbg !123 + %62 = bitcast i8* %61 to i32*, !dbg !123 + store i32 2, i32* %62, align 8, !dbg !123, !tbaa !138 + %63 = getelementptr inbounds i8, i8* %56, i64 12, !dbg !123 + %64 = getelementptr inbounds i8, i8* %56, i64 4, !dbg !123 + %65 = bitcast i8* %64 to i32*, !dbg !123 + tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %63, i8 0, i64 20, i1 false) #17, !dbg !123 + store i32 2, i32* %65, align 4, !dbg !123, !tbaa !141 + store i64 %rubyId_x.i, i64* %positional_table334.i, align 8, !dbg !123 + %66 = getelementptr i64, i64* %positional_table334.i, i32 1, !dbg !123 + store i64 %rubyId_y.i, i64* %66, align 8, !dbg !123 + %67 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 2, i64 noundef 8) #18, !dbg !123 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %67, i8* nocapture noundef nonnull readonly align 8 dereferenceable(16) %8, i64 noundef 16, i1 noundef false) #17, !dbg !123 + %68 = getelementptr inbounds i8, i8* %56, i64 32, !dbg !123 + %69 = bitcast i8* %68 to i8**, !dbg !123 + store i8* %67, i8** %69, align 8, !dbg !123, !tbaa !142 + tail call void @sorbet_vm_define_method(i64 %27, i8* getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 115), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#2lt", i8* nonnull %56, %struct.rb_iseq_struct* %stackFrame332.i, i1 noundef zeroext false) #17, !dbg !123 + store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 23), i64** %15, align 8, !dbg !123, !tbaa !72 + %stackFrame346.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#3lte", align 8, !dbg !124 + %70 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #18, !dbg !124 + %71 = bitcast i8* %70 to i16*, !dbg !124 + %72 = load i16, i16* %71, align 8, !dbg !124 + %73 = and i16 %72, -384, !dbg !124 + %74 = or i16 %73, 1, !dbg !124 + store i16 %74, i16* %71, align 8, !dbg !124 + %75 = getelementptr inbounds i8, i8* %70, i64 8, !dbg !124 + %76 = bitcast i8* %75 to i32*, !dbg !124 + store i32 2, i32* %76, align 8, !dbg !124, !tbaa !138 + %77 = getelementptr inbounds i8, i8* %70, i64 12, !dbg !124 + %78 = getelementptr inbounds i8, i8* %70, i64 4, !dbg !124 + %79 = bitcast i8* %78 to i32*, !dbg !124 + tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %77, i8 0, i64 20, i1 false) #17, !dbg !124 + store i32 2, i32* %79, align 4, !dbg !124, !tbaa !141 + store i64 %rubyId_x.i, i64* %positional_table348.i, align 8, !dbg !124 + %80 = getelementptr i64, i64* %positional_table348.i, i32 1, !dbg !124 + store i64 %rubyId_y.i, i64* %80, align 8, !dbg !124 + %81 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 2, i64 noundef 8) #18, !dbg !124 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %81, i8* nocapture noundef nonnull readonly align 8 dereferenceable(16) %9, i64 noundef 16, i1 noundef false) #17, !dbg !124 + %82 = getelementptr inbounds i8, i8* %70, i64 32, !dbg !124 + %83 = bitcast i8* %82 to i8**, !dbg !124 + store i8* %81, i8** %83, align 8, !dbg !124, !tbaa !142 + tail call void @sorbet_vm_define_method(i64 %27, i8* getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 131), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#3lte", i8* nonnull %70, %struct.rb_iseq_struct* %stackFrame346.i, i1 noundef zeroext false) #17, !dbg !124 + store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 27), i64** %15, align 8, !dbg !124, !tbaa !72 %84 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !31 %85 = load i64*, i64** %84, align 8, !dbg !31 store i64 %2, i64* %85, align 8, !dbg !31, !tbaa !6 @@ -1573,17 +1574,17 @@ declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #5 declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #5 ; Function Attrs: cold minsize noreturn nounwind sspreq uwtable -define internal fastcc void @"func_Object#2lt.cold.1"(i64 %0) unnamed_addr #12 !dbg !147 { +define internal fastcc void @"func_Object#2lt.cold.1"(i64 %0) unnamed_addr #12 !dbg !143 { newFuncRoot: tail call void @sorbet_cast_failure(i64 %0, i8* getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 94), i8* getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 120)) #15 unreachable } ; Function Attrs: cold minsize noreturn nounwind sspreq uwtable -define internal fastcc void @"func_Object#2lt.cold.3"(i64 %rawArg_x) unnamed_addr #12 !dbg !149 { +define internal fastcc void @"func_Object#2lt.cold.3"(i64 %rawArg_x) unnamed_addr #12 !dbg !145 { newFuncRoot: - tail call void @sorbet_cast_failure(i64 %rawArg_x, i8* getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 48), i8* getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 62)) #15, !dbg !150 - unreachable, !dbg !150 + tail call void @sorbet_cast_failure(i64 %rawArg_x, i8* getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 48), i8* getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 62)) #15, !dbg !146 + unreachable, !dbg !146 } ; Function Attrs: nofree nosync nounwind willreturn @@ -1760,33 +1761,29 @@ attributes #18 = { nounwind allocsize(0,1) } !118 = !{!119, !119, i64 0} !119 = !{!"long long", !8, i64 0} !120 = !{!"branch_weights", i32 1, i32 10000} -!121 = !DILocation(line: 7, column: 1, scope: !16) -!122 = !DILocation(line: 12, column: 1, scope: !21) -!123 = !DILocation(line: 17, column: 1, scope: !25) -!124 = !DILocation(line: 22, column: 1, scope: !28) -!125 = !DILocation(line: 8, column: 1, scope: !17) -!126 = !DILocation(line: 13, column: 1, scope: !17) -!127 = !DILocation(line: 18, column: 1, scope: !17) -!128 = !DILocation(line: 23, column: 1, scope: !17) -!129 = !{!130, !7, i64 400} -!130 = !{!"rb_vm_struct", !7, i64 0, !131, i64 8, !73, i64 192, !73, i64 200, !73, i64 208, !119, i64 216, !8, i64 224, !132, i64 264, !132, i64 280, !132, i64 296, !132, i64 312, !7, i64 328, !87, i64 336, !87, i64 340, !87, i64 344, !87, i64 344, !87, i64 344, !87, i64 344, !87, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !73, i64 456, !73, i64 464, !134, i64 472, !135, i64 992, !73, i64 1016, !73, i64 1024, !87, i64 1032, !87, i64 1036, !132, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !87, i64 1136, !73, i64 1144, !73, i64 1152, !73, i64 1160, !73, i64 1168, !73, i64 1176, !73, i64 1184, !87, i64 1192, !136, i64 1200, !8, i64 1232} -!131 = !{!"rb_global_vm_lock_struct", !73, i64 0, !8, i64 8, !132, i64 48, !73, i64 64, !87, i64 72, !8, i64 80, !8, i64 128, !87, i64 176, !87, i64 180} -!132 = !{!"list_head", !133, i64 0} -!133 = !{!"list_node", !73, i64 0, !73, i64 8} -!134 = !{!"", !8, i64 0} -!135 = !{!"rb_hook_list_struct", !73, i64 0, !87, i64 8, !87, i64 12, !87, i64 16} -!136 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!137 = !DILocation(line: 0, scope: !17) -!138 = !DILocation(line: 7, column: 1, scope: !17) -!139 = !DILocation(line: 12, column: 1, scope: !17) -!140 = !DILocation(line: 17, column: 1, scope: !17) -!141 = !DILocation(line: 22, column: 1, scope: !17) -!142 = !{!143, !87, i64 8} -!143 = !{!"rb_sorbet_param_struct", !144, i64 0, !87, i64 4, !87, i64 8, !87, i64 12, !87, i64 16, !87, i64 20, !87, i64 24, !87, i64 28, !73, i64 32, !87, i64 40, !87, i64 44, !87, i64 48, !87, i64 52, !73, i64 56} -!144 = !{!"", !87, i64 0, !87, i64 0, !87, i64 0, !87, i64 0, !87, i64 0, !87, i64 0, !87, i64 0, !87, i64 0, !87, i64 1, !87, i64 1} -!145 = !{!143, !87, i64 4} -!146 = !{!143, !73, i64 32} -!147 = distinct !DISubprogram(name: "func_Object#2lt.cold.1", linkageName: "func_Object#2lt.cold.1", scope: null, file: !4, type: !148, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) -!148 = !DISubroutineType(types: !5) -!149 = distinct !DISubprogram(name: "func_Object#2lt.cold.3", linkageName: "func_Object#2lt.cold.3", scope: null, file: !4, type: !148, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) -!150 = !DILocation(line: 18, column: 8, scope: !149) +!121 = !DILocation(line: 8, column: 1, scope: !17) +!122 = !DILocation(line: 13, column: 1, scope: !17) +!123 = !DILocation(line: 18, column: 1, scope: !17) +!124 = !DILocation(line: 23, column: 1, scope: !17) +!125 = !{!126, !7, i64 400} +!126 = !{!"rb_vm_struct", !7, i64 0, !127, i64 8, !73, i64 192, !73, i64 200, !73, i64 208, !119, i64 216, !8, i64 224, !128, i64 264, !128, i64 280, !128, i64 296, !128, i64 312, !7, i64 328, !87, i64 336, !87, i64 340, !87, i64 344, !87, i64 344, !87, i64 344, !87, i64 344, !87, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !73, i64 456, !73, i64 464, !130, i64 472, !131, i64 992, !73, i64 1016, !73, i64 1024, !87, i64 1032, !87, i64 1036, !128, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !87, i64 1136, !73, i64 1144, !73, i64 1152, !73, i64 1160, !73, i64 1168, !73, i64 1176, !73, i64 1184, !87, i64 1192, !132, i64 1200, !8, i64 1232} +!127 = !{!"rb_global_vm_lock_struct", !73, i64 0, !8, i64 8, !128, i64 48, !73, i64 64, !87, i64 72, !8, i64 80, !8, i64 128, !87, i64 176, !87, i64 180} +!128 = !{!"list_head", !129, i64 0} +!129 = !{!"list_node", !73, i64 0, !73, i64 8} +!130 = !{!"", !8, i64 0} +!131 = !{!"rb_hook_list_struct", !73, i64 0, !87, i64 8, !87, i64 12, !87, i64 16} +!132 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} +!133 = !DILocation(line: 0, scope: !17) +!134 = !DILocation(line: 7, column: 1, scope: !17) +!135 = !DILocation(line: 12, column: 1, scope: !17) +!136 = !DILocation(line: 17, column: 1, scope: !17) +!137 = !DILocation(line: 22, column: 1, scope: !17) +!138 = !{!139, !87, i64 8} +!139 = !{!"rb_sorbet_param_struct", !140, i64 0, !87, i64 4, !87, i64 8, !87, i64 12, !87, i64 16, !87, i64 20, !87, i64 24, !87, i64 28, !73, i64 32, !87, i64 40, !87, i64 44, !87, i64 48, !87, i64 52, !73, i64 56} +!140 = !{!"", !87, i64 0, !87, i64 0, !87, i64 0, !87, i64 0, !87, i64 0, !87, i64 0, !87, i64 0, !87, i64 0, !87, i64 1, !87, i64 1} +!141 = !{!139, !87, i64 4} +!142 = !{!139, !73, i64 32} +!143 = distinct !DISubprogram(name: "func_Object#2lt.cold.1", linkageName: "func_Object#2lt.cold.1", scope: null, file: !4, type: !144, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) +!144 = !DISubroutineType(types: !5) +!145 = distinct !DISubprogram(name: "func_Object#2lt.cold.3", linkageName: "func_Object#2lt.cold.3", scope: null, file: !4, type: !144, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) +!146 = !DILocation(line: 18, column: 8, scope: !145) diff --git a/test/testdata/compiler/globalfields.opt.ll.exp b/test/testdata/compiler/globalfields.opt.ll.exp index 1f01ea83ae..a80ef8b34f 100644 --- a/test/testdata/compiler/globalfields.opt.ll.exp +++ b/test/testdata/compiler/globalfields.opt.ll.exp @@ -2,16 +2,17 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -27,34 +28,34 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.rb_fiber_struct = type opaque -%struct.anon.3 = type { [65 x i64] } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -68,7 +69,7 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } +%struct.anon.6 = type { i64, i64, i64, i64 } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } %struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } diff --git a/test/testdata/compiler/hello.opt.ll.exp b/test/testdata/compiler/hello.opt.ll.exp index 657cc043d2..c787e5e27a 100644 --- a/test/testdata/compiler/hello.opt.ll.exp +++ b/test/testdata/compiler/hello.opt.ll.exp @@ -2,16 +2,17 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -27,34 +28,34 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.rb_fiber_struct = type opaque -%struct.anon.3 = type { [65 x i64] } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -68,7 +69,7 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } +%struct.anon.6 = type { i64, i64, i64, i64 } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } %struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } diff --git a/test/testdata/compiler/intrinsics/bang.opt.ll.exp b/test/testdata/compiler/intrinsics/bang.opt.ll.exp index 71bff95708..ea541341c9 100644 --- a/test/testdata/compiler/intrinsics/bang.opt.ll.exp +++ b/test/testdata/compiler/intrinsics/bang.opt.ll.exp @@ -2,10 +2,10 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -21,27 +21,28 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_fiber_struct = type opaque -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.anon.3 = type { [65 x i64] } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -49,24 +50,24 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_objspace = type opaque %struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.anon.6 = type { i64, i64, i64, i64 } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %union.pthread_mutex_t = type { %struct.__pthread_mutex_s } %struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } %struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } diff --git a/test/testdata/compiler/intrinsics/t_must.opt.ll.exp b/test/testdata/compiler/intrinsics/t_must.opt.ll.exp index 4eee457e73..8d9f38bd68 100644 --- a/test/testdata/compiler/intrinsics/t_must.opt.ll.exp +++ b/test/testdata/compiler/intrinsics/t_must.opt.ll.exp @@ -2,10 +2,10 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -21,27 +21,28 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_fiber_struct = type opaque -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.anon.3 = type { [65 x i64] } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -49,24 +50,24 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_objspace = type opaque %struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.anon.6 = type { i64, i64, i64, i64 } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %union.pthread_mutex_t = type { %struct.__pthread_mutex_s } %struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } %struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } @@ -94,102 +95,104 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 @"stackFramePrecomputed_func_Test.14test_known_nil$block_2" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 @"stackFramePrecomputed_func_Test.14test_known_nil$block_3" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 @"" = internal unnamed_addr global i64 0 -@"ic_is_a?" = internal global %struct.FunctionInlineCache zeroinitializer +@"ic_===" = internal global %struct.FunctionInlineCache zeroinitializer @ic_puts = internal global %struct.FunctionInlineCache zeroinitializer @stackFramePrecomputed_func_Test.16test_nilable_arg = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 @"stackFramePrecomputed_func_Test.16test_nilable_arg$block_2" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 @"stackFramePrecomputed_func_Test.16test_nilable_arg$block_3" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 @ic_puts.3 = internal global %struct.FunctionInlineCache zeroinitializer -@"ic_is_a?.4" = internal global %struct.FunctionInlineCache zeroinitializer +@"ic_===.4" = internal global %struct.FunctionInlineCache zeroinitializer @ic_puts.5 = internal global %struct.FunctionInlineCache zeroinitializer @"stackFramePrecomputed_func_Test.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@sorbet_moduleStringTable = internal unnamed_addr constant [358 x i8] c"\00test/testdata/compiler/intrinsics/t_must.rb\00Test\00test_known_nil\00test_nilable_arg\00master\00\00\00\00\00rescue in test_known_nil\00ensure in test_known_nil\00T\00must\00StandardError\00is_a?\00puts\00arg\00rescue in test_nilable_arg\00ensure in test_nilable_arg\00 wasn't nil\00\00\00normal\00", align 1 +@sorbet_moduleStringTable = internal unnamed_addr constant [363 x i8] c"\00test/testdata/compiler/intrinsics/t_must.rb\00Test\00test_known_nil\00test_nilable_arg\00master\00\00\00\00\00rescue in test_known_nil\00ensure in test_known_nil\00T\00must\00StandardError\00Module\00===\00puts\00arg\00rescue in test_nilable_arg\00ensure in test_nilable_arg\00 wasn't nil\00\00\00normal\00", align 1 @sorbet_moduleIDTable = internal unnamed_addr global [18 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [18 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 66, i32 14 }, %struct.rb_code_position_struct { i32 81, i32 16 }, %struct.rb_code_position_struct { i32 105, i32 16 }, %struct.rb_code_position_struct { i32 122, i32 7 }, %struct.rb_code_position_struct { i32 130, i32 18 }, %struct.rb_code_position_struct { i32 149, i32 14 }, %struct.rb_code_position_struct { i32 164, i32 24 }, %struct.rb_code_position_struct { i32 189, i32 24 }, %struct.rb_code_position_struct { i32 216, i32 4 }, %struct.rb_code_position_struct { i32 235, i32 5 }, %struct.rb_code_position_struct { i32 241, i32 4 }, %struct.rb_code_position_struct { i32 246, i32 3 }, %struct.rb_code_position_struct { i32 250, i32 26 }, %struct.rb_code_position_struct { i32 277, i32 26 }, %struct.rb_code_position_struct { i32 316, i32 20 }, %struct.rb_code_position_struct { i32 337, i32 13 }, %struct.rb_code_position_struct { i32 351, i32 6 }], align 8 +@sorbet_moduleIDDescriptors = internal unnamed_addr constant [18 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 66, i32 14 }, %struct.rb_code_position_struct { i32 81, i32 16 }, %struct.rb_code_position_struct { i32 105, i32 16 }, %struct.rb_code_position_struct { i32 122, i32 7 }, %struct.rb_code_position_struct { i32 130, i32 18 }, %struct.rb_code_position_struct { i32 149, i32 14 }, %struct.rb_code_position_struct { i32 164, i32 24 }, %struct.rb_code_position_struct { i32 189, i32 24 }, %struct.rb_code_position_struct { i32 216, i32 4 }, %struct.rb_code_position_struct { i32 242, i32 3 }, %struct.rb_code_position_struct { i32 246, i32 4 }, %struct.rb_code_position_struct { i32 251, i32 3 }, %struct.rb_code_position_struct { i32 255, i32 26 }, %struct.rb_code_position_struct { i32 282, i32 26 }, %struct.rb_code_position_struct { i32 321, i32 20 }, %struct.rb_code_position_struct { i32 342, i32 13 }, %struct.rb_code_position_struct { i32 356, i32 6 }], align 8 @sorbet_moduleRubyStringTable = internal unnamed_addr global [10 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [10 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 43 }, %struct.rb_code_position_struct { i32 66, i32 14 }, %struct.rb_code_position_struct { i32 164, i32 24 }, %struct.rb_code_position_struct { i32 189, i32 24 }, %struct.rb_code_position_struct { i32 81, i32 16 }, %struct.rb_code_position_struct { i32 250, i32 26 }, %struct.rb_code_position_struct { i32 277, i32 26 }, %struct.rb_code_position_struct { i32 304, i32 11 }, %struct.rb_code_position_struct { i32 337, i32 13 }], align 8 +@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [10 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 43 }, %struct.rb_code_position_struct { i32 66, i32 14 }, %struct.rb_code_position_struct { i32 164, i32 24 }, %struct.rb_code_position_struct { i32 189, i32 24 }, %struct.rb_code_position_struct { i32 81, i32 16 }, %struct.rb_code_position_struct { i32 255, i32 26 }, %struct.rb_code_position_struct { i32 282, i32 26 }, %struct.rb_code_position_struct { i32 309, i32 11 }, %struct.rb_code_position_struct { i32 342, i32 13 }], align 8 @guard_epoch_Test = linkonce local_unnamed_addr global i64 0 @guarded_const_Test = linkonce local_unnamed_addr global i64 0 @rb_eStandardError = external local_unnamed_addr constant i64 +@rb_cModule = external local_unnamed_addr constant i64 -; Function Attrs: noreturn -declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #0 +; Function Attrs: nounwind readnone willreturn +declare i64 @rb_obj_is_kind_of(i64, i64) local_unnamed_addr #0 -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #1 +; Function Attrs: noreturn +declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #1 -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #1 +declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #2 -declare i64 @sorbet_getConstant(i8*, i64) local_unnamed_addr #1 +declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #2 -declare i64 @sorbet_readRealpath() local_unnamed_addr #1 +declare i64 @sorbet_getConstant(i8*, i64) local_unnamed_addr #2 -declare %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64) local_unnamed_addr #1 +declare i64 @sorbet_readRealpath() local_unnamed_addr #2 -declare void @sorbet_popFrame() local_unnamed_addr #1 +declare %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64) local_unnamed_addr #2 -declare void @sorbet_vm_env_write_slowpath(i64*, i32, i64) local_unnamed_addr #1 +declare void @sorbet_popFrame() local_unnamed_addr #2 -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #1 +declare void @sorbet_vm_env_write_slowpath(i64*, i32, i64) local_unnamed_addr #2 -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #1 +declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #2 -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #1 +declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #2 -declare void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #1 +declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #2 -declare i64 @sorbet_stringInterpolate(i64, i64, i32, i64*, i64 (i64, i64, i32, i64*, i64)*, i64) local_unnamed_addr #1 +declare void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #2 -declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #1 +declare i64 @sorbet_stringInterpolate(i64, i64, i32, i64*, i64 (i64, i64, i32, i64*, i64)*, i64) local_unnamed_addr #2 -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #1 +declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #2 -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #1 +declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #2 -declare i64 @sorbet_vm_isa_p(%struct.FunctionInlineCache*, %struct.rb_control_frame_struct*, i64, i64) local_unnamed_addr #1 +declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #2 -declare i64 @sorbet_run_exception_handling(%struct.rb_execution_context_struct*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64**, i64, %struct.rb_control_frame_struct*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64, i64, i64) local_unnamed_addr #1 +declare i64 @sorbet_run_exception_handling(%struct.rb_execution_context_struct*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64**, i64, %struct.rb_control_frame_struct*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64, i64, i64) local_unnamed_addr #2 -declare i64 @rb_define_module(i8*) local_unnamed_addr #1 +declare i64 @rb_define_module(i8*) local_unnamed_addr #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #2 +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #3 ; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #2 +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #3 ; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #0 +declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #1 ; Function Attrs: inaccessiblememonly nofree nosync nounwind willreturn -declare void @llvm.experimental.noalias.scope.decl(metadata) #3 +declare void @llvm.experimental.noalias.scope.decl(metadata) #4 -declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #1 +declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #2 ; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #4 +declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #5 ; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xmalloc2(i64, i64) local_unnamed_addr #4 +declare noalias nonnull i8* @ruby_xmalloc2(i64, i64) local_unnamed_addr #5 ; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #2 +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #3 ; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #5 { +define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #6 { %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #13 + tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #14 unreachable } ; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #5 { +define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #6 { %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #13 + tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #14 unreachable } ; Function Attrs: sspreq -define void @Init_t_must() local_unnamed_addr #6 { +define void @Init_t_must() local_unnamed_addr #7 { entry: %positional_table.i.i = alloca i64, align 8, !dbg !10 %locals.i22.i = alloca i64, i32 0, align 8 @@ -197,8 +200,8 @@ entry: %locals.i7.i = alloca i64, i32 4, align 8 %locals.i.i = alloca i64, i32 0, align 8 %realpath = tail call i64 @sorbet_readRealpath() - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([18 x %struct.rb_code_position_struct], [18 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 18, i8* noundef getelementptr inbounds ([358 x i8], [358 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([10 x %struct.rb_code_position_struct], [10 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 10, i8* noundef getelementptr inbounds ([358 x i8], [358 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) + tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([18 x %struct.rb_code_position_struct], [18 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 18, i8* noundef getelementptr inbounds ([363 x i8], [363 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) + tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([10 x %struct.rb_code_position_struct], [10 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 10, i8* noundef getelementptr inbounds ([363 x i8], [363 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 28) %"rubyId_.i.i" = load i64, i64* getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 %"rubyStr_.i.i" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 @@ -237,10 +240,10 @@ entry: %"rubyStr_ensure in test_known_nil.i.i" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 4), align 8, !invariant.load !5 %7 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_ensure in test_known_nil.i.i", i64 %"rubyId_ensure in test_known_nil.i.i", i64 %"rubyStr_test/testdata/compiler/intrinsics/t_must.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* %stackFrame.i9.i, i32 noundef 5, i32 noundef 6, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 2) store %struct.rb_iseq_struct* %7, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Test.14test_known_nil$block_3", align 8 - %8 = call i64 @sorbet_getConstant(i8* noundef getelementptr inbounds ([25 x i8], [25 x i8]* @sorbet_getTRetry.retry, i64 0, i64 0), i64 noundef 25) #14 + %8 = call i64 @sorbet_getConstant(i8* noundef getelementptr inbounds ([25 x i8], [25 x i8]* @sorbet_getTRetry.retry, i64 0, i64 0), i64 noundef 25) #15 store i64 %8, i64* @"", align 8 - %"rubyId_is_a?.i" = load i64, i64* getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i64 0, i64 10), align 8, !dbg !21, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_is_a?", i64 %"rubyId_is_a?.i", i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !21 + %"rubyId_===.i" = load i64, i64* getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i64 0, i64 10), align 8, !dbg !21, !invariant.load !5 + call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_===", i64 %"rubyId_===.i", i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !21 %rubyId_puts.i = load i64, i64* getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i64 0, i64 11), align 8, !dbg !24, !invariant.load !5 call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !24 %9 = bitcast i64* %locals.i12.i to i8* @@ -269,7 +272,7 @@ entry: %16 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_ensure in test_nilable_arg.i.i", i64 %"rubyId_ensure in test_nilable_arg.i.i", i64 %"rubyStr_test/testdata/compiler/intrinsics/t_must.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* %stackFrame.i19.i, i32 noundef 5, i32 noundef 14, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 3) store %struct.rb_iseq_struct* %16, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Test.16test_nilable_arg$block_3", align 8 call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.3, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !25 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_is_a?.4", i64 %"rubyId_is_a?.i", i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !28 + call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_===.4", i64 %"rubyId_===.i", i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !28 call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.5, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !30 %"rubyId_.i.i" = load i64, i64* getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i64 0, i64 16), align 8, !invariant.load !5 %"rubyStr_.i.i" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 9), align 8, !invariant.load !5 @@ -286,13 +289,13 @@ entry: %24 = load i64, i64* %23, align 8, !tbaa !6 %25 = and i64 %24, -33 store i64 %25, i64* %23, align 8, !tbaa !6 - call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %18, %struct.rb_control_frame_struct* %20, %struct.rb_iseq_struct* %stackFrame.i) #14 + call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %18, %struct.rb_control_frame_struct* %20, %struct.rb_iseq_struct* %stackFrame.i) #15 %26 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %20, i64 0, i32 0 store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %26, align 8, !dbg !40, !tbaa !31 - %27 = call i64 @rb_define_module(i8* getelementptr inbounds ([358 x i8], [358 x i8]* @sorbet_moduleStringTable, i64 0, i64 61)) #14, !dbg !41 - %28 = call %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64 %27) #14, !dbg !41 + %27 = call i64 @rb_define_module(i8* getelementptr inbounds ([363 x i8], [363 x i8]* @sorbet_moduleStringTable, i64 0, i64 61)) #15, !dbg !41 + %28 = call %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64 %27) #15, !dbg !41 %29 = bitcast i64* %positional_table.i.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %29) #14 + call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %29) #15 %stackFrame.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Test.13", align 8 %30 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !31 %31 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %30, i64 0, i32 2 @@ -304,7 +307,7 @@ entry: %36 = load i64, i64* %35, align 8, !tbaa !6 %37 = and i64 %36, -33 store i64 %37, i64* %35, align 8, !tbaa !6 - call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %30, %struct.rb_control_frame_struct* %32, %struct.rb_iseq_struct* %stackFrame.i.i) #14 + call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %30, %struct.rb_control_frame_struct* %32, %struct.rb_iseq_struct* %stackFrame.i.i) #15 %38 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %28, i64 0, i32 0 store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %38, align 8, !dbg !42, !tbaa !31 %39 = load i64, i64* @guard_epoch_Test, align 8, !dbg !43 @@ -323,17 +326,17 @@ entry: %guardUpdated = icmp eq i64 %44, %45, !dbg !43 call void @llvm.assume(i1 %guardUpdated), !dbg !43 %stackFrame17.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @stackFramePrecomputed_func_Test.14test_known_nil, align 8, !dbg !43 - %46 = call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #15, !dbg !43 + %46 = call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #16, !dbg !43 %47 = bitcast i8* %46 to i16*, !dbg !43 %48 = load i16, i16* %47, align 8, !dbg !43 %49 = and i16 %48, -384, !dbg !43 store i16 %49, i16* %47, align 8, !dbg !43 %50 = getelementptr inbounds i8, i8* %46, i64 4, !dbg !43 - call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %50, i8 0, i64 28, i1 false) #14, !dbg !43 - call void @sorbet_vm_define_method(i64 %43, i8* getelementptr inbounds ([358 x i8], [358 x i8]* @sorbet_moduleStringTable, i64 0, i64 66), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @func_Test.14test_known_nil, i8* nonnull %46, %struct.rb_iseq_struct* %stackFrame17.i.i, i1 noundef zeroext true) #14, !dbg !43 + call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %50, i8 0, i64 28, i1 false) #15, !dbg !43 + call void @sorbet_vm_define_method(i64 %43, i8* getelementptr inbounds ([363 x i8], [363 x i8]* @sorbet_moduleStringTable, i64 0, i64 66), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @func_Test.14test_known_nil, i8* nonnull %46, %struct.rb_iseq_struct* %stackFrame17.i.i, i1 noundef zeroext true) #15, !dbg !43 store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %38, align 8, !dbg !43, !tbaa !31 %stackFrame26.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @stackFramePrecomputed_func_Test.16test_nilable_arg, align 8, !dbg !10 - %51 = call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #15, !dbg !10 + %51 = call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #16, !dbg !10 %52 = bitcast i8* %51 to i16*, !dbg !10 %53 = load i16, i16* %52, align 8, !dbg !10 %54 = and i16 %53, -384, !dbg !10 @@ -345,17 +348,17 @@ entry: %58 = getelementptr inbounds i8, i8* %51, i64 12, !dbg !10 %59 = getelementptr inbounds i8, i8* %51, i64 4, !dbg !10 %60 = bitcast i8* %59 to i32*, !dbg !10 - call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %58, i8 0, i64 20, i1 false) #14, !dbg !10 + call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %58, i8 0, i64 20, i1 false) #15, !dbg !10 store i32 1, i32* %60, align 4, !dbg !10, !tbaa !50 store i64 %rubyId_arg.i.i, i64* %positional_table.i.i, align 8, !dbg !10 - %61 = call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 1, i64 noundef 8) #15, !dbg !10 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %61, i8* nocapture noundef nonnull readonly align 8 dereferenceable(8) %29, i64 noundef 8, i1 noundef false) #14, !dbg !10 + %61 = call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 1, i64 noundef 8) #16, !dbg !10 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %61, i8* nocapture noundef nonnull readonly align 8 dereferenceable(8) %29, i64 noundef 8, i1 noundef false) #15, !dbg !10 %62 = getelementptr inbounds i8, i8* %51, i64 32, !dbg !10 %63 = bitcast i8* %62 to i8**, !dbg !10 store i8* %61, i8** %63, align 8, !dbg !10, !tbaa !51 - call void @sorbet_vm_define_method(i64 %43, i8* getelementptr inbounds ([358 x i8], [358 x i8]* @sorbet_moduleStringTable, i64 0, i64 81), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @func_Test.16test_nilable_arg, i8* nonnull %51, %struct.rb_iseq_struct* %stackFrame26.i.i, i1 noundef zeroext true) #14, !dbg !10 - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %29) #14 - call void @sorbet_popFrame() #14, !dbg !41 + call void @sorbet_vm_define_method(i64 %43, i8* getelementptr inbounds ([363 x i8], [363 x i8]* @sorbet_moduleStringTable, i64 0, i64 81), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @func_Test.16test_nilable_arg, i8* nonnull %51, %struct.rb_iseq_struct* %stackFrame26.i.i, i1 noundef zeroext true) #15, !dbg !10 + call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %29) #15 + call void @sorbet_popFrame() #15, !dbg !41 store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 24), i64** %26, align 8, !dbg !41, !tbaa !31 %64 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %20, i64 0, i32 1, !dbg !17 %65 = load i64*, i64** %64, align 8, !dbg !17 @@ -394,7 +397,7 @@ entry: } ; Function Attrs: nounwind sspreq uwtable -define internal i64 @func_Test.14test_known_nil(i32 %argc, i64* nocapture nofree readnone %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #7 !dbg !23 { +define internal i64 @func_Test.14test_known_nil(i32 %argc, i64* nocapture nofree readnone %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #8 !dbg !23 { functionEntryInitializers: %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %0, align 8, !tbaa !31 @@ -406,11 +409,11 @@ postProcess: ; preds = %fillRequiredArgs, % ret i64 %".sroa.0.0" argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 0, i32 noundef 0) #16, !dbg !52 + tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 0, i32 noundef 0) #17, !dbg !52 unreachable, !dbg !52 fillRequiredArgs: ; preds = %functionEntryInitializers - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %0, align 8, !dbg !54, !tbaa !31 + store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %0, align 8, !dbg !54, !tbaa !31 %1 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !55, !tbaa !31 %"" = load i64, i64* @"", align 8, !dbg !55 %2 = tail call i64 @sorbet_run_exception_handling(%struct.rb_execution_context_struct* %1, i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_Test.14test_known_nil$block_1", i64** nonnull align 8 dereferenceable(8) %0, i64 noundef 0, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_Test.14test_known_nil$block_2", i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_Test.14test_known_nil$block_4", i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_Test.14test_known_nil$block_3", i64 %"", i64 noundef 0, i64 noundef 0), !dbg !55 @@ -427,25 +430,25 @@ exception-continue: ; preds = %fillRequiredArgs } ; Function Attrs: noreturn nounwind ssp -define internal i64 @"func_Test.14test_known_nil$block_1"(i64** nocapture nonnull writeonly align 8 dereferenceable(8) %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #8 !dbg !57 { +define internal i64 @"func_Test.14test_known_nil$block_1"(i64** nocapture nonnull writeonly align 8 dereferenceable(8) %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #9 !dbg !57 { functionEntryInitializers: store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %pc, align 8, !tbaa !31 - tail call void @llvm.experimental.noalias.scope.decl(metadata !58) #17, !dbg !61 + tail call void @llvm.experimental.noalias.scope.decl(metadata !58) #18, !dbg !61 %0 = load i64, i64* @rb_eTypeError, align 8, !dbg !61, !tbaa !6, !noalias !58 - tail call void (i64, i8*, ...) @rb_raise(i64 %0, i8* noundef getelementptr inbounds ([25 x i8], [25 x i8]* @.str.1, i64 0, i64 0)) #13, !dbg !61, !noalias !58 + tail call void (i64, i8*, ...) @rb_raise(i64 %0, i8* noundef getelementptr inbounds ([25 x i8], [25 x i8]* @.str.1, i64 0, i64 0)) #14, !dbg !61, !noalias !58 unreachable, !dbg !61 } ; Function Attrs: ssp -define internal noundef i64 @"func_Test.14test_known_nil$block_2"(i64** nocapture nofree readnone %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #9 !dbg !22 { -vm_get_ep.exit34: +define internal noundef i64 @"func_Test.14test_known_nil$block_2"(i64** nocapture nofree readnone %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #10 !dbg !22 { +vm_get_ep.exit37: %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !31 %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !33 %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 %4 = load i64, i64* %3, align 8, !tbaa !62 %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Test.14test_known_nil$block_2", align 8 - tail call void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #14 + tail call void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #15 %5 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !31 %6 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %5, i64 0, i32 2 %7 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %6, align 8, !tbaa !33 @@ -463,109 +466,128 @@ vm_get_ep.exit34: %18 = getelementptr inbounds i64, i64* %17, i64 -3, !dbg !21 %19 = load i64, i64* %18, align 8, !dbg !21, !tbaa !6 %20 = load i64, i64* @rb_eStandardError, align 8, !dbg !21 - %21 = tail call i64 @sorbet_vm_isa_p(%struct.FunctionInlineCache* noundef @"ic_is_a?", %struct.rb_control_frame_struct* %11, i64 %19, i64 %20), !dbg !21 - %22 = and i64 %21, -9, !dbg !21 - %23 = icmp ne i64 %22, 0, !dbg !21 - br i1 %23, label %vm_get_ep.exit32, label %vm_get_ep.exit, !dbg !21 - -blockExit: ; preds = %75, %73, %60, %58 + %21 = load i64, i64* @rb_cModule, align 8, !dbg !21 + %22 = tail call i64 @rb_obj_is_kind_of(i64 %19, i64 %20), !dbg !21 + %23 = icmp eq i64 %22, 20, !dbg !21 + %24 = select i1 %23, i64 20, i64 0, !dbg !21 + %25 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 5, !dbg !21 + %26 = load i32, i32* %25, align 8, !dbg !21, !tbaa !63 + %27 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 6, !dbg !21 + %28 = load i32, i32* %27, align 4, !dbg !21, !tbaa !64 + %29 = xor i32 %28, -1, !dbg !21 + %30 = and i32 %29, %26, !dbg !21 + %31 = icmp eq i32 %30, 0, !dbg !21 + br i1 %31, label %afterSend, label %86, !dbg !21, !prof !65 + +blockExit: ; preds = %83, %81, %68, %66 tail call void @sorbet_popFrame() ret i64 52 -vm_get_ep.exit32: ; preds = %vm_get_ep.exit34 - %24 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !63, !tbaa !31 - %25 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %24, i64 0, i32 2, !dbg !63 - %26 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %25, align 8, !dbg !63, !tbaa !33 - %27 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %26, i64 0, i32 4, !dbg !63 - %28 = load i64*, i64** %27, align 8, !dbg !63 - %29 = getelementptr inbounds i64, i64* %28, i64 -1, !dbg !63 - %30 = load i64, i64* %29, align 8, !dbg !63, !tbaa !6 - %31 = and i64 %30, -4, !dbg !63 - %32 = inttoptr i64 %31 to i64*, !dbg !63 - %33 = load i64, i64* %32, align 8, !dbg !63, !tbaa !6 - %34 = and i64 %33, 8, !dbg !63 - %35 = icmp eq i64 %34, 0, !dbg !63 - br i1 %35, label %36, label %38, !dbg !63, !prof !64 - -36: ; preds = %vm_get_ep.exit32 - %37 = getelementptr inbounds i64, i64* %32, i64 -3, !dbg !63 - store i64 8, i64* %37, align 8, !dbg !63, !tbaa !6 - br label %vm_get_ep.exit30, !dbg !63 - -38: ; preds = %vm_get_ep.exit32 - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %32, i32 noundef -3, i64 noundef 8) #14, !dbg !63 - br label %vm_get_ep.exit30, !dbg !63 - -vm_get_ep.exit30: ; preds = %36, %38 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 10), i64** %8, align 8, !dbg !65, !tbaa !31 - %39 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !24, !tbaa !31 - %40 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %39, i64 0, i32 2, !dbg !24 - %41 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %40, align 8, !dbg !24, !tbaa !33 - %42 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %41, i64 0, i32 1, !dbg !24 - %43 = load i64*, i64** %42, align 8, !dbg !24 - store i64 %4, i64* %43, align 8, !dbg !24, !tbaa !6 - %44 = getelementptr inbounds i64, i64* %43, i64 1, !dbg !24 - store i64 %19, i64* %44, align 8, !dbg !24, !tbaa !6 - %45 = getelementptr inbounds i64, i64* %44, i64 1, !dbg !24 - store i64* %45, i64** %42, align 8, !dbg !24 +vm_get_ep.exit35: ; preds = %afterSend + %32 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !66, !tbaa !31 + %33 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %32, i64 0, i32 2, !dbg !66 + %34 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %33, align 8, !dbg !66, !tbaa !33 + %35 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %34, i64 0, i32 4, !dbg !66 + %36 = load i64*, i64** %35, align 8, !dbg !66 + %37 = getelementptr inbounds i64, i64* %36, i64 -1, !dbg !66 + %38 = load i64, i64* %37, align 8, !dbg !66, !tbaa !6 + %39 = and i64 %38, -4, !dbg !66 + %40 = inttoptr i64 %39 to i64*, !dbg !66 + %41 = load i64, i64* %40, align 8, !dbg !66, !tbaa !6 + %42 = and i64 %41, 8, !dbg !66 + %43 = icmp eq i64 %42, 0, !dbg !66 + br i1 %43, label %44, label %46, !dbg !66, !prof !65 + +44: ; preds = %vm_get_ep.exit35 + %45 = getelementptr inbounds i64, i64* %40, i64 -3, !dbg !66 + store i64 8, i64* %45, align 8, !dbg !66, !tbaa !6 + br label %vm_get_ep.exit33, !dbg !66 + +46: ; preds = %vm_get_ep.exit35 + tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %40, i32 noundef -3, i64 noundef 8) #15, !dbg !66 + br label %vm_get_ep.exit33, !dbg !66 + +vm_get_ep.exit33: ; preds = %44, %46 + store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 10), i64** %8, align 8, !dbg !67, !tbaa !31 + %47 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !24, !tbaa !31 + %48 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %47, i64 0, i32 2, !dbg !24 + %49 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %48, align 8, !dbg !24, !tbaa !33 + %50 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %49, i64 0, i32 1, !dbg !24 + %51 = load i64*, i64** %50, align 8, !dbg !24 + store i64 %4, i64* %51, align 8, !dbg !24, !tbaa !6 + %52 = getelementptr inbounds i64, i64* %51, i64 1, !dbg !24 + store i64 %19, i64* %52, align 8, !dbg !24, !tbaa !6 + %53 = getelementptr inbounds i64, i64* %52, i64 1, !dbg !24 + store i64* %53, i64** %50, align 8, !dbg !24 %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !24 - %46 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !24, !tbaa !31 - %47 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %46, i64 0, i32 2, !dbg !24 - %48 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %47, align 8, !dbg !24, !tbaa !33 - %49 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %48, i64 0, i32 4, !dbg !24 - %50 = load i64*, i64** %49, align 8, !dbg !24 - %51 = getelementptr inbounds i64, i64* %50, i64 -1, !dbg !24 - %52 = load i64, i64* %51, align 8, !dbg !24, !tbaa !6 - %53 = and i64 %52, -4, !dbg !24 - %54 = inttoptr i64 %53 to i64*, !dbg !24 - %55 = load i64, i64* %54, align 8, !dbg !24, !tbaa !6 - %56 = and i64 %55, 8, !dbg !24 - %57 = icmp eq i64 %56, 0, !dbg !24 - br i1 %57, label %58, label %60, !dbg !24, !prof !64 - -58: ; preds = %vm_get_ep.exit30 - %59 = getelementptr inbounds i64, i64* %54, i64 -5, !dbg !24 - store i64 %send, i64* %59, align 8, !dbg !24, !tbaa !6 + %54 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !24, !tbaa !31 + %55 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %54, i64 0, i32 2, !dbg !24 + %56 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %55, align 8, !dbg !24, !tbaa !33 + %57 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %56, i64 0, i32 4, !dbg !24 + %58 = load i64*, i64** %57, align 8, !dbg !24 + %59 = getelementptr inbounds i64, i64* %58, i64 -1, !dbg !24 + %60 = load i64, i64* %59, align 8, !dbg !24, !tbaa !6 + %61 = and i64 %60, -4, !dbg !24 + %62 = inttoptr i64 %61 to i64*, !dbg !24 + %63 = load i64, i64* %62, align 8, !dbg !24, !tbaa !6 + %64 = and i64 %63, 8, !dbg !24 + %65 = icmp eq i64 %64, 0, !dbg !24 + br i1 %65, label %66, label %68, !dbg !24, !prof !65 + +66: ; preds = %vm_get_ep.exit33 + %67 = getelementptr inbounds i64, i64* %62, i64 -5, !dbg !24 + store i64 %send, i64* %67, align 8, !dbg !24, !tbaa !6 br label %blockExit, !dbg !24 -60: ; preds = %vm_get_ep.exit30 - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %54, i32 noundef -5, i64 %send) #14, !dbg !24 +68: ; preds = %vm_get_ep.exit33 + tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %62, i32 noundef -5, i64 %send) #15, !dbg !24 br label %blockExit, !dbg !24 -vm_get_ep.exit: ; preds = %vm_get_ep.exit34 +vm_get_ep.exit: ; preds = %afterSend store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %8, align 8, !tbaa !31 - %61 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !66, !tbaa !31 - %62 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %61, i64 0, i32 2, !dbg !66 - %63 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %62, align 8, !dbg !66, !tbaa !33 - %64 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %63, i64 0, i32 4, !dbg !66 - %65 = load i64*, i64** %64, align 8, !dbg !66 - %66 = getelementptr inbounds i64, i64* %65, i64 -1, !dbg !66 - %67 = load i64, i64* %66, align 8, !dbg !66, !tbaa !6 - %68 = and i64 %67, -4, !dbg !66 - %69 = inttoptr i64 %68 to i64*, !dbg !66 - %70 = load i64, i64* %69, align 8, !dbg !66, !tbaa !6 - %71 = and i64 %70, 8, !dbg !66 - %72 = icmp eq i64 %71, 0, !dbg !66 - br i1 %72, label %73, label %75, !dbg !66, !prof !64 - -73: ; preds = %vm_get_ep.exit - %74 = getelementptr inbounds i64, i64* %69, i64 -6, !dbg !66 - store i64 20, i64* %74, align 8, !dbg !66, !tbaa !6 - br label %blockExit, !dbg !66 - -75: ; preds = %vm_get_ep.exit - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %69, i32 noundef -6, i64 noundef 20) #14, !dbg !66 - br label %blockExit, !dbg !66 + %69 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !68, !tbaa !31 + %70 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %69, i64 0, i32 2, !dbg !68 + %71 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %70, align 8, !dbg !68, !tbaa !33 + %72 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %71, i64 0, i32 4, !dbg !68 + %73 = load i64*, i64** %72, align 8, !dbg !68 + %74 = getelementptr inbounds i64, i64* %73, i64 -1, !dbg !68 + %75 = load i64, i64* %74, align 8, !dbg !68, !tbaa !6 + %76 = and i64 %75, -4, !dbg !68 + %77 = inttoptr i64 %76 to i64*, !dbg !68 + %78 = load i64, i64* %77, align 8, !dbg !68, !tbaa !6 + %79 = and i64 %78, 8, !dbg !68 + %80 = icmp eq i64 %79, 0, !dbg !68 + br i1 %80, label %81, label %83, !dbg !68, !prof !65 + +81: ; preds = %vm_get_ep.exit + %82 = getelementptr inbounds i64, i64* %77, i64 -6, !dbg !68 + store i64 20, i64* %82, align 8, !dbg !68, !tbaa !6 + br label %blockExit, !dbg !68 + +83: ; preds = %vm_get_ep.exit + tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %77, i32 noundef -6, i64 noundef 20) #15, !dbg !68 + br label %blockExit, !dbg !68 + +afterSend: ; preds = %86, %vm_get_ep.exit37 + %84 = and i64 %24, -9, !dbg !21 + %85 = icmp ne i64 %84, 0, !dbg !21 + br i1 %85, label %vm_get_ep.exit35, label %vm_get_ep.exit, !dbg !21 + +86: ; preds = %vm_get_ep.exit37 + %87 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 8, !dbg !21 + %88 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %87, align 8, !dbg !21, !tbaa !69 + %89 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %88, i32 noundef 0) #15, !dbg !21 + br label %afterSend, !dbg !21 } ; Function Attrs: ssp -define internal noundef i64 @"func_Test.14test_known_nil$block_3"(i64** nocapture nofree readnone %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #9 !dbg !67 { +define internal noundef i64 @"func_Test.14test_known_nil$block_3"(i64** nocapture nofree readnone %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #10 !dbg !70 { functionEntryInitializers: %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Test.14test_known_nil$block_3", align 8 %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !31 %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !33 - tail call void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #14 + tail call void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #15 %3 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !31 %4 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %3, i64 0, i32 2 %5 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !33 @@ -576,66 +598,66 @@ functionEntryInitializers: } ; Function Attrs: argmemonly nofree norecurse nosync nounwind ssp willreturn writeonly -define internal noundef i64 @"func_Test.14test_known_nil$block_4"(i64** nocapture nofree nonnull writeonly align 8 dereferenceable(8) %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #10 !dbg !68 { +define internal noundef i64 @"func_Test.14test_known_nil$block_4"(i64** nocapture nofree nonnull writeonly align 8 dereferenceable(8) %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #11 !dbg !71 { functionEntryInitializers: store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %pc, align 8, !tbaa !31 ret i64 52 } ; Function Attrs: nounwind sspreq uwtable -define internal i64 @func_Test.16test_nilable_arg(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #7 !dbg !27 { +define internal i64 @func_Test.16test_nilable_arg(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #8 !dbg !27 { functionEntryInitializers: %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %0, align 8, !tbaa !31 - %tooManyArgs = icmp ugt i32 %argc, 1, !dbg !69 - %tooFewArgs = icmp ult i32 %argc, 1, !dbg !69 - %or.cond = or i1 %tooManyArgs, %tooFewArgs, !dbg !69 - br i1 %or.cond, label %argCountFailBlock, label %fillRequiredArgs, !dbg !69, !prof !70 + %tooManyArgs = icmp ugt i32 %argc, 1, !dbg !72 + %tooFewArgs = icmp ult i32 %argc, 1, !dbg !72 + %or.cond = or i1 %tooManyArgs, %tooFewArgs, !dbg !72 + br i1 %or.cond, label %argCountFailBlock, label %fillRequiredArgs, !dbg !72, !prof !73 postProcess: ; preds = %sorbet_writeLocal.exit, %exception-continue - %".sroa.0.0" = phi i64 [ %13, %exception-continue ], [ %10, %sorbet_writeLocal.exit ], !dbg !71 + %".sroa.0.0" = phi i64 [ %13, %exception-continue ], [ %10, %sorbet_writeLocal.exit ], !dbg !74 ret i64 %".sroa.0.0" argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 1, i32 noundef 1) #16, !dbg !69 - unreachable, !dbg !69 + tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 1, i32 noundef 1) #17, !dbg !72 + unreachable, !dbg !72 fillRequiredArgs: ; preds = %functionEntryInitializers - %rawArg_arg = load i64, i64* %argArray, align 8, !dbg !69 - %1 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 4, !dbg !69 - %2 = load i64*, i64** %1, align 8, !dbg !69, !tbaa !39 - %3 = load i64, i64* %2, align 8, !dbg !69, !tbaa !6 - %4 = and i64 %3, 8, !dbg !69 - %5 = icmp eq i64 %4, 0, !dbg !69 - br i1 %5, label %6, label %8, !dbg !69, !prof !64 + %rawArg_arg = load i64, i64* %argArray, align 8, !dbg !72 + %1 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 4, !dbg !72 + %2 = load i64*, i64** %1, align 8, !dbg !72, !tbaa !39 + %3 = load i64, i64* %2, align 8, !dbg !72, !tbaa !6 + %4 = and i64 %3, 8, !dbg !72 + %5 = icmp eq i64 %4, 0, !dbg !72 + br i1 %5, label %6, label %8, !dbg !72, !prof !65 6: ; preds = %fillRequiredArgs - %7 = getelementptr inbounds i64, i64* %2, i64 -4, !dbg !69 - store i64 %rawArg_arg, i64* %7, align 8, !dbg !69, !tbaa !6 - br label %sorbet_writeLocal.exit, !dbg !69 + %7 = getelementptr inbounds i64, i64* %2, i64 -4, !dbg !72 + store i64 %rawArg_arg, i64* %7, align 8, !dbg !72, !tbaa !6 + br label %sorbet_writeLocal.exit, !dbg !72 8: ; preds = %fillRequiredArgs - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %2, i32 noundef -4, i64 %rawArg_arg) #14, !dbg !69 - br label %sorbet_writeLocal.exit, !dbg !69 + tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %2, i32 noundef -4, i64 %rawArg_arg) #15, !dbg !72 + br label %sorbet_writeLocal.exit, !dbg !72 sorbet_writeLocal.exit: ; preds = %6, %8 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 16), i64** %0, align 8, !dbg !71, !tbaa !31 - %9 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !72, !tbaa !31 - %"" = load i64, i64* @"", align 8, !dbg !72 - %10 = tail call i64 @sorbet_run_exception_handling(%struct.rb_execution_context_struct* %9, i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_Test.16test_nilable_arg$block_1", i64** nonnull %0, i64 noundef 0, %struct.rb_control_frame_struct* nonnull %cfp, i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_Test.16test_nilable_arg$block_2", i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_Test.16test_nilable_arg$block_4", i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_Test.16test_nilable_arg$block_3", i64 %"", i64 noundef 0, i64 noundef 0), !dbg !72 - %ensureReturnValue = icmp ne i64 %10, 52, !dbg !72 - br i1 %ensureReturnValue, label %postProcess, label %exception-continue, !dbg !72 + store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %0, align 8, !dbg !74, !tbaa !31 + %9 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !75, !tbaa !31 + %"" = load i64, i64* @"", align 8, !dbg !75 + %10 = tail call i64 @sorbet_run_exception_handling(%struct.rb_execution_context_struct* %9, i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_Test.16test_nilable_arg$block_1", i64** nonnull %0, i64 noundef 0, %struct.rb_control_frame_struct* nonnull %cfp, i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_Test.16test_nilable_arg$block_2", i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_Test.16test_nilable_arg$block_4", i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_Test.16test_nilable_arg$block_3", i64 %"", i64 noundef 0, i64 noundef 0), !dbg !75 + %ensureReturnValue = icmp ne i64 %10, 52, !dbg !75 + br i1 %ensureReturnValue, label %postProcess, label %exception-continue, !dbg !75 exception-continue: ; preds = %sorbet_writeLocal.exit store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 21), i64** %0, align 8, !tbaa !31 - %11 = load i64*, i64** %1, align 8, !dbg !73, !tbaa !39 - %12 = getelementptr inbounds i64, i64* %11, i64 -6, !dbg !73 - %13 = load i64, i64* %12, align 8, !dbg !73, !tbaa !6 - br label %postProcess, !dbg !73 + %11 = load i64*, i64** %1, align 8, !dbg !76, !tbaa !39 + %12 = getelementptr inbounds i64, i64* %11, i64 -6, !dbg !76 + %13 = load i64, i64* %12, align 8, !dbg !76, !tbaa !6 + br label %postProcess, !dbg !76 } ; Function Attrs: ssp -define internal noundef i64 @"func_Test.16test_nilable_arg$block_1"(i64** nocapture nonnull writeonly align 8 dereferenceable(8) %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(40) %cfp) #9 !dbg !26 { +define internal noundef i64 @"func_Test.16test_nilable_arg$block_1"(i64** nocapture nonnull writeonly align 8 dereferenceable(8) %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(40) %cfp) #10 !dbg !26 { functionEntryInitializers: %callArgs = alloca [3 x i64], align 8 %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !31 @@ -644,40 +666,40 @@ functionEntryInitializers: %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 %4 = load i64, i64* %3, align 8, !tbaa !62 store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 16), i64** %pc, align 8, !tbaa !31 - %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 4, !dbg !74 - %6 = load i64*, i64** %5, align 8, !dbg !74, !tbaa !39 - %7 = getelementptr inbounds i64, i64* %6, i64 -4, !dbg !74 - %8 = load i64, i64* %7, align 8, !dbg !74, !tbaa !6 - %callArgs0Addr = getelementptr [3 x i64], [3 x i64]* %callArgs, i64 0, i64 0, !dbg !74 - store i64 %8, i64* %callArgs0Addr, align 8, !dbg !74 - tail call void @llvm.experimental.noalias.scope.decl(metadata !75), !dbg !74 - %9 = icmp eq i64 %8, 8, !dbg !74 - br i1 %9, label %10, label %sorbet_T_must.exit, !dbg !74, !prof !53 + %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 4, !dbg !77 + %6 = load i64*, i64** %5, align 8, !dbg !77, !tbaa !39 + %7 = getelementptr inbounds i64, i64* %6, i64 -4, !dbg !77 + %8 = load i64, i64* %7, align 8, !dbg !77, !tbaa !6 + %callArgs0Addr = getelementptr [3 x i64], [3 x i64]* %callArgs, i64 0, i64 0, !dbg !77 + store i64 %8, i64* %callArgs0Addr, align 8, !dbg !77 + tail call void @llvm.experimental.noalias.scope.decl(metadata !78), !dbg !77 + %9 = icmp eq i64 %8, 8, !dbg !77 + br i1 %9, label %10, label %sorbet_T_must.exit, !dbg !77, !prof !53 10: ; preds = %functionEntryInitializers - %11 = load i64, i64* @rb_eTypeError, align 8, !dbg !74, !tbaa !6, !noalias !75 - tail call void (i64, i8*, ...) @rb_raise(i64 %11, i8* noundef getelementptr inbounds ([25 x i8], [25 x i8]* @.str.1, i64 0, i64 0)) #13, !dbg !74, !noalias !75 - unreachable, !dbg !74 + %11 = load i64, i64* @rb_eTypeError, align 8, !dbg !77, !tbaa !6, !noalias !78 + tail call void (i64, i8*, ...) @rb_raise(i64 %11, i8* noundef getelementptr inbounds ([25 x i8], [25 x i8]* @.str.1, i64 0, i64 0)) #14, !dbg !77, !noalias !78 + unreachable, !dbg !77 sorbet_T_must.exit: ; preds = %functionEntryInitializers - %12 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !74, !tbaa !31 - %13 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %12, i64 0, i32 5, !dbg !74 - %14 = load i32, i32* %13, align 8, !dbg !74, !tbaa !78 - %15 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %12, i64 0, i32 6, !dbg !74 - %16 = load i32, i32* %15, align 4, !dbg !74, !tbaa !79 - %17 = xor i32 %16, -1, !dbg !74 - %18 = and i32 %17, %14, !dbg !74 - %19 = icmp eq i32 %18, 0, !dbg !74 - br i1 %19, label %rb_vm_check_ints.exit, label %20, !dbg !74, !prof !64 + %12 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !77, !tbaa !31 + %13 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %12, i64 0, i32 5, !dbg !77 + %14 = load i32, i32* %13, align 8, !dbg !77, !tbaa !63 + %15 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %12, i64 0, i32 6, !dbg !77 + %16 = load i32, i32* %15, align 4, !dbg !77, !tbaa !64 + %17 = xor i32 %16, -1, !dbg !77 + %18 = and i32 %17, %14, !dbg !77 + %19 = icmp eq i32 %18, 0, !dbg !77 + br i1 %19, label %rb_vm_check_ints.exit, label %20, !dbg !77, !prof !65 20: ; preds = %sorbet_T_must.exit - %21 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %12, i64 0, i32 8, !dbg !74 - %22 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %21, align 8, !dbg !74, !tbaa !80 - %23 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %22, i32 noundef 0) #14, !dbg !74 - br label %rb_vm_check_ints.exit, !dbg !74 + %21 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %12, i64 0, i32 8, !dbg !77 + %22 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %21, align 8, !dbg !77, !tbaa !69 + %23 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %22, i32 noundef 0) #15, !dbg !77 + br label %rb_vm_check_ints.exit, !dbg !77 rb_vm_check_ints.exit: ; preds = %sorbet_T_must.exit, %20 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 17), i64** %pc, align 8, !dbg !74, !tbaa !31 + store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 17), i64** %pc, align 8, !dbg !77, !tbaa !31 %"rubyStr_ wasn't nil" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 8), align 8, !dbg !81, !invariant.load !5 %24 = load i64*, i64** %5, align 8, !dbg !82, !tbaa !39 %25 = getelementptr inbounds i64, i64* %24, i64 -4, !dbg !82 @@ -699,7 +721,7 @@ rb_vm_check_ints.exit: ; preds = %sorbet_T_must.exit, %32 = load i64, i64* %31, align 8, !dbg !25, !tbaa !6 %33 = and i64 %32, 8, !dbg !25 %34 = icmp eq i64 %33, 0, !dbg !25 - br i1 %34, label %35, label %37, !dbg !25, !prof !64 + br i1 %34, label %35, label %37, !dbg !25, !prof !65 35: ; preds = %rb_vm_check_ints.exit %36 = getelementptr inbounds i64, i64* %31, i64 -6, !dbg !25 @@ -707,24 +729,24 @@ rb_vm_check_ints.exit: ; preds = %sorbet_T_must.exit, br label %sorbet_writeLocal.exit, !dbg !25 37: ; preds = %rb_vm_check_ints.exit - call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %31, i32 noundef -6, i64 %send) #14, !dbg !25 + call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %31, i32 noundef -6, i64 %send) #15, !dbg !25 br label %sorbet_writeLocal.exit, !dbg !25 sorbet_writeLocal.exit: ; preds = %35, %37 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 16), i64** %pc, align 8, !dbg !25, !tbaa !31 + store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %pc, align 8, !dbg !25, !tbaa !31 ret i64 52 } ; Function Attrs: ssp -define internal noundef i64 @"func_Test.16test_nilable_arg$block_2"(i64** nocapture nofree readnone %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #9 !dbg !29 { -vm_get_ep.exit34: +define internal noundef i64 @"func_Test.16test_nilable_arg$block_2"(i64** nocapture nofree readnone %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #10 !dbg !29 { +vm_get_ep.exit37: %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !31 %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !33 %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 %4 = load i64, i64* %3, align 8, !tbaa !62 %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Test.16test_nilable_arg$block_2", align 8 - tail call void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #14 + tail call void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #15 %5 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !31 %6 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %5, i64 0, i32 2 %7 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %6, align 8, !tbaa !33 @@ -742,109 +764,128 @@ vm_get_ep.exit34: %18 = getelementptr inbounds i64, i64* %17, i64 -3, !dbg !28 %19 = load i64, i64* %18, align 8, !dbg !28, !tbaa !6 %20 = load i64, i64* @rb_eStandardError, align 8, !dbg !28 - %21 = tail call i64 @sorbet_vm_isa_p(%struct.FunctionInlineCache* noundef @"ic_is_a?.4", %struct.rb_control_frame_struct* %11, i64 %19, i64 %20), !dbg !28 - %22 = and i64 %21, -9, !dbg !28 - %23 = icmp ne i64 %22, 0, !dbg !28 - br i1 %23, label %vm_get_ep.exit32, label %vm_get_ep.exit, !dbg !28 - -blockExit: ; preds = %75, %73, %60, %58 + %21 = load i64, i64* @rb_cModule, align 8, !dbg !28 + %22 = tail call i64 @rb_obj_is_kind_of(i64 %19, i64 %20), !dbg !28 + %23 = icmp eq i64 %22, 20, !dbg !28 + %24 = select i1 %23, i64 20, i64 0, !dbg !28 + %25 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 5, !dbg !28 + %26 = load i32, i32* %25, align 8, !dbg !28, !tbaa !63 + %27 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 6, !dbg !28 + %28 = load i32, i32* %27, align 4, !dbg !28, !tbaa !64 + %29 = xor i32 %28, -1, !dbg !28 + %30 = and i32 %29, %26, !dbg !28 + %31 = icmp eq i32 %30, 0, !dbg !28 + br i1 %31, label %afterSend, label %86, !dbg !28, !prof !65 + +blockExit: ; preds = %83, %81, %68, %66 tail call void @sorbet_popFrame() ret i64 52 -vm_get_ep.exit32: ; preds = %vm_get_ep.exit34 - %24 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !83, !tbaa !31 - %25 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %24, i64 0, i32 2, !dbg !83 - %26 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %25, align 8, !dbg !83, !tbaa !33 - %27 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %26, i64 0, i32 4, !dbg !83 - %28 = load i64*, i64** %27, align 8, !dbg !83 - %29 = getelementptr inbounds i64, i64* %28, i64 -1, !dbg !83 - %30 = load i64, i64* %29, align 8, !dbg !83, !tbaa !6 - %31 = and i64 %30, -4, !dbg !83 - %32 = inttoptr i64 %31 to i64*, !dbg !83 - %33 = load i64, i64* %32, align 8, !dbg !83, !tbaa !6 - %34 = and i64 %33, 8, !dbg !83 - %35 = icmp eq i64 %34, 0, !dbg !83 - br i1 %35, label %36, label %38, !dbg !83, !prof !64 - -36: ; preds = %vm_get_ep.exit32 - %37 = getelementptr inbounds i64, i64* %32, i64 -3, !dbg !83 - store i64 8, i64* %37, align 8, !dbg !83, !tbaa !6 - br label %vm_get_ep.exit30, !dbg !83 - -38: ; preds = %vm_get_ep.exit32 - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %32, i32 noundef -3, i64 noundef 8) #14, !dbg !83 - br label %vm_get_ep.exit30, !dbg !83 - -vm_get_ep.exit30: ; preds = %36, %38 +vm_get_ep.exit35: ; preds = %afterSend + %32 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !83, !tbaa !31 + %33 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %32, i64 0, i32 2, !dbg !83 + %34 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %33, align 8, !dbg !83, !tbaa !33 + %35 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %34, i64 0, i32 4, !dbg !83 + %36 = load i64*, i64** %35, align 8, !dbg !83 + %37 = getelementptr inbounds i64, i64* %36, i64 -1, !dbg !83 + %38 = load i64, i64* %37, align 8, !dbg !83, !tbaa !6 + %39 = and i64 %38, -4, !dbg !83 + %40 = inttoptr i64 %39 to i64*, !dbg !83 + %41 = load i64, i64* %40, align 8, !dbg !83, !tbaa !6 + %42 = and i64 %41, 8, !dbg !83 + %43 = icmp eq i64 %42, 0, !dbg !83 + br i1 %43, label %44, label %46, !dbg !83, !prof !65 + +44: ; preds = %vm_get_ep.exit35 + %45 = getelementptr inbounds i64, i64* %40, i64 -3, !dbg !83 + store i64 8, i64* %45, align 8, !dbg !83, !tbaa !6 + br label %vm_get_ep.exit33, !dbg !83 + +46: ; preds = %vm_get_ep.exit35 + tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %40, i32 noundef -3, i64 noundef 8) #15, !dbg !83 + br label %vm_get_ep.exit33, !dbg !83 + +vm_get_ep.exit33: ; preds = %44, %46 store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 19), i64** %8, align 8, !dbg !84, !tbaa !31 - %39 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !30, !tbaa !31 - %40 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %39, i64 0, i32 2, !dbg !30 - %41 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %40, align 8, !dbg !30, !tbaa !33 - %42 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %41, i64 0, i32 1, !dbg !30 - %43 = load i64*, i64** %42, align 8, !dbg !30 - store i64 %4, i64* %43, align 8, !dbg !30, !tbaa !6 - %44 = getelementptr inbounds i64, i64* %43, i64 1, !dbg !30 - store i64 %19, i64* %44, align 8, !dbg !30, !tbaa !6 - %45 = getelementptr inbounds i64, i64* %44, i64 1, !dbg !30 - store i64* %45, i64** %42, align 8, !dbg !30 + %47 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !30, !tbaa !31 + %48 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %47, i64 0, i32 2, !dbg !30 + %49 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %48, align 8, !dbg !30, !tbaa !33 + %50 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %49, i64 0, i32 1, !dbg !30 + %51 = load i64*, i64** %50, align 8, !dbg !30 + store i64 %4, i64* %51, align 8, !dbg !30, !tbaa !6 + %52 = getelementptr inbounds i64, i64* %51, i64 1, !dbg !30 + store i64 %19, i64* %52, align 8, !dbg !30, !tbaa !6 + %53 = getelementptr inbounds i64, i64* %52, i64 1, !dbg !30 + store i64* %53, i64** %50, align 8, !dbg !30 %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.5, i64 0), !dbg !30 - %46 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !30, !tbaa !31 - %47 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %46, i64 0, i32 2, !dbg !30 - %48 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %47, align 8, !dbg !30, !tbaa !33 - %49 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %48, i64 0, i32 4, !dbg !30 - %50 = load i64*, i64** %49, align 8, !dbg !30 - %51 = getelementptr inbounds i64, i64* %50, i64 -1, !dbg !30 - %52 = load i64, i64* %51, align 8, !dbg !30, !tbaa !6 - %53 = and i64 %52, -4, !dbg !30 - %54 = inttoptr i64 %53 to i64*, !dbg !30 - %55 = load i64, i64* %54, align 8, !dbg !30, !tbaa !6 - %56 = and i64 %55, 8, !dbg !30 - %57 = icmp eq i64 %56, 0, !dbg !30 - br i1 %57, label %58, label %60, !dbg !30, !prof !64 - -58: ; preds = %vm_get_ep.exit30 - %59 = getelementptr inbounds i64, i64* %54, i64 -6, !dbg !30 - store i64 %send, i64* %59, align 8, !dbg !30, !tbaa !6 + %54 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !30, !tbaa !31 + %55 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %54, i64 0, i32 2, !dbg !30 + %56 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %55, align 8, !dbg !30, !tbaa !33 + %57 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %56, i64 0, i32 4, !dbg !30 + %58 = load i64*, i64** %57, align 8, !dbg !30 + %59 = getelementptr inbounds i64, i64* %58, i64 -1, !dbg !30 + %60 = load i64, i64* %59, align 8, !dbg !30, !tbaa !6 + %61 = and i64 %60, -4, !dbg !30 + %62 = inttoptr i64 %61 to i64*, !dbg !30 + %63 = load i64, i64* %62, align 8, !dbg !30, !tbaa !6 + %64 = and i64 %63, 8, !dbg !30 + %65 = icmp eq i64 %64, 0, !dbg !30 + br i1 %65, label %66, label %68, !dbg !30, !prof !65 + +66: ; preds = %vm_get_ep.exit33 + %67 = getelementptr inbounds i64, i64* %62, i64 -6, !dbg !30 + store i64 %send, i64* %67, align 8, !dbg !30, !tbaa !6 br label %blockExit, !dbg !30 -60: ; preds = %vm_get_ep.exit30 - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %54, i32 noundef -6, i64 %send) #14, !dbg !30 +68: ; preds = %vm_get_ep.exit33 + tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %62, i32 noundef -6, i64 %send) #15, !dbg !30 br label %blockExit, !dbg !30 -vm_get_ep.exit: ; preds = %vm_get_ep.exit34 +vm_get_ep.exit: ; preds = %afterSend store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 16), i64** %8, align 8, !tbaa !31 - %61 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !85, !tbaa !31 - %62 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %61, i64 0, i32 2, !dbg !85 - %63 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %62, align 8, !dbg !85, !tbaa !33 - %64 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %63, i64 0, i32 4, !dbg !85 - %65 = load i64*, i64** %64, align 8, !dbg !85 - %66 = getelementptr inbounds i64, i64* %65, i64 -1, !dbg !85 - %67 = load i64, i64* %66, align 8, !dbg !85, !tbaa !6 - %68 = and i64 %67, -4, !dbg !85 - %69 = inttoptr i64 %68 to i64*, !dbg !85 - %70 = load i64, i64* %69, align 8, !dbg !85, !tbaa !6 - %71 = and i64 %70, 8, !dbg !85 - %72 = icmp eq i64 %71, 0, !dbg !85 - br i1 %72, label %73, label %75, !dbg !85, !prof !64 - -73: ; preds = %vm_get_ep.exit - %74 = getelementptr inbounds i64, i64* %69, i64 -7, !dbg !85 - store i64 20, i64* %74, align 8, !dbg !85, !tbaa !6 + %69 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !85, !tbaa !31 + %70 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %69, i64 0, i32 2, !dbg !85 + %71 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %70, align 8, !dbg !85, !tbaa !33 + %72 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %71, i64 0, i32 4, !dbg !85 + %73 = load i64*, i64** %72, align 8, !dbg !85 + %74 = getelementptr inbounds i64, i64* %73, i64 -1, !dbg !85 + %75 = load i64, i64* %74, align 8, !dbg !85, !tbaa !6 + %76 = and i64 %75, -4, !dbg !85 + %77 = inttoptr i64 %76 to i64*, !dbg !85 + %78 = load i64, i64* %77, align 8, !dbg !85, !tbaa !6 + %79 = and i64 %78, 8, !dbg !85 + %80 = icmp eq i64 %79, 0, !dbg !85 + br i1 %80, label %81, label %83, !dbg !85, !prof !65 + +81: ; preds = %vm_get_ep.exit + %82 = getelementptr inbounds i64, i64* %77, i64 -7, !dbg !85 + store i64 20, i64* %82, align 8, !dbg !85, !tbaa !6 br label %blockExit, !dbg !85 -75: ; preds = %vm_get_ep.exit - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %69, i32 noundef -7, i64 noundef 20) #14, !dbg !85 +83: ; preds = %vm_get_ep.exit + tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %77, i32 noundef -7, i64 noundef 20) #15, !dbg !85 br label %blockExit, !dbg !85 + +afterSend: ; preds = %86, %vm_get_ep.exit37 + %84 = and i64 %24, -9, !dbg !28 + %85 = icmp ne i64 %84, 0, !dbg !28 + br i1 %85, label %vm_get_ep.exit35, label %vm_get_ep.exit, !dbg !28 + +86: ; preds = %vm_get_ep.exit37 + %87 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 8, !dbg !28 + %88 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %87, align 8, !dbg !28, !tbaa !69 + %89 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %88, i32 noundef 0) #15, !dbg !28 + br label %afterSend, !dbg !28 } ; Function Attrs: ssp -define internal noundef i64 @"func_Test.16test_nilable_arg$block_3"(i64** nocapture nofree readnone %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #9 !dbg !86 { +define internal noundef i64 @"func_Test.16test_nilable_arg$block_3"(i64** nocapture nofree readnone %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #10 !dbg !86 { functionEntryInitializers: %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Test.16test_nilable_arg$block_3", align 8 %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !31 %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !33 - tail call void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #14 + tail call void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #15 %3 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !31 %4 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %3, i64 0, i32 2 %5 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !33 @@ -855,45 +896,46 @@ functionEntryInitializers: } ; Function Attrs: argmemonly nofree norecurse nosync nounwind ssp willreturn writeonly -define internal noundef i64 @"func_Test.16test_nilable_arg$block_4"(i64** nocapture nofree nonnull writeonly align 8 dereferenceable(8) %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #10 !dbg !87 { +define internal noundef i64 @"func_Test.16test_nilable_arg$block_4"(i64** nocapture nofree nonnull writeonly align 8 dereferenceable(8) %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #11 !dbg !87 { functionEntryInitializers: store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %pc, align 8, !tbaa !31 ret i64 52 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #11 +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #12 ; Function Attrs: nofree nosync nounwind willreturn -declare void @llvm.assume(i1 noundef) #12 +declare void @llvm.assume(i1 noundef) #13 ; Function Attrs: ssp -define linkonce void @const_recompute_Test() local_unnamed_addr #9 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([358 x i8], [358 x i8]* @sorbet_moduleStringTable, i64 0, i64 61), i64 4) +define linkonce void @const_recompute_Test() local_unnamed_addr #10 { + %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([363 x i8], [363 x i8]* @sorbet_moduleStringTable, i64 0, i64 61), i64 4) store i64 %1, i64* @guarded_const_Test, align 8 %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !44 store i64 %2, i64* @guard_epoch_Test, align 8 ret void } -attributes #0 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { argmemonly nofree nosync nounwind willreturn } -attributes #3 = { inaccessiblememonly nofree nosync nounwind willreturn } -attributes #4 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #5 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #6 = { sspreq } -attributes #7 = { nounwind sspreq uwtable } -attributes #8 = { noreturn nounwind ssp } -attributes #9 = { ssp } -attributes #10 = { argmemonly nofree norecurse nosync nounwind ssp willreturn writeonly } -attributes #11 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #12 = { nofree nosync nounwind willreturn } -attributes #13 = { noreturn nounwind } -attributes #14 = { nounwind } -attributes #15 = { nounwind allocsize(0,1) } -attributes #16 = { noreturn } -attributes #17 = { willreturn } +attributes #0 = { nounwind readnone willreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { argmemonly nofree nosync nounwind willreturn } +attributes #4 = { inaccessiblememonly nofree nosync nounwind willreturn } +attributes #5 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #6 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #7 = { sspreq } +attributes #8 = { nounwind sspreq uwtable } +attributes #9 = { noreturn nounwind ssp } +attributes #10 = { ssp } +attributes #11 = { argmemonly nofree norecurse nosync nounwind ssp willreturn writeonly } +attributes #12 = { argmemonly nofree nosync nounwind willreturn writeonly } +attributes #13 = { nofree nosync nounwind willreturn } +attributes #14 = { noreturn nounwind } +attributes #15 = { nounwind } +attributes #16 = { nounwind allocsize(0,1) } +attributes #17 = { noreturn } +attributes #18 = { willreturn } !llvm.module.flags = !{!0, !1, !2} !llvm.dbg.cu = !{!3} @@ -953,7 +995,7 @@ attributes #17 = { willreturn } !52 = !DILocation(line: 6, column: 3, scope: !23) !53 = !{!"branch_weights", i32 1, i32 2000} !54 = !DILocation(line: 0, scope: !23) -!55 = !DILocation(line: 8, column: 7, scope: !23) +!55 = !DILocation(line: 9, column: 5, scope: !23) !56 = !DILocation(line: 12, column: 3, scope: !23) !57 = distinct !DISubprogram(name: "Test.test_known_nil", linkageName: "func_Test.14test_known_nil$block_1", scope: !23, file: !4, line: 6, type: !12, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) !58 = !{!59} @@ -961,24 +1003,24 @@ attributes #17 = { willreturn } !60 = distinct !{!60, !"sorbet_T_must"} !61 = !DILocation(line: 8, column: 7, scope: !57) !62 = !{!38, !7, i64 24} -!63 = !DILocation(line: 0, scope: !22) -!64 = !{!"branch_weights", i32 2000, i32 1} -!65 = !DILocation(line: 9, column: 5, scope: !22) -!66 = !DILocation(line: 8, column: 7, scope: !22) -!67 = distinct !DISubprogram(name: "Test.test_known_nil", linkageName: "func_Test.14test_known_nil$block_3", scope: !23, file: !4, line: 6, type: !12, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!68 = distinct !DISubprogram(name: "Test.test_known_nil", linkageName: "func_Test.14test_known_nil$block_4", scope: !23, file: !4, line: 6, type: !12, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!69 = !DILocation(line: 14, column: 3, scope: !27) -!70 = !{!"branch_weights", i32 4001, i32 4000000} -!71 = !DILocation(line: 0, scope: !27) -!72 = !DILocation(line: 16, column: 7, scope: !27) -!73 = !DILocation(line: 21, column: 3, scope: !27) -!74 = !DILocation(line: 16, column: 7, scope: !26) -!75 = !{!76} -!76 = distinct !{!76, !77, !"sorbet_T_must: argument 0"} -!77 = distinct !{!77, !"sorbet_T_must"} -!78 = !{!34, !35, i64 40} -!79 = !{!34, !35, i64 44} -!80 = !{!34, !32, i64 56} +!63 = !{!34, !35, i64 40} +!64 = !{!34, !35, i64 44} +!65 = !{!"branch_weights", i32 2000, i32 1} +!66 = !DILocation(line: 0, scope: !22) +!67 = !DILocation(line: 9, column: 5, scope: !22) +!68 = !DILocation(line: 8, column: 7, scope: !22) +!69 = !{!34, !32, i64 56} +!70 = distinct !DISubprogram(name: "Test.test_known_nil", linkageName: "func_Test.14test_known_nil$block_3", scope: !23, file: !4, line: 6, type: !12, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) +!71 = distinct !DISubprogram(name: "Test.test_known_nil", linkageName: "func_Test.14test_known_nil$block_4", scope: !23, file: !4, line: 6, type: !12, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) +!72 = !DILocation(line: 14, column: 3, scope: !27) +!73 = !{!"branch_weights", i32 4001, i32 4000000} +!74 = !DILocation(line: 0, scope: !27) +!75 = !DILocation(line: 18, column: 5, scope: !27) +!76 = !DILocation(line: 21, column: 3, scope: !27) +!77 = !DILocation(line: 16, column: 7, scope: !26) +!78 = !{!79} +!79 = distinct !{!79, !80, !"sorbet_T_must: argument 0"} +!80 = distinct !{!80, !"sorbet_T_must"} !81 = !DILocation(line: 17, column: 19, scope: !26) !82 = !DILocation(line: 17, column: 12, scope: !26) !83 = !DILocation(line: 0, scope: !29) diff --git a/test/testdata/compiler/literal_hash.opt.ll.exp b/test/testdata/compiler/literal_hash.opt.ll.exp index 5d4b586fac..51968d72f8 100644 --- a/test/testdata/compiler/literal_hash.opt.ll.exp +++ b/test/testdata/compiler/literal_hash.opt.ll.exp @@ -2,16 +2,17 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -27,34 +28,34 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.rb_fiber_struct = type opaque -%struct.anon.3 = type { [65 x i64] } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -68,7 +69,7 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } +%struct.anon.6 = type { i64, i64, i64, i64 } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } %struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } diff --git a/test/testdata/compiler/literals.opt.ll.exp b/test/testdata/compiler/literals.opt.ll.exp index c05b5e6eec..58191cdbe3 100644 --- a/test/testdata/compiler/literals.opt.ll.exp +++ b/test/testdata/compiler/literals.opt.ll.exp @@ -2,16 +2,17 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -27,34 +28,34 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.rb_fiber_struct = type opaque -%struct.anon.3 = type { [65 x i64] } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -68,7 +69,7 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } +%struct.anon.6 = type { i64, i64, i64, i64 } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } %struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } diff --git a/test/testdata/compiler/repeated_casts.opt.ll.exp b/test/testdata/compiler/repeated_casts.opt.ll.exp index d1d1d75135..eecdfd148e 100644 --- a/test/testdata/compiler/repeated_casts.opt.ll.exp +++ b/test/testdata/compiler/repeated_casts.opt.ll.exp @@ -2,10 +2,10 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -21,27 +21,28 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_fiber_struct = type opaque -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.anon.3 = type { [65 x i64] } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -49,24 +50,24 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_objspace = type opaque %struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.anon.6 = type { i64, i64, i64, i64 } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %union.pthread_mutex_t = type { %struct.__pthread_mutex_s } %struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } %struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } diff --git a/test/testdata/compiler/send_with_block_param.opt.ll.exp b/test/testdata/compiler/send_with_block_param.opt.ll.exp index 98bc06133a..cc60a36163 100644 --- a/test/testdata/compiler/send_with_block_param.opt.ll.exp +++ b/test/testdata/compiler/send_with_block_param.opt.ll.exp @@ -2,16 +2,17 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -27,34 +28,34 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.rb_fiber_struct = type opaque -%struct.anon.3 = type { [65 x i64] } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -68,7 +69,7 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } +%struct.anon.6 = type { i64, i64, i64, i64 } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } %struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } diff --git a/test/testdata/compiler/sig_rewriter.opt.ll.exp b/test/testdata/compiler/sig_rewriter.opt.ll.exp index a13bddb1b0..aeaf21d54f 100644 --- a/test/testdata/compiler/sig_rewriter.opt.ll.exp +++ b/test/testdata/compiler/sig_rewriter.opt.ll.exp @@ -2,16 +2,17 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -27,34 +28,34 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.rb_fiber_struct = type opaque -%struct.anon.3 = type { [65 x i64] } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -68,7 +69,7 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } +%struct.anon.6 = type { i64, i64, i64, i64 } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } %struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } @@ -387,7 +388,7 @@ functionEntryInitializers: %15 = getelementptr inbounds i64, i64* %14, i64 1, !dbg !16 store i64* %15, i64** %12, align 8, !dbg !16 %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_returns, i64 0), !dbg !16 - ret i64 %send, !dbg !52 + ret i64 %send, !dbg !16 } ; Function Attrs: ssp @@ -508,4 +509,3 @@ attributes #13 = { noreturn } !49 = !{!"branch_weights", i32 1, i32 2000} !50 = !DILocation(line: 0, scope: !47) !51 = !{!36, !7, i64 24} -!52 = !DILocation(line: 7, column: 3, scope: !17) diff --git a/test/testdata/compiler/splat_assign.opt.ll.exp b/test/testdata/compiler/splat_assign.opt.ll.exp index d322d79fc0..3dabccb56a 100644 --- a/test/testdata/compiler/splat_assign.opt.ll.exp +++ b/test/testdata/compiler/splat_assign.opt.ll.exp @@ -2,16 +2,17 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -27,34 +28,34 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.rb_fiber_struct = type opaque -%struct.anon.3 = type { [65 x i64] } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -68,7 +69,7 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } +%struct.anon.6 = type { i64, i64, i64, i64 } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } %struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } diff --git a/test/testdata/compiler/unsafe.opt.ll.exp b/test/testdata/compiler/unsafe.opt.ll.exp index 8e81c201b8..214d2e6e8a 100644 --- a/test/testdata/compiler/unsafe.opt.ll.exp +++ b/test/testdata/compiler/unsafe.opt.ll.exp @@ -2,16 +2,17 @@ source_filename = "llvm-link" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.3, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.4, [29 x i16] } +%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } %struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.anon.2 } -%struct.anon.2 = type { i32, i32, i64, i64, i64, i8*, i32, i32 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.7, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } +%union.pthread_cond_t = type { %struct.__pthread_cond_s } +%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } +%union.anon = type { i64 } +%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } %struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.5 } +%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } %struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.14 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.13, i32, i32, i32, i32, i32, i8, i64 } +%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } +%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } %struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } %struct.anon.0 = type { i16, [2 x i8] } %struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } @@ -27,34 +28,34 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } %struct.rb_scope_visi_struct = type { i8, [3 x i8] } %struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.12 } +%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } %struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.10, i64, i64 } -%union.anon.10 = type { %struct.rb_method_cfunc_struct } +%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } +%union.anon.13 = type { %struct.rb_method_cfunc_struct } %struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.12 = type { i32 } +%union.anon.15 = type { i32 } %struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.13 = type { i64, i64, i64, i64* } -%union.anon.14 = type { %struct.anon.15 } -%struct.anon.15 = type { i64, i32 } +%struct.anon.16 = type { i64, i64, i64, i64* } +%union.anon.17 = type { %struct.anon.18 } +%struct.anon.18 = type { i64, i32 } %struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } %struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } %struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } %struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } %struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.5 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } +%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } %struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } %struct.__sigset_t = type { [16 x i64] } %struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.6 } -%union.anon.6 = type { %union.pthread_cond_t } +%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } +%union.anon.9 = type { %union.pthread_cond_t } %struct.rb_unblock_callback = type { void (i8*)*, i8* } %struct.rb_mutex_struct = type opaque %struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.7 = type { %struct.anon.8 } -%struct.anon.8 = type { i64, i64, i32 } +%union.anon.10 = type { %struct.anon.11 } +%struct.anon.11 = type { i64, i64, i32 } %struct.rb_fiber_struct = type opaque -%struct.anon.3 = type { [65 x i64] } +%struct.anon.5 = type { [65 x i64] } %struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } %struct.rb_event_hook_struct = type opaque %struct.rb_postponed_job_struct = type opaque @@ -68,7 +69,7 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 %struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } %struct.st_table_entry = type opaque %struct.rb_builtin_function = type opaque -%struct.anon.4 = type { i64, i64, i64, i64 } +%struct.anon.6 = type { i64, i64, i64, i64 } %struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } %struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } %struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } diff --git a/test/testdata/core/fuzz_bad_subtyping.rb b/test/testdata/core/fuzz_bad_subtyping.rb index dd30c83f35..ed127cdfe9 100644 --- a/test/testdata/core/fuzz_bad_subtyping.rb +++ b/test/testdata/core/fuzz_bad_subtyping.rb @@ -6,14 +6,14 @@ module MyEnumerable A = type_member sig {params(a: MyEnumerable[])} # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Malformed `sig`: No return type specified. Specify one with .returns() - # ^^^^^^^^^^^^^^ error: Wrong number of type parameters for `MyEnumerable`. + # ^^ error: Wrong number of type parameters for `MyEnumerable`. # ^^ error: Wrong number of type parameters for `MyEnumerable`. # ^^^^^^ error: Method `params` does not exist on `T.class_of(MyEnumerable)` # ^^^ error: Method `sig` does not exist on `T.class_of(MyEnumerable)` def -(a) end class MySet include MyEnumerable - A = # error: Type variable `A` needs to be declared as `= type_member(SOMETHING)` + A = # error: Type variable `A` needs to be declared as a type_member or type_template, not a static-field new - MySet.new() end end diff --git a/test/testdata/desugar/for.rb.cfg-text.exp b/test/testdata/desugar/for.rb.cfg-text.exp index d4cc84677f..d90568fbe5 100644 --- a/test/testdata/desugar/for.rb.cfg-text.exp +++ b/test/testdata/desugar/for.rb.cfg-text.exp @@ -120,9 +120,9 @@ bb1[rubyRegionId=0, firstDead=-1](): method ::#main { bb0[rubyRegionId=0, firstDead=-1](): - @a$105: T.untyped = alias > (@a) - @@b$115: T.untyped = alias > (@@b) - $c$125: T.untyped = alias > ($c) + @a$104: T.untyped = alias > (@a) + @@b$114: T.untyped = alias > (@@b) + $c$124: T.untyped = alias > ($c) : T.class_of(Main) = cast(: NilClass, T.class_of(Main)); $5: T.class_of(A) = alias $6: Sorbet::Private::Static::Void = $5: T.class_of(A).each() @@ -137,13 +137,13 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb5(rubyRegionId=1) -bb2[rubyRegionId=1, firstDead=-1](: T.class_of(Main), $6: Sorbet::Private::Static::Void, $7: T.class_of(Main), @a$105: T.untyped, @@b$115: T.untyped, $c$125: T.untyped): +bb2[rubyRegionId=1, firstDead=-1](: T.class_of(Main), $6: Sorbet::Private::Static::Void, $7: T.class_of(Main), @a$104: T.untyped, @@b$114: T.untyped, $c$124: T.untyped): # outerLoops: 1 -> (NilClass ? bb5 : bb3) # backedges # - bb2(rubyRegionId=1) -bb3[rubyRegionId=0, firstDead=-1]($6: Sorbet::Private::Static::Void, $7: T.class_of(Main), @a$105: T.untyped, @@b$115: T.untyped, $c$125: T.untyped): +bb3[rubyRegionId=0, firstDead=-1]($6: Sorbet::Private::Static::Void, $7: T.class_of(Main), @a$104: T.untyped, @@b$114: T.untyped, $c$124: T.untyped): $3: T.untyped = Solve<$6, each> : T.class_of(Main) = $7 $16: T.class_of(A) = alias @@ -153,7 +153,7 @@ bb3[rubyRegionId=0, firstDead=-1]($6: Sorbet::Private::Stat # backedges # - bb2(rubyRegionId=1) -bb5[rubyRegionId=1, firstDead=6](: T.class_of(Main), $6: Sorbet::Private::Static::Void, $7: T.class_of(Main), @a$105: T.untyped, @@b$115: T.untyped, $c$125: T.untyped): +bb5[rubyRegionId=1, firstDead=6](: T.class_of(Main), $6: Sorbet::Private::Static::Void, $7: T.class_of(Main), @a$104: T.untyped, @@b$114: T.untyped, $c$124: T.untyped): # outerLoops: 1 : T.class_of(Main) = loadSelf(each) $8: T.untyped = load_yield_params(each) @@ -166,13 +166,13 @@ bb5[rubyRegionId=1, firstDead=6](: T.class_of(Main), # backedges # - bb3(rubyRegionId=0) # - bb9(rubyRegionId=2) -bb6[rubyRegionId=2, firstDead=-1](: T.class_of(Main), $17: Sorbet::Private::Static::Void, $18: T.class_of(Main), @a$105: T.untyped, @@b$115: T.untyped, $c$125: T.untyped): +bb6[rubyRegionId=2, firstDead=-1](: T.class_of(Main), $17: Sorbet::Private::Static::Void, $18: T.class_of(Main), @a$104: T.untyped, @@b$114: T.untyped, $c$124: T.untyped): # outerLoops: 1 -> (NilClass ? bb9 : bb7) # backedges # - bb6(rubyRegionId=2) -bb7[rubyRegionId=0, firstDead=-1]($17: Sorbet::Private::Static::Void, $18: T.class_of(Main), @a$105: T.untyped, @@b$115: T.untyped, $c$125: T.untyped): +bb7[rubyRegionId=0, firstDead=-1]($17: Sorbet::Private::Static::Void, $18: T.class_of(Main), @a$104: T.untyped, @@b$114: T.untyped, $c$124: T.untyped): $14: T.untyped = Solve<$17, each> : T.class_of(Main) = $18 $41: T.class_of(A) = alias @@ -182,7 +182,7 @@ bb7[rubyRegionId=0, firstDead=-1]($17: Sorbet::Private::Sta # backedges # - bb6(rubyRegionId=2) -bb9[rubyRegionId=2, firstDead=14](: T.class_of(Main), $17: Sorbet::Private::Static::Void, $18: T.class_of(Main), @a$105: T.untyped, @@b$115: T.untyped, $c$125: T.untyped): +bb9[rubyRegionId=2, firstDead=14](: T.class_of(Main), $17: Sorbet::Private::Static::Void, $18: T.class_of(Main), @a$104: T.untyped, @@b$114: T.untyped, $c$124: T.untyped): # outerLoops: 1 : T.class_of(Main) = loadSelf(each) $19: T.untyped = load_yield_params(each) @@ -203,13 +203,13 @@ bb9[rubyRegionId=2, firstDead=14](: T.class_of(Main), : T.class_of(Main), $42: Sorbet::Private::Static::Void, $43: T.class_of(Main), @a$105: T.untyped, @@b$115: T.untyped, $c$125: T.untyped): +bb10[rubyRegionId=3, firstDead=-1](: T.class_of(Main), $42: Sorbet::Private::Static::Void, $43: T.class_of(Main), @a$104: T.untyped, @@b$114: T.untyped, $c$124: T.untyped): # outerLoops: 1 -> (NilClass ? bb13 : bb11) # backedges # - bb10(rubyRegionId=3) -bb11[rubyRegionId=0, firstDead=-1]($42: Sorbet::Private::Static::Void, $43: T.class_of(Main), @a$105: T.untyped, @@b$115: T.untyped, $c$125: T.untyped): +bb11[rubyRegionId=0, firstDead=-1]($42: Sorbet::Private::Static::Void, $43: T.class_of(Main), @a$104: T.untyped, @@b$114: T.untyped, $c$124: T.untyped): $39: T.untyped = Solve<$42, each> : T.class_of(Main) = $43 $56: T.class_of(A) = alias @@ -219,7 +219,7 @@ bb11[rubyRegionId=0, firstDead=-1]($42: Sorbet::Private::St # backedges # - bb10(rubyRegionId=3) -bb13[rubyRegionId=3, firstDead=9](: T.class_of(Main), $42: Sorbet::Private::Static::Void, $43: T.class_of(Main), @a$105: T.untyped, @@b$115: T.untyped, $c$125: T.untyped): +bb13[rubyRegionId=3, firstDead=9](: T.class_of(Main), $42: Sorbet::Private::Static::Void, $43: T.class_of(Main), @a$104: T.untyped, @@b$114: T.untyped, $c$124: T.untyped): # outerLoops: 1 : T.class_of(Main) = loadSelf(each) $44: T.untyped = load_yield_params(each) @@ -235,13 +235,13 @@ bb13[rubyRegionId=3, firstDead=9](: T.class_of(Main), : T.class_of(Main), $57: Sorbet::Private::Static::Void, $58: T.class_of(Main), @a$105: T.untyped, @@b$115: T.untyped, $c$125: T.untyped): +bb14[rubyRegionId=4, firstDead=-1](: T.class_of(Main), $57: Sorbet::Private::Static::Void, $58: T.class_of(Main), @a$104: T.untyped, @@b$114: T.untyped, $c$124: T.untyped): # outerLoops: 1 -> (NilClass ? bb17 : bb15) # backedges # - bb14(rubyRegionId=4) -bb15[rubyRegionId=0, firstDead=-1]($57: Sorbet::Private::Static::Void, $58: T.class_of(Main), @a$105: T.untyped, @@b$115: T.untyped, $c$125: T.untyped): +bb15[rubyRegionId=0, firstDead=-1]($57: Sorbet::Private::Static::Void, $58: T.class_of(Main), @a$104: T.untyped, @@b$114: T.untyped, $c$124: T.untyped): $54: T.untyped = Solve<$57, each> : T.class_of(Main) = $58 $88: String("main") = "main" @@ -253,7 +253,7 @@ bb15[rubyRegionId=0, firstDead=-1]($57: Sorbet::Private::St # backedges # - bb14(rubyRegionId=4) -bb17[rubyRegionId=4, firstDead=18](: T.class_of(Main), $57: Sorbet::Private::Static::Void, $58: T.class_of(Main), @a$105: T.untyped, @@b$115: T.untyped, $c$125: T.untyped): +bb17[rubyRegionId=4, firstDead=18](: T.class_of(Main), $57: Sorbet::Private::Static::Void, $58: T.class_of(Main), @a$104: T.untyped, @@b$114: T.untyped, $c$124: T.untyped): # outerLoops: 1 : T.class_of(Main) = loadSelf(each) $59: T.untyped = load_yield_params(each) @@ -278,128 +278,128 @@ bb17[rubyRegionId=4, firstDead=18](: T.class_of(Main), : T.class_of(Main), $92: Sorbet::Private::Static::Void, $93: T.class_of(Main), @a$105: T.untyped, @@b$115: T.untyped, $c$125: T.untyped): +bb18[rubyRegionId=5, firstDead=-1](: T.class_of(Main), $92: Sorbet::Private::Static::Void, $93: T.class_of(Main), @a$104: T.untyped, @@b$114: T.untyped, $c$124: T.untyped): # outerLoops: 1 -> (NilClass ? bb21 : bb19) # backedges # - bb18(rubyRegionId=5) -bb19[rubyRegionId=0, firstDead=-1]($92: Sorbet::Private::Static::Void, $93: T.class_of(Main), @a$105: T.untyped, @@b$115: T.untyped, $c$125: T.untyped): +bb19[rubyRegionId=0, firstDead=-1]($92: Sorbet::Private::Static::Void, $93: T.class_of(Main), @a$104: T.untyped, @@b$114: T.untyped, $c$124: T.untyped): $89: T.untyped = Solve<$92, each> : T.class_of(Main) = $93 - $160: T.class_of(A) = alias - $161: Sorbet::Private::Static::Void = $160: T.class_of(A).each() - $162: T.class_of(Main) = + $159: T.class_of(A) = alias + $160: Sorbet::Private::Static::Void = $159: T.class_of(A).each() + $161: T.class_of(Main) = -> bb22 # backedges # - bb18(rubyRegionId=5) -bb21[rubyRegionId=5, firstDead=40](: T.class_of(Main), $92: Sorbet::Private::Static::Void, $93: T.class_of(Main), @a$105: T.untyped, @@b$115: T.untyped, $c$125: T.untyped): +bb21[rubyRegionId=5, firstDead=40](: T.class_of(Main), $92: Sorbet::Private::Static::Void, $93: T.class_of(Main), @a$104: T.untyped, @@b$114: T.untyped, $c$124: T.untyped): # outerLoops: 1 : T.class_of(Main) = loadSelf(each) - $100: T.class_of() = alias > - $102: Integer(5) = 5 - $103: Integer(0) = 0 - $8$5: [NilClass, NilClass, NilClass, NilClass, NilClass] = $100: T.class_of().(forTemp$6$5: NilClass, $102: Integer(5), $103: Integer(0)) - $107: T.class_of() = alias > - $110: Integer(0) = 0 - $108: NilClass = $8$5: [NilClass, NilClass, NilClass, NilClass, NilClass].[]($110: Integer(0)) - $111: String("instance") = "instance" - $112: String("main") = "main" - $113: Symbol(:@a) = :@a - @a$105: NilClass = $107: T.class_of().($108: NilClass, $111: String("instance"), $112: String("main"), $113: Symbol(:@a)) - $117: T.class_of() = alias > - $120: Integer(1) = 1 - $118: NilClass = $8$5: [NilClass, NilClass, NilClass, NilClass, NilClass].[]($120: Integer(1)) - $121: String("class") = "class" - $122: String("main") = "main" - $123: Symbol(:@@b) = :@@b - @@b$115: NilClass = $117: T.class_of().($118: NilClass, $121: String("class"), $122: String("main"), $123: Symbol(:@@b)) - $127: Integer(2) = 2 - $c$125: NilClass = $8$5: [NilClass, NilClass, NilClass, NilClass, NilClass].[]($127: Integer(2)) - $130: Integer(3) = 3 - d$5: NilClass = $8$5: [NilClass, NilClass, NilClass, NilClass, NilClass].[]($130: Integer(3)) - $133: T.class_of(E) = alias - $136: Integer(4) = 4 - $134: NilClass = $8$5: [NilClass, NilClass, NilClass, NilClass, NilClass].[]($136: Integer(4)) - $131: NilClass = $133: T.class_of(E).e=($134: NilClass) - $139: T.untyped = @a$105: NilClass.inspect() - $137: NilClass = : T.class_of(Main).puts($139: T.untyped) - $143: T.untyped = @@b$115: NilClass.inspect() - $141: NilClass = : T.class_of(Main).puts($143: T.untyped) - $147: T.untyped = $c$125: NilClass.inspect() - $145: NilClass = : T.class_of(Main).puts($147: T.untyped) - $151: T.untyped = d$5: NilClass.inspect() - $149: NilClass = : T.class_of(Main).puts($151: T.untyped) - $157: T.class_of(E) = alias - $155: T.untyped = $157: T.class_of(E).e() - $154: T.untyped = $155: T.untyped.inspect() - $95: NilClass = : T.class_of(Main).puts($154: T.untyped) - $158: T.noreturn = blockreturn $95: NilClass + $99: T.class_of() = alias > + $101: Integer(5) = 5 + $102: Integer(0) = 0 + $8$5: [NilClass, NilClass, NilClass, NilClass, NilClass] = $99: T.class_of().(forTemp$6$5: NilClass, $101: Integer(5), $102: Integer(0)) + $106: T.class_of() = alias > + $109: Integer(0) = 0 + $107: NilClass = $8$5: [NilClass, NilClass, NilClass, NilClass, NilClass].[]($109: Integer(0)) + $110: String("instance") = "instance" + $111: String("main") = "main" + $112: Symbol(:@a) = :@a + @a$104: NilClass = $106: T.class_of().($107: NilClass, $110: String("instance"), $111: String("main"), $112: Symbol(:@a)) + $116: T.class_of() = alias > + $119: Integer(1) = 1 + $117: NilClass = $8$5: [NilClass, NilClass, NilClass, NilClass, NilClass].[]($119: Integer(1)) + $120: String("class") = "class" + $121: String("main") = "main" + $122: Symbol(:@@b) = :@@b + @@b$114: NilClass = $116: T.class_of().($117: NilClass, $120: String("class"), $121: String("main"), $122: Symbol(:@@b)) + $126: Integer(2) = 2 + $c$124: NilClass = $8$5: [NilClass, NilClass, NilClass, NilClass, NilClass].[]($126: Integer(2)) + $129: Integer(3) = 3 + d$5: NilClass = $8$5: [NilClass, NilClass, NilClass, NilClass, NilClass].[]($129: Integer(3)) + $132: T.class_of(E) = alias + $135: Integer(4) = 4 + $133: NilClass = $8$5: [NilClass, NilClass, NilClass, NilClass, NilClass].[]($135: Integer(4)) + $130: NilClass = $132: T.class_of(E).e=($133: NilClass) + $138: T.untyped = @a$104: NilClass.inspect() + $136: NilClass = : T.class_of(Main).puts($138: T.untyped) + $142: T.untyped = @@b$114: NilClass.inspect() + $140: NilClass = : T.class_of(Main).puts($142: T.untyped) + $146: T.untyped = $c$124: NilClass.inspect() + $144: NilClass = : T.class_of(Main).puts($146: T.untyped) + $150: T.untyped = d$5: NilClass.inspect() + $148: NilClass = : T.class_of(Main).puts($150: T.untyped) + $156: T.class_of(E) = alias + $154: T.untyped = $156: T.class_of(E).e() + $153: T.untyped = $154: T.untyped.inspect() + $94: NilClass = : T.class_of(Main).puts($153: T.untyped) + $157: T.noreturn = blockreturn $94: NilClass -> bb18 # backedges # - bb19(rubyRegionId=0) # - bb25(rubyRegionId=6) -bb22[rubyRegionId=6, firstDead=-1](: T.class_of(Main), @a$105: T.untyped, @@b$115: T.untyped, $c$125: T.untyped, $161: Sorbet::Private::Static::Void, $162: T.class_of(Main)): +bb22[rubyRegionId=6, firstDead=-1](: T.class_of(Main), @a$104: T.untyped, @@b$114: T.untyped, $c$124: T.untyped, $160: Sorbet::Private::Static::Void, $161: T.class_of(Main)): # outerLoops: 1 -> (NilClass ? bb25 : bb23) # backedges # - bb22(rubyRegionId=6) -bb23[rubyRegionId=0, firstDead=2]($161: Sorbet::Private::Static::Void, $162: T.class_of(Main)): - $2: T.untyped = Solve<$161, each> +bb23[rubyRegionId=0, firstDead=2]($160: Sorbet::Private::Static::Void, $161: T.class_of(Main)): + $2: T.untyped = Solve<$160, each> : T.noreturn = return $2: T.untyped -> bb1 # backedges # - bb22(rubyRegionId=6) -bb25[rubyRegionId=6, firstDead=44](: T.class_of(Main), @a$105: T.untyped, @@b$115: T.untyped, $c$125: T.untyped, $161: Sorbet::Private::Static::Void, $162: T.class_of(Main)): +bb25[rubyRegionId=6, firstDead=44](: T.class_of(Main), @a$104: T.untyped, @@b$114: T.untyped, $c$124: T.untyped, $160: Sorbet::Private::Static::Void, $161: T.class_of(Main)): # outerLoops: 1 : T.class_of(Main) = loadSelf(each) - $163: T.untyped = load_yield_params(each) - forTemp$6: T.untyped = yield_load_arg(0, $163: T.untyped) - $168: T.class_of() = alias > - $9$6: T.untyped = $168: T.class_of().(forTemp$6: T.untyped) - $172: T.class_of() = alias > - $174: Integer(5) = 5 - $175: Integer(0) = 0 - $10$6: T.untyped = $172: T.class_of().($9$6: T.untyped, $174: Integer(5), $175: Integer(0)) - $178: T.class_of() = alias > - $181: Integer(0) = 0 - $179: T.untyped = $10$6: T.untyped.[]($181: Integer(0)) - $182: String("instance") = "instance" - $183: String("main") = "main" - $184: Symbol(:@a) = :@a - @a$105: T.untyped = $178: T.class_of().($179: T.untyped, $182: String("instance"), $183: String("main"), $184: Symbol(:@a)) - $187: T.class_of() = alias > - $190: Integer(1) = 1 - $188: T.untyped = $10$6: T.untyped.[]($190: Integer(1)) - $191: String("class") = "class" - $192: String("main") = "main" - $193: Symbol(:@@b) = :@@b - @@b$115: T.untyped = $187: T.class_of().($188: T.untyped, $191: String("class"), $192: String("main"), $193: Symbol(:@@b)) - $196: Integer(2) = 2 - $c$125: T.untyped = $10$6: T.untyped.[]($196: Integer(2)) - $199: Integer(3) = 3 - d$6: T.untyped = $10$6: T.untyped.[]($199: Integer(3)) - $202: T.class_of(E) = alias - $205: Integer(4) = 4 - $203: T.untyped = $10$6: T.untyped.[]($205: Integer(4)) - $200: T.untyped = $202: T.class_of(E).e=($203: T.untyped) - $208: T.untyped = @a$105: T.untyped.inspect() - $206: NilClass = : T.class_of(Main).puts($208: T.untyped) - $212: T.untyped = @@b$115: T.untyped.inspect() - $210: NilClass = : T.class_of(Main).puts($212: T.untyped) - $216: T.untyped = $c$125: T.untyped.inspect() - $214: NilClass = : T.class_of(Main).puts($216: T.untyped) - $220: T.untyped = d$6: T.untyped.inspect() - $218: NilClass = : T.class_of(Main).puts($220: T.untyped) - $226: T.class_of(E) = alias - $224: T.untyped = $226: T.class_of(E).e() - $223: T.untyped = $224: T.untyped.inspect() - $164: NilClass = : T.class_of(Main).puts($223: T.untyped) - $227: T.noreturn = blockreturn $164: NilClass + $162: T.untyped = load_yield_params(each) + forTemp$6: T.untyped = yield_load_arg(0, $162: T.untyped) + $167: T.class_of() = alias > + $9$6: T.untyped = $167: T.class_of().(forTemp$6: T.untyped) + $171: T.class_of() = alias > + $173: Integer(5) = 5 + $174: Integer(0) = 0 + $10$6: T.untyped = $171: T.class_of().($9$6: T.untyped, $173: Integer(5), $174: Integer(0)) + $177: T.class_of() = alias > + $180: Integer(0) = 0 + $178: T.untyped = $10$6: T.untyped.[]($180: Integer(0)) + $181: String("instance") = "instance" + $182: String("main") = "main" + $183: Symbol(:@a) = :@a + @a$104: T.untyped = $177: T.class_of().($178: T.untyped, $181: String("instance"), $182: String("main"), $183: Symbol(:@a)) + $186: T.class_of() = alias > + $189: Integer(1) = 1 + $187: T.untyped = $10$6: T.untyped.[]($189: Integer(1)) + $190: String("class") = "class" + $191: String("main") = "main" + $192: Symbol(:@@b) = :@@b + @@b$114: T.untyped = $186: T.class_of().($187: T.untyped, $190: String("class"), $191: String("main"), $192: Symbol(:@@b)) + $195: Integer(2) = 2 + $c$124: T.untyped = $10$6: T.untyped.[]($195: Integer(2)) + $198: Integer(3) = 3 + d$6: T.untyped = $10$6: T.untyped.[]($198: Integer(3)) + $201: T.class_of(E) = alias + $204: Integer(4) = 4 + $202: T.untyped = $10$6: T.untyped.[]($204: Integer(4)) + $199: T.untyped = $201: T.class_of(E).e=($202: T.untyped) + $207: T.untyped = @a$104: T.untyped.inspect() + $205: NilClass = : T.class_of(Main).puts($207: T.untyped) + $211: T.untyped = @@b$114: T.untyped.inspect() + $209: NilClass = : T.class_of(Main).puts($211: T.untyped) + $215: T.untyped = $c$124: T.untyped.inspect() + $213: NilClass = : T.class_of(Main).puts($215: T.untyped) + $219: T.untyped = d$6: T.untyped.inspect() + $217: NilClass = : T.class_of(Main).puts($219: T.untyped) + $225: T.class_of(E) = alias + $223: T.untyped = $225: T.class_of(E).e() + $222: T.untyped = $223: T.untyped.inspect() + $163: NilClass = : T.class_of(Main).puts($222: T.untyped) + $226: T.noreturn = blockreturn $163: NilClass -> bb22 } diff --git a/test/testdata/desugar/forwarded_restarg_and_kwrestarg.rb b/test/testdata/desugar/forwarded_restarg_and_kwrestarg.rb new file mode 100644 index 0000000000..6ec4000df6 --- /dev/null +++ b/test/testdata/desugar/forwarded_restarg_and_kwrestarg.rb @@ -0,0 +1,35 @@ +# typed: true + +def pos_args(a, b, c); end +def req_kwargs(a:, b:, c:); end +def opt_kwargs(a: 1, b: 2, c: 3); end +def all_the_args(a, b: 2, &blk); end + +def foo(*) + pos_args(*) +# ^^^^^^^^^^^ error: Splats are only supported where the size of the array is known statically + + T.unsafe(self).pos_args(*) + T.unsafe(self).pos_args(1, *) + T.unsafe(self).pos_args(1, 2, *) +end + +def bar(**) + req_kwargs(**) + req_kwargs(a: 1, **) +# ^^^^^^^^ error: Cannot call `Object#req_kwargs` with a `Hash` keyword splat because the method has required keyword parameters + req_kwargs(a: 1, b: 2, **) +# ^^^^^^^^^^^^^^ error: Cannot call `Object#req_kwargs` with a `Hash` keyword splat because the method has required keyword parameters + + opt_kwargs(**) + opt_kwargs(a: 1, **) + opt_kwargs(a: 1, b: 2, **) +end + +def baz(*, **, &) + all_the_args(*, **, &) +# ^^^^^^^^^^^^^^^^^^^^^^ error: Splats are only supported where the size of the array is known statically + T.unsafe(self).all_the_args(*, **, &) + all_the_args(1, **, &) + all_the_args(1, b: 3, &) +end diff --git a/test/testdata/desugar/sclass.rb.symbol-table-raw.exp b/test/testdata/desugar/sclass.rb.symbol-table-raw.exp index 887e91b0f6..f799ac1825 100644 --- a/test/testdata/desugar/sclass.rb.symbol-table-raw.exp +++ b/test/testdata/desugar/sclass.rb.symbol-table-raw.exp @@ -14,7 +14,7 @@ class >> < > () argument @ Loc {file=test/testdata/desugar/sclass.rb start=??? end=???} method > $1># () @ Loc {file=test/testdata/desugar/sclass.rb start=13:9 end=13:14} argument @ Loc {file=test/testdata/desugar/sclass.rb start=??? end=???} - class > $1> $1>[>>] < > $1> $1> () @ Loc {file=test/testdata/desugar/sclass.rb start=12:5 end=12:10} + class > $1> $1>[>>] < > () @ Loc {file=test/testdata/desugar/sclass.rb start=12:5 end=12:10} type-member(+) > $1> $1>::>> -> LambdaParam(> $1> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > $1> targs = [ >> = B ] }) @ Loc {file=test/testdata/desugar/sclass.rb start=12:5 end=12:10} method > $1> $1>#> () @ Loc {file=test/testdata/desugar/sclass.rb start=12:5 end=16:8} argument @ Loc {file=test/testdata/desugar/sclass.rb start=??? end=???} @@ -23,13 +23,13 @@ class >> < > () type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=D) @ Loc {file=test/testdata/desugar/sclass.rb start=26:1 end=26:8} method > $1>#> () @ Loc {file=test/testdata/desugar/sclass.rb start=26:1 end=34:4} argument @ Loc {file=test/testdata/desugar/sclass.rb start=??? end=???} - class > $1> $1>[>>] < > $1> $1> () @ Loc {file=test/testdata/desugar/sclass.rb start=28:9 end=28:14} + class > $1> $1>[>>] < > () @ Loc {file=test/testdata/desugar/sclass.rb start=28:9 end=28:14} type-member(+) > $1> $1>::>> -> LambdaParam(> $1> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > $1> targs = [ >> = D ] }) @ Loc {file=test/testdata/desugar/sclass.rb start=27:5 end=27:10} method > $1> $1>#> () @ Loc {file=test/testdata/desugar/sclass.rb start=27:5 end=33:8} argument @ Loc {file=test/testdata/desugar/sclass.rb start=??? end=???} method > $1> $1># () @ Loc {file=test/testdata/desugar/sclass.rb start=29:13 end=29:18} argument @ Loc {file=test/testdata/desugar/sclass.rb start=??? end=???} - class > $1> $1> $1>[>>] < > $1> $1> $1> () @ Loc {file=test/testdata/desugar/sclass.rb start=28:9 end=28:14} + class > $1> $1> $1>[>>] < > $1> () @ Loc {file=test/testdata/desugar/sclass.rb start=28:9 end=28:14} type-member(+) > $1> $1> $1>::>> -> LambdaParam(> $1> $1> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > $1> $1> targs = [ >> = AppliedType { klass = > $1> targs = [ >> = D ] } ] }) @ Loc {file=test/testdata/desugar/sclass.rb start=28:9 end=28:14} method > $1> $1> $1>#> () @ Loc {file=test/testdata/desugar/sclass.rb start=28:9 end=32:12} argument @ Loc {file=test/testdata/desugar/sclass.rb start=??? end=???} @@ -42,7 +42,7 @@ class >> < > () argument @ Loc {file=test/testdata/desugar/sclass.rb start=??? end=???} method > $1># () @ Loc {file=test/testdata/desugar/sclass.rb start=38:9 end=38:20} argument @ Loc {file=test/testdata/desugar/sclass.rb start=??? end=???} - class > $1> $1>[>>] < > $1> $1> () @ Loc {file=test/testdata/desugar/sclass.rb start=37:5 end=37:10} + class > $1> $1>[>>] < > () @ Loc {file=test/testdata/desugar/sclass.rb start=37:5 end=37:10} type-member(+) > $1> $1>::>> -> LambdaParam(> $1> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > $1> targs = [ >> = E ] }) @ Loc {file=test/testdata/desugar/sclass.rb start=37:5 end=37:10} method > $1> $1>#> () @ Loc {file=test/testdata/desugar/sclass.rb start=37:5 end=43:8} argument @ Loc {file=test/testdata/desugar/sclass.rb start=??? end=???} @@ -57,7 +57,7 @@ class >> < > () argument -> T.untyped @ Loc {file=test/testdata/desugar/sclass.rb start=??? end=???} method > $1># () @ Loc {file=test/testdata/desugar/sclass.rb start=49:9 end=49:23} argument @ Loc {file=test/testdata/desugar/sclass.rb start=??? end=???} - class > $1> $1>[>>] < > $1> $1> (>) @ Loc {file=test/testdata/desugar/sclass.rb start=48:3 end=48:8} + class > $1> $1>[>>] < > (>) @ Loc {file=test/testdata/desugar/sclass.rb start=48:3 end=48:8} type-member(+) > $1> $1>::>> -> LambdaParam(> $1> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > $1> targs = [ >> = F ] }) @ Loc {file=test/testdata/desugar/sclass.rb start=48:3 end=48:8} method > $1> $1>#> () @ Loc {file=test/testdata/desugar/sclass.rb start=48:3 end=57:8} argument @ Loc {file=test/testdata/desugar/sclass.rb start=??? end=???} @@ -72,7 +72,7 @@ class >> < > () argument @ Loc {file=test/testdata/desugar/sclass.rb start=??? end=???} method > $1># () @ Loc {file=test/testdata/desugar/sclass.rb start=63:13 end=63:22} argument @ Loc {file=test/testdata/desugar/sclass.rb start=??? end=???} - class > $1> $1>[>>] < > $1> $1> () @ Loc {file=test/testdata/desugar/sclass.rb start=62:9 end=62:14} + class > $1> $1>[>>] < > () @ Loc {file=test/testdata/desugar/sclass.rb start=62:9 end=62:14} type-member(+) > $1> $1>::>> -> LambdaParam(> $1> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > $1> targs = [ >> = G ] }) @ Loc {file=test/testdata/desugar/sclass.rb start=62:9 end=62:14} method > $1> $1>#> () @ Loc {file=test/testdata/desugar/sclass.rb start=62:9 end=66:12} argument @ Loc {file=test/testdata/desugar/sclass.rb start=??? end=???} @@ -88,7 +88,7 @@ class >> < > () argument @ Loc {file=test/testdata/desugar/sclass.rb start=??? end=???} method > $1>::> $1># () @ Loc {file=test/testdata/desugar/sclass.rb start=77:13 end=77:23} argument @ Loc {file=test/testdata/desugar/sclass.rb start=??? end=???} - class > $1> $1>[>>] < > $1> $1> () @ Loc {file=test/testdata/desugar/sclass.rb start=75:5 end=75:10} + class > $1> $1>[>>] < > () @ Loc {file=test/testdata/desugar/sclass.rb start=75:5 end=75:10} type-member(+) > $1> $1>::>> -> LambdaParam(> $1> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > $1> targs = [ >> = H ] }) @ Loc {file=test/testdata/desugar/sclass.rb start=75:5 end=75:10} method > $1> $1>#> () @ Loc {file=test/testdata/desugar/sclass.rb start=75:5 end=81:8} argument @ Loc {file=test/testdata/desugar/sclass.rb start=??? end=???} diff --git a/test/testdata/desugar/sclass_inheritance.rb.flatten-tree.exp b/test/testdata/desugar/sclass_inheritance.rb.flatten-tree.exp index 839eff1bfe..cf9148add9 100644 --- a/test/testdata/desugar/sclass_inheritance.rb.flatten-tree.exp +++ b/test/testdata/desugar/sclass_inheritance.rb.flatten-tree.exp @@ -54,7 +54,7 @@ begin end class <> < (::MM) def newer() - ::.() + .new() end def self.() @@ -67,7 +67,7 @@ begin end class ::B<> < (::) def self.newer() - ::.() + .new() end def self.() diff --git a/test/testdata/desugar/sclass_inheritance.rb.symbol-table-raw.exp b/test/testdata/desugar/sclass_inheritance.rb.symbol-table-raw.exp index 2718b8c332..93ac75aee1 100644 --- a/test/testdata/desugar/sclass_inheritance.rb.symbol-table-raw.exp +++ b/test/testdata/desugar/sclass_inheritance.rb.symbol-table-raw.exp @@ -9,7 +9,7 @@ class >> < > () argument @ Loc {file=test/testdata/desugar/sclass_inheritance.rb start=??? end=???} method > $1># () @ Loc {file=test/testdata/desugar/sclass_inheritance.rb start=8:9 end=8:18} argument @ Loc {file=test/testdata/desugar/sclass_inheritance.rb start=??? end=???} - class > $1> $1>[>>] < > $1> $1> () @ Loc {file=test/testdata/desugar/sclass_inheritance.rb start=5:5 end=5:10} + class > $1> $1>[>>] < > () @ Loc {file=test/testdata/desugar/sclass_inheritance.rb start=5:5 end=5:10} type-member(+) > $1> $1>::>> -> LambdaParam(> $1> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > $1> targs = [ >> = A ] }) @ Loc {file=test/testdata/desugar/sclass_inheritance.rb start=5:5 end=5:10} method > $1> $1>#> () @ Loc {file=test/testdata/desugar/sclass_inheritance.rb start=5:5 end=11:8} argument @ Loc {file=test/testdata/desugar/sclass_inheritance.rb start=??? end=???} @@ -32,8 +32,7 @@ class >> < > () method > $1> $1>#> () @ Loc {file=test/testdata/desugar/sclass_inheritance.rb start=23:4 end=27:7} argument @ Loc {file=test/testdata/desugar/sclass_inheritance.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/desugar/sclass_inheritance.rb start=2:1 end=2:10} - class > $1>[>>] < > () @ Loc {file=test/testdata/desugar/sclass_inheritance.rb start=2:1 end=2:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=MM) @ Loc {file=test/testdata/desugar/sclass_inheritance.rb start=2:1 end=2:10} + class > $1> < > () @ Loc {file=test/testdata/desugar/sclass_inheritance.rb start=2:1 end=2:10} method > $1>#> () @ Loc {file=test/testdata/desugar/sclass_inheritance.rb start=2:1 end=2:15} argument @ Loc {file=test/testdata/desugar/sclass_inheritance.rb start=??? end=???} class > < > (>) @ Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed} diff --git a/test/testdata/deviations/non_ruby_names.rb.symbol-table-raw.exp b/test/testdata/deviations/non_ruby_names.rb.symbol-table-raw.exp index 12d2a3939e..2f58e03cc0 100644 --- a/test/testdata/deviations/non_ruby_names.rb.symbol-table-raw.exp +++ b/test/testdata/deviations/non_ruby_names.rb.symbol-table-raw.exp @@ -9,15 +9,12 @@ class >> < > () type-member(+) >::>::> $1>::>> -> LambdaParam(>::>::> $1>::>>, lower=T.noreturn, upper=A::B::C) @ Loc {file=test/testdata/deviations/non_ruby_names.rb start=7:3 end=7:13} method >::>::> $1>#> () @ Loc {file=test/testdata/deviations/non_ruby_names.rb start=7:3 end=8:6} argument @ Loc {file=test/testdata/deviations/non_ruby_names.rb start=??? end=???} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/deviations/non_ruby_names.rb start=7:9 end=7:10} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=A::B) @ Loc {file=test/testdata/deviations/non_ruby_names.rb start=7:9 end=7:10} - class > $1>[>>] < > () @ Loc {file=test/testdata/deviations/non_ruby_names.rb start=5:1 end=5:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/deviations/non_ruby_names.rb start=5:1 end=5:9} + class >::> $1> < > () @ Loc {file=test/testdata/deviations/non_ruby_names.rb start=7:9 end=7:10} + class > $1> < > () @ Loc {file=test/testdata/deviations/non_ruby_names.rb start=5:1 end=5:9} method > $1>#> () @ Loc {file=test/testdata/deviations/non_ruby_names.rb start=5:1 end=9:4} argument @ Loc {file=test/testdata/deviations/non_ruby_names.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/deviations/non_ruby_names.rb start=2:1 end=2:9} - class > $1>[>>] < > () @ Loc {file=test/testdata/deviations/non_ruby_names.rb start=2:1 end=2:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=B) @ Loc {file=test/testdata/deviations/non_ruby_names.rb start=2:1 end=2:9} + class > $1> < > () @ Loc {file=test/testdata/deviations/non_ruby_names.rb start=2:1 end=2:9} method > $1>#> () @ Loc {file=test/testdata/deviations/non_ruby_names.rb start=2:1 end=3:4} argument @ Loc {file=test/testdata/deviations/non_ruby_names.rb start=??? end=???} diff --git a/test/testdata/infer/and_and_nil.rb b/test/testdata/infer/and_and_nil.rb index b2d98c9d56..a78fb718de 100644 --- a/test/testdata/infer/and_and_nil.rb +++ b/test/testdata/infer/and_and_nil.rb @@ -27,7 +27,7 @@ def bar end if a.get_private && a.get_private.my_private - # ^^^^^^^^^^^^^^^^^^^^^^^^ error: Non-private call to private method `my_private` + # ^^^^^^^^^^ error: Non-private call to private method `my_private` # ^^^^^^^^^^ error: Call to method `my_private` after `&&` assumes result type doesn't change puts a.foo end diff --git a/test/testdata/infer/attached_class_intrinsic.rb b/test/testdata/infer/attached_class_intrinsic.rb index e5a6544092..157cd9d920 100644 --- a/test/testdata/infer/attached_class_intrinsic.rb +++ b/test/testdata/infer/attached_class_intrinsic.rb @@ -48,11 +48,11 @@ def self.bind_attached(&blk); end # `T.let` and the intrinsic report this error b = T.let(new, T.attached_class) # ^^^ error: Method `new` does not exist - # ^^^^^^^^^^^^^^^^ error: `T.attached_class` may only be used in a singleton class method context - # ^^^^^^^^^^^^^^^^ error: `T.attached_class` may only be used in a singleton class method context + # ^^^^^^^^^^^^^^^^ error: `T.attached_class` may only be used in singleton methods on classes or instance methods on `has_attached_class!` modules + # ^^^^^^^^^^^^^^^^ error: `T.attached_class` may only be used in singleton methods on classes or instance methods on `has_attached_class!` modules puts(b) bs = T::Array[T.attached_class].new - # ^^^^^^^^^^^^^^^^ error: `T.attached_class` may only be used in a singleton class method context + # ^^^^^^^^^^^^^^^^ error: `T.attached_class` may only be used in singleton methods on classes or instance methods on `has_attached_class!` modules T.reveal_type(bs) # error: `T::Array[T.untyped]` end diff --git a/test/testdata/infer/attached_class_module.rb b/test/testdata/infer/attached_class_module.rb new file mode 100644 index 0000000000..b5db706c70 --- /dev/null +++ b/test/testdata/infer/attached_class_module.rb @@ -0,0 +1,15 @@ +# typed: true + +module M + extend T::Sig + + sig {returns(T.attached_class)} + # ^^^^^^^^^^^^^^^^ error: `M` must declare `has_attached_class!` before module instance methods can use `T.attached_class + def instance_method + end + + sig {returns(T.attached_class)} + # ^^^^^^^^^^^^^^^^ error: `T.attached_class` cannot be used in singleton methods on modules, because modules cannot be instantiated + def self.class_method + end +end diff --git a/test/testdata/infer/autocorrect_extend_t_sig.rb.autocorrects.exp b/test/testdata/infer/autocorrect_extend_t_sig.rb.autocorrects.exp index 3c91994e51..d67db423f4 100644 --- a/test/testdata/infer/autocorrect_extend_t_sig.rb.autocorrects.exp +++ b/test/testdata/infer/autocorrect_extend_t_sig.rb.autocorrects.exp @@ -19,7 +19,6 @@ class HasSig end extend T::Sig - sig do # ^^^ error: Method `sig` does not exist load diff --git a/test/testdata/infer/autocorrect_t_let_nilable.rb b/test/testdata/infer/autocorrect_t_let_nilable.rb new file mode 100644 index 0000000000..5f590e6ac3 --- /dev/null +++ b/test/testdata/infer/autocorrect_t_let_nilable.rb @@ -0,0 +1,5 @@ +# typed: true + +x = T.let(5, T.nilable(Integer)) + T.let(x, Integer) +# ^^^^^^^^^^^^^^^^^ error: Argument does not have asserted type `Integer` diff --git a/test/testdata/infer/autocorrect_t_let_nilable.rb.autocorrects.exp b/test/testdata/infer/autocorrect_t_let_nilable.rb.autocorrects.exp new file mode 100644 index 0000000000..a0cccbeb19 --- /dev/null +++ b/test/testdata/infer/autocorrect_t_let_nilable.rb.autocorrects.exp @@ -0,0 +1,7 @@ +# -- test/testdata/infer/autocorrect_t_let_nilable.rb -- +# typed: true + +x = T.let(5, T.nilable(Integer)) + T.let(T.must(x), Integer) +# ^^^^^^^^^^^^^^^^^ error: Argument does not have asserted type `Integer` +# ------------------------------ diff --git a/test/testdata/infer/block_return_loc.rb b/test/testdata/infer/block_return_loc.rb new file mode 100644 index 0000000000..96d9aaec4f --- /dev/null +++ b/test/testdata/infer/block_return_loc.rb @@ -0,0 +1,28 @@ +# typed: true +extend T::Sig + +sig {params(blk: T.proc.returns(String)).void} +def example(&blk) +end + +example {0} +# ^ error: Expected `String` but found `Integer(0)` for block result type + +example do 0 end +# ^ error: Expected `String` but found `Integer(0)` for block result type + +example do end +# ^^^ error: Expected `String` but found `NilClass` for block result type + +example do |x| end +# ^^^ error: Expected `String` but found `NilClass` for block result type + +example do if Random.rand(2).even?; 0; else 0; end end +# ^^^ error: Expected `String` but found `Integer(0)` for block result type + +sig {params(blk: T.proc.params(x: Integer).returns(String)).void} +def example_blockpass(&blk) +end + +example_blockpass(&:even?) +# ^^^^^^ error: Expected `String` but found `T::Boolean` for block result type diff --git a/test/testdata/infer/boolean_t_boolean.rb b/test/testdata/infer/boolean_t_boolean.rb deleted file mode 100644 index 531f77af4c..0000000000 --- a/test/testdata/infer/boolean_t_boolean.rb +++ /dev/null @@ -1,48 +0,0 @@ -# typed: true - -extend T::Sig - -module ::Boolean -end - -class TrueClass - include Boolean -end - -class FalseClass - include Boolean -end - -class BadBang - extend T::Sig - - sig {returns(String)} - def !() - 'bad bang override' - end -end - -sig {params(arg0: T.untyped).returns(Boolean)} -def returns_boolean(arg0) - false -end - -sig {params(x: Boolean).returns(T::Boolean)} -def boolean_to_t_boolean(x) - x # error: Expected `T::Boolean` but found `Boolean` -end - -sig {params(x: Boolean).returns(T::Boolean)} -def explicit_return(x) - return x # error: Expected `T::Boolean` but found `Boolean` -end - -sig {returns(T::Boolean)} -def can_have_spaces - returns_boolean T.unsafe(nil) # error: Expected `T::Boolean` but found `Boolean` -end - -sig {params(x: BadBang).returns(T::Boolean)} -def should_not_suggest_bang_bang(x) - x # error: Expected `T::Boolean` but found `BadBang` -end diff --git a/test/testdata/infer/boolean_t_boolean.rb.autocorrects.exp b/test/testdata/infer/boolean_t_boolean.rb.autocorrects.exp deleted file mode 100644 index 4e1868229f..0000000000 --- a/test/testdata/infer/boolean_t_boolean.rb.autocorrects.exp +++ /dev/null @@ -1,50 +0,0 @@ -# -- test/testdata/infer/boolean_t_boolean.rb -- -# typed: true - -extend T::Sig - -module ::Boolean -end - -class TrueClass - include Boolean -end - -class FalseClass - include Boolean -end - -class BadBang - extend T::Sig - - sig {returns(String)} - def !() - 'bad bang override' - end -end - -sig {params(arg0: T.untyped).returns(Boolean)} -def returns_boolean(arg0) - false -end - -sig {params(x: Boolean).returns(T::Boolean)} -def boolean_to_t_boolean(x) - !!(x) # error: Expected `T::Boolean` but found `Boolean` -end - -sig {params(x: Boolean).returns(T::Boolean)} -def explicit_return(x) - return !!(x) # error: Expected `T::Boolean` but found `Boolean` -end - -sig {returns(T::Boolean)} -def can_have_spaces - !!(returns_boolean T.unsafe(nil)) # error: Expected `T::Boolean` but found `Boolean` -end - -sig {params(x: BadBang).returns(T::Boolean)} -def should_not_suggest_bang_bang(x) - x # error: Expected `T::Boolean` but found `BadBang` -end -# ------------------------------ diff --git a/test/testdata/infer/bound_proc.rb.cfg-text.exp b/test/testdata/infer/bound_proc.rb.cfg-text.exp index e2993ee272..9d5476c7dc 100644 --- a/test/testdata/infer/bound_proc.rb.cfg-text.exp +++ b/test/testdata/infer/bound_proc.rb.cfg-text.exp @@ -57,20 +57,20 @@ bb2[rubyRegionId=1, firstDead=-1](: T.class_of(), $79: Sorbet::Private::Static::Void, $80: T.class_of()): $76: Sorbet::Private::Static::Void = Solve<$79, class_helper> : T.class_of() = $80 - $95: T.class_of(T) = alias - $93: T.class_of() = $95: T.class_of(T).reveal_type(: T.class_of()) - $101: T.class_of(Sorbet::Private::Static) = alias - $103: T.class_of(N) = alias - $99: Sorbet::Private::Static::Void = $101: T.class_of(Sorbet::Private::Static).keep_for_ide($103: T.class_of(N)) - $108: T.class_of(Sorbet::Private::Static) = alias - $110: T.class_of(M) = alias - $106: Sorbet::Private::Static::Void = $108: T.class_of(Sorbet::Private::Static).keep_for_ide($110: T.class_of(M)) - $115: T.class_of(Sorbet::Private::Static) = alias - $117: T.class_of(ThisSelf) = alias - $113: Sorbet::Private::Static::Void = $115: T.class_of(Sorbet::Private::Static).keep_for_ide($117: T.class_of(ThisSelf)) - $122: T.class_of(Sorbet::Private::Static) = alias - $124: T.class_of(Rescues) = alias - $120: Sorbet::Private::Static::Void = $122: T.class_of(Sorbet::Private::Static).keep_for_ide($124: T.class_of(Rescues)) + $94: T.class_of(T) = alias + $92: T.class_of() = $94: T.class_of(T).reveal_type(: T.class_of()) + $100: T.class_of(Sorbet::Private::Static) = alias + $102: T.class_of(N) = alias + $98: Sorbet::Private::Static::Void = $100: T.class_of(Sorbet::Private::Static).keep_for_ide($102: T.class_of(N)) + $107: T.class_of(Sorbet::Private::Static) = alias + $109: T.class_of(M) = alias + $105: Sorbet::Private::Static::Void = $107: T.class_of(Sorbet::Private::Static).keep_for_ide($109: T.class_of(M)) + $114: T.class_of(Sorbet::Private::Static) = alias + $116: T.class_of(ThisSelf) = alias + $112: Sorbet::Private::Static::Void = $114: T.class_of(Sorbet::Private::Static).keep_for_ide($116: T.class_of(ThisSelf)) + $121: T.class_of(Sorbet::Private::Static) = alias + $123: T.class_of(Rescues) = alias + $119: Sorbet::Private::Static::Void = $121: T.class_of(Sorbet::Private::Static).keep_for_ide($123: T.class_of(Rescues)) : T.noreturn = return $2: NilClass -> bb1 @@ -79,15 +79,15 @@ bb3[rubyRegionId=0, firstDead=17]($79: Sorbet::Private::Sta bb5[rubyRegionId=1, firstDead=10](: T.class_of(), $79: Sorbet::Private::Static::Void, $80: T.class_of()): # outerLoops: 1 : T.class_of() = loadSelf(class_helper) - $85: T.class_of(B) = alias - keep_for_ide$84: T.class_of(B) = $85 - keep_for_ide$84: T.untyped = keep_for_ide$84 - $86: T.class_of() = - : B = cast($86: T.class_of(), B); - $89: T.class_of(T) = alias - $87: B = $89: T.class_of(T).reveal_type(: B) - $82: T.untyped = : B.instance_helper() - $92: T.noreturn = blockreturn $82: T.untyped + $84: T.class_of(B) = alias + keep_for_ide$83: T.class_of(B) = $84 + keep_for_ide$83: T.untyped = keep_for_ide$83 + $85: T.class_of() = + : B = cast($85: T.class_of(), B); + $88: T.class_of(T) = alias + $86: B = $88: T.class_of(T).reveal_type(: B) + $81: T.untyped = : B.instance_helper() + $91: T.noreturn = blockreturn $81: T.untyped -> bb2 } @@ -201,9 +201,9 @@ bb3[rubyRegionId=0, firstDead=2]($9: Sorbet::Private::Stati bb5[rubyRegionId=1, firstDead=4](: T.class_of(Readable), $9: Sorbet::Private::Static::Void, $10: T.class_of(Readable)): # outerLoops: 1 : T.class_of(Readable) = loadSelf(included) - $14: Symbol(:do_this) = :do_this - $12: T.untyped = : T.class_of(Readable).before_create($14: Symbol(:do_this)) - $15: T.noreturn = blockreturn $12: T.untyped + $13: Symbol(:do_this) = :do_this + $11: T.untyped = : T.class_of(Readable).before_create($13: Symbol(:do_this)) + $14: T.noreturn = blockreturn $11: T.untyped -> bb2 } @@ -242,20 +242,20 @@ bb3[rubyRegionId=0, firstDead=2]($9: Sorbet::Private::Stati bb5[rubyRegionId=1, firstDead=15](: T.class_of(Writable), $9: Sorbet::Private::Static::Void, $10: T.class_of(Writable)): # outerLoops: 1 : T.class_of(Writable) = loadSelf(included) - $16: T.class_of(T) = alias - $19: T.class_of(T) = alias - $21: T.class_of(Article) = alias - $17: Runtime object representing type: T.class_of(Article) = $19: T.class_of(T).class_of($21: T.class_of(Article)) - $24: T.class_of(T) = alias - $26: T.class_of(Post) = alias - $22: Runtime object representing type: T.class_of(Post) = $24: T.class_of(T).class_of($26: T.class_of(Post)) - keep_for_ide$14: Runtime object representing type: T.any(T.class_of(Article), T.class_of(Post)) = $16: T.class_of(T).any($17: Runtime object representing type: T.class_of(Article), $22: Runtime object representing type: T.class_of(Post)) - keep_for_ide$14: T.untyped = keep_for_ide$14 - $27: T.class_of(Writable) = - : T.any(T.class_of(Article), T.class_of(Post)) = cast($27: T.class_of(Writable), T.any(T.class_of(Article), T.class_of(Post))); - $29: Symbol(:name) = :name - $12: T.untyped = : T.any(T.class_of(Article), T.class_of(Post)).some_class_method($29: Symbol(:name)) - $30: T.noreturn = blockreturn $12: T.untyped + $15: T.class_of(T) = alias + $18: T.class_of(T) = alias + $20: T.class_of(Article) = alias + $16: Runtime object representing type: T.class_of(Article) = $18: T.class_of(T).class_of($20: T.class_of(Article)) + $23: T.class_of(T) = alias + $25: T.class_of(Post) = alias + $21: Runtime object representing type: T.class_of(Post) = $23: T.class_of(T).class_of($25: T.class_of(Post)) + keep_for_ide$13: Runtime object representing type: T.any(T.class_of(Article), T.class_of(Post)) = $15: T.class_of(T).any($16: Runtime object representing type: T.class_of(Article), $21: Runtime object representing type: T.class_of(Post)) + keep_for_ide$13: T.untyped = keep_for_ide$13 + $26: T.class_of(Writable) = + : T.any(T.class_of(Article), T.class_of(Post)) = cast($26: T.class_of(Writable), T.any(T.class_of(Article), T.class_of(Post))); + $28: Symbol(:name) = :name + $11: T.untyped = : T.any(T.class_of(Article), T.class_of(Post)).some_class_method($28: Symbol(:name)) + $29: T.noreturn = blockreturn $11: T.untyped -> bb2 } @@ -294,15 +294,15 @@ bb3[rubyRegionId=0, firstDead=2]($9: Sorbet::Private::Stati bb5[rubyRegionId=1, firstDead=10](: T.class_of(Shareable), $9: Sorbet::Private::Static::Void, $10: T.class_of(Shareable)): # outerLoops: 1 : T.class_of(Shareable) = loadSelf(included) - $16: T.class_of(T) = alias - $18: T.class_of(Article) = alias - keep_for_ide$14: Runtime object representing type: T.class_of(Article) = $16: T.class_of(T).class_of($18: T.class_of(Article)) - keep_for_ide$14: T.untyped = keep_for_ide$14 - $19: T.class_of(Shareable) = - : T.class_of(Article) = cast($19: T.class_of(Shareable), T.class_of(Article)); - $21: Symbol(:do_this) = :do_this - $12: T.untyped = : T.class_of(Article).before_save($21: Symbol(:do_this)) - $22: T.noreturn = blockreturn $12: T.untyped + $15: T.class_of(T) = alias + $17: T.class_of(Article) = alias + keep_for_ide$13: Runtime object representing type: T.class_of(Article) = $15: T.class_of(T).class_of($17: T.class_of(Article)) + keep_for_ide$13: T.untyped = keep_for_ide$13 + $18: T.class_of(Shareable) = + : T.class_of(Article) = cast($18: T.class_of(Shareable), T.class_of(Article)); + $20: Symbol(:do_this) = :do_this + $11: T.untyped = : T.class_of(Article).before_save($20: Symbol(:do_this)) + $21: T.noreturn = blockreturn $11: T.untyped -> bb2 } @@ -408,8 +408,8 @@ bb3[rubyRegionId=0, firstDead=3]($12: T.class_of(Post), $13: bb5[rubyRegionId=1, firstDead=3](: T.class_of(Post), $12: T.class_of(Post), $13: Symbol(:run_callback), $14: Symbol(:if), $18: Sorbet::Private::Static::Void, $19: T.class_of(Post)): # outerLoops: 1 : T.class_of(Post) = loadSelf(lambda) - $21: T.untyped = : T.class_of(Post).should_run_callback?() - $23: T.noreturn = blockreturn $21: T.untyped + $20: T.untyped = : T.class_of(Post).should_run_callback?() + $22: T.noreturn = blockreturn $20: T.untyped -> bb2 } @@ -484,13 +484,13 @@ bb3[rubyRegionId=0, firstDead=3]($12: T.class_of(Article), $ bb5[rubyRegionId=1, firstDead=8](: T.class_of(Article), $12: T.class_of(Article), $13: Symbol(:run_callback), $14: Symbol(:if), $18: Sorbet::Private::Static::Void, $19: T.class_of(Article)): # outerLoops: 1 : T.class_of(Article) = loadSelf(lambda) - $24: T.class_of(Article) = alias - keep_for_ide$23: T.class_of(Article) = $24 - keep_for_ide$23: T.untyped = keep_for_ide$23 - $25: T.class_of(Article) = - : Article = cast($25: T.class_of(Article), Article); - $21: T.untyped = : Article.should_run_callback?() - $26: T.noreturn = blockreturn $21: T.untyped + $23: T.class_of(Article) = alias + keep_for_ide$22: T.class_of(Article) = $23 + keep_for_ide$22: T.untyped = keep_for_ide$22 + $24: T.class_of(Article) = + : Article = cast($24: T.class_of(Article), Article); + $20: T.untyped = : Article.should_run_callback?() + $25: T.noreturn = blockreturn $20: T.untyped -> bb2 } @@ -535,9 +535,9 @@ bb2[rubyRegionId=1, firstDead=-1](: T.class_of(A), $7 bb3[rubyRegionId=0, firstDead=6]($7: Sorbet::Private::Static::Void, $8: T.class_of(A)): f: T.proc.returns(T.untyped) = Solve<$7, lambda> : T.class_of(A) = $8 - $23: T.class_of(T) = alias - $21: T.class_of(A) = $23: T.class_of(T).reveal_type(: T.class_of(A)) - $25: NilClass = : T.class_of(A).puts(f: T.proc.returns(T.untyped)) + $22: T.class_of(T) = alias + $20: T.class_of(A) = $22: T.class_of(T).reveal_type(: T.class_of(A)) + $24: NilClass = : T.class_of(A).puts(f: T.proc.returns(T.untyped)) : T.noreturn = return $2: NilClass -> bb1 @@ -546,15 +546,15 @@ bb3[rubyRegionId=0, firstDead=6]($7: Sorbet::Private::Stati bb5[rubyRegionId=1, firstDead=10](: T.class_of(A), $7: Sorbet::Private::Static::Void, $8: T.class_of(A)): # outerLoops: 1 : T.class_of(A) = loadSelf(lambda) - $13: T.class_of(A) = alias - keep_for_ide$12: T.class_of(A) = $13 - keep_for_ide$12: T.untyped = keep_for_ide$12 - $14: T.class_of(A) = - : A = cast($14: T.class_of(A), A); - $17: T.class_of(T) = alias - $15: A = $17: T.class_of(T).reveal_type(: A) - $10: T.untyped = : A.instance_helper() - $20: T.noreturn = blockreturn $10: T.untyped + $12: T.class_of(A) = alias + keep_for_ide$11: T.class_of(A) = $12 + keep_for_ide$11: T.untyped = keep_for_ide$11 + $13: T.class_of(A) = + : A = cast($13: T.class_of(A), A); + $16: T.class_of(T) = alias + $14: A = $16: T.class_of(T).reveal_type(: A) + $9: T.untyped = : A.instance_helper() + $19: T.noreturn = blockreturn $9: T.untyped -> bb2 } @@ -613,9 +613,9 @@ bb2[rubyRegionId=1, firstDead=-1](: T.class_of(B), $7 bb3[rubyRegionId=0, firstDead=6]($7: Sorbet::Private::Static::Void, $8: T.class_of(B)): $3: Sorbet::Private::Static::Void = Solve<$7, sig> : T.class_of(B) = $8 - $22: T.class_of(T::Sig) = alias - $24: T.class_of(T) = alias - $19: T.class_of(B) = : T.class_of(B).extend($22: T.class_of(T::Sig)) + $21: T.class_of(T::Sig) = alias + $23: T.class_of(T) = alias + $18: T.class_of(B) = : T.class_of(B).extend($21: T.class_of(T::Sig)) : T.noreturn = return $2: NilClass -> bb1 @@ -624,13 +624,13 @@ bb3[rubyRegionId=0, firstDead=6]($7: Sorbet::Private::Stati bb5[rubyRegionId=1, firstDead=8](: T.class_of(B), $7: Sorbet::Private::Static::Void, $8: T.class_of(B)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $13: Symbol(:blk) = :blk - $17: T.class_of(T) = alias - $15: T.class_of(T.proc) = $17: T.class_of(T).proc() - $14: Runtime object representing type: T.proc.void = $15: T.class_of(T.proc).void() - $11: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($13: Symbol(:blk), $14: Runtime object representing type: T.proc.void) - $10: T::Private::Methods::DeclBuilder = $11: T::Private::Methods::DeclBuilder.void() - $18: T.noreturn = blockreturn $10: T::Private::Methods::DeclBuilder + $12: Symbol(:blk) = :blk + $16: T.class_of(T) = alias + $14: T.class_of(T.proc) = $16: T.class_of(T).proc() + $13: Runtime object representing type: T.proc.void = $14: T.class_of(T.proc).void() + $10: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($12: Symbol(:blk), $13: Runtime object representing type: T.proc.void) + $9: T::Private::Methods::DeclBuilder = $10: T::Private::Methods::DeclBuilder.void() + $17: T.noreturn = blockreturn $9: T::Private::Methods::DeclBuilder -> bb2 } @@ -794,8 +794,8 @@ bb1[rubyRegionId=0, firstDead=-1](): # - bb4(rubyRegionId=1) bb3[rubyRegionId=2, firstDead=-1](: String, $2: T.nilable(String), $3: T.untyped, $11: T.class_of()): $14: T.class_of(StandardError) = alias - $15: T.untyped = $3: T.untyped.is_a?($14: T.class_of(StandardError)) - $15 -> (T.untyped ? bb7 : bb8) + $15: T::Boolean = $14: T.class_of(StandardError).===($3: T.untyped) + $15 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) @@ -869,8 +869,8 @@ bb1[rubyRegionId=0, firstDead=-1](): # - bb4(rubyRegionId=1) bb3[rubyRegionId=2, firstDead=-1](: T.any(Float, String), $2: T.nilable(String), $3: T.untyped, $11: T.class_of()): $14: T.class_of(StandardError) = alias - $15: T.untyped = $3: T.untyped.is_a?($14: T.class_of(StandardError)) - $15 -> (T.untyped ? bb7 : bb8) + $15: T::Boolean = $14: T.class_of(StandardError).===($3: T.untyped) + $15 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) @@ -954,8 +954,8 @@ bb1[rubyRegionId=0, firstDead=-1](): # - bb4(rubyRegionId=1) bb3[rubyRegionId=2, firstDead=-1](: String, $2: T.nilable(String), $7: T.untyped, $15: T.class_of()): $18: T.class_of(StandardError) = alias - $19: T.untyped = $7: T.untyped.is_a?($18: T.class_of(StandardError)) - $19 -> (T.untyped ? bb7 : bb8) + $19: T::Boolean = $18: T.class_of(StandardError).===($7: T.untyped) + $19 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) @@ -1022,7 +1022,7 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb13(rubyRegionId=1) -bb2[rubyRegionId=1, firstDead=-1](: T.class_of(Rescues), $6: Sorbet::Private::Static::Void, $7: T.class_of(Rescues), $9: NilClass, $14: NilClass, $26: NilClass): +bb2[rubyRegionId=1, firstDead=-1](: T.class_of(Rescues), $6: Sorbet::Private::Static::Void, $7: T.class_of(Rescues), $8: NilClass, $13: NilClass, $25: NilClass): # outerLoops: 1 -> (NilClass ? bb5 : bb3) @@ -1035,40 +1035,40 @@ bb3[rubyRegionId=0, firstDead=2]($6: Sorbet::Private::Stati # backedges # - bb2(rubyRegionId=1) -bb5[rubyRegionId=1, firstDead=-1](: T.class_of(Rescues), $6: Sorbet::Private::Static::Void, $7: T.class_of(Rescues), $9: NilClass, $14: NilClass, $26: NilClass): +bb5[rubyRegionId=1, firstDead=-1](: T.class_of(Rescues), $6: Sorbet::Private::Static::Void, $7: T.class_of(Rescues), $8: NilClass, $13: NilClass, $25: NilClass): # outerLoops: 1 : T.class_of(Rescues) = loadSelf(takes_block) - $18: T.class_of() = alias > - $10: T.untyped = - : Integer = cast($14: NilClass, Integer); - $10 -> (T.untyped ? bb7 : bb8) + $17: T.class_of() = alias > + $9: T.untyped = + : Integer = cast($13: NilClass, Integer); + $9 -> (T.untyped ? bb7 : bb8) # backedges # - bb5(rubyRegionId=1) # - bb8(rubyRegionId=2) -bb7[rubyRegionId=3, firstDead=-1](: Integer, $6: Sorbet::Private::Static::Void, $7: T.class_of(Rescues), $9: T.nilable(Integer), $10: T.untyped, $14: T.nilable(Integer), $18: T.class_of(), $26: NilClass): +bb7[rubyRegionId=3, firstDead=-1](: Integer, $6: Sorbet::Private::Static::Void, $7: T.class_of(Rescues), $8: T.nilable(Integer), $9: T.untyped, $13: T.nilable(Integer), $17: T.class_of(), $25: NilClass): # outerLoops: 1 - $21: T.class_of(StandardError) = alias - $22: T.untyped = $10: T.untyped.is_a?($21: T.class_of(StandardError)) - $22 -> (T.untyped ? bb11 : bb12) + $20: T.class_of(StandardError) = alias + $21: T::Boolean = $20: T.class_of(StandardError).===($9: T.untyped) + $21 -> (T::Boolean ? bb11 : bb12) # backedges # - bb5(rubyRegionId=1) -bb8[rubyRegionId=2, firstDead=-1](: Integer, $6: Sorbet::Private::Static::Void, $7: T.class_of(Rescues), $18: T.class_of(), $26: NilClass): +bb8[rubyRegionId=2, firstDead=-1](: Integer, $6: Sorbet::Private::Static::Void, $7: T.class_of(Rescues), $17: T.class_of(), $25: NilClass): # outerLoops: 1 - $13: T.class_of(Integer) = alias - keep_for_ide$12: T.class_of(Integer) = $13 - keep_for_ide$12: T.untyped = keep_for_ide$12 - $14: Integer = - : Integer = cast($14: Integer, Integer); - $16: T.class_of(T) = alias - $9: Integer = $16: T.class_of(T).reveal_type(: Integer) - $10: T.untyped = - $10 -> (T.untyped ? bb7 : bb9) + $12: T.class_of(Integer) = alias + keep_for_ide$11: T.class_of(Integer) = $12 + keep_for_ide$11: T.untyped = keep_for_ide$11 + $13: Integer = + : Integer = cast($13: Integer, Integer); + $15: T.class_of(T) = alias + $8: Integer = $15: T.class_of(T).reveal_type(: Integer) + $9: T.untyped = + $9 -> (T.untyped ? bb7 : bb9) # backedges # - bb8(rubyRegionId=2) -bb9[rubyRegionId=5, firstDead=-1](: Integer, $6: Sorbet::Private::Static::Void, $7: T.class_of(Rescues), $9: Integer, $14: Integer, $26: NilClass): +bb9[rubyRegionId=5, firstDead=-1](: Integer, $6: Sorbet::Private::Static::Void, $7: T.class_of(Rescues), $8: Integer, $13: Integer, $25: NilClass): # outerLoops: 1 -> bb10 @@ -1076,32 +1076,32 @@ bb9[rubyRegionId=5, firstDead=-1](: Integer, $6: Sorb # - bb9(rubyRegionId=5) # - bb11(rubyRegionId=3) # - bb12(rubyRegionId=3) -bb10[rubyRegionId=4, firstDead=-1](: Integer, $6: Sorbet::Private::Static::Void, $7: T.class_of(Rescues), $9: T.nilable(Integer), $14: T.nilable(Integer), $26: T.nilable(TrueClass)): +bb10[rubyRegionId=4, firstDead=-1](: Integer, $6: Sorbet::Private::Static::Void, $7: T.class_of(Rescues), $8: T.nilable(Integer), $13: T.nilable(Integer), $25: T.nilable(TrueClass)): # outerLoops: 1 - $26 -> (T.nilable(TrueClass) ? bb1 : bb13) + $25 -> (T.nilable(TrueClass) ? bb1 : bb13) # backedges # - bb7(rubyRegionId=3) -bb11[rubyRegionId=3, firstDead=-1](: Integer, $6: Sorbet::Private::Static::Void, $7: T.class_of(Rescues), $14: T.nilable(Integer), $18: T.class_of(), $26: NilClass): +bb11[rubyRegionId=3, firstDead=-1](: Integer, $6: Sorbet::Private::Static::Void, $7: T.class_of(Rescues), $13: T.nilable(Integer), $17: T.class_of(), $25: NilClass): # outerLoops: 1 - $10: NilClass = nil - $19: Sorbet::Private::Static::Void = $18: T.class_of().($10: NilClass) - $24: T.class_of(T) = alias - $9: Integer = $24: T.class_of(T).reveal_type(: Integer) + $9: NilClass = nil + $18: Sorbet::Private::Static::Void = $17: T.class_of().($9: NilClass) + $23: T.class_of(T) = alias + $8: Integer = $23: T.class_of(T).reveal_type(: Integer) -> bb10 # backedges # - bb7(rubyRegionId=3) -bb12[rubyRegionId=3, firstDead=-1](: Integer, $6: Sorbet::Private::Static::Void, $7: T.class_of(Rescues), $9: T.nilable(Integer), $14: T.nilable(Integer)): +bb12[rubyRegionId=3, firstDead=-1](: Integer, $6: Sorbet::Private::Static::Void, $7: T.class_of(Rescues), $8: T.nilable(Integer), $13: T.nilable(Integer)): # outerLoops: 1 - $26: TrueClass = true + $25: TrueClass = true -> bb10 # backedges # - bb10(rubyRegionId=4) -bb13[rubyRegionId=1, firstDead=1](: Integer, $6: Sorbet::Private::Static::Void, $7: T.class_of(Rescues), $9: Integer, $14: T.nilable(Integer), $26: NilClass): +bb13[rubyRegionId=1, firstDead=1](: Integer, $6: Sorbet::Private::Static::Void, $7: T.class_of(Rescues), $8: Integer, $13: T.nilable(Integer), $25: NilClass): # outerLoops: 1 - $28: T.noreturn = blockreturn $9: Integer + $27: T.noreturn = blockreturn $8: Integer -> bb2 } diff --git a/test/testdata/infer/call_with_block.rb b/test/testdata/infer/call_with_block.rb index ac26a0d693..7e7e2a520c 100644 --- a/test/testdata/infer/call_with_block.rb +++ b/test/testdata/infer/call_with_block.rb @@ -44,7 +44,7 @@ def baz(a, &blk) def int_map(&blk) a = [1, 2, 3].map(&blk) T.reveal_type(a) # error: Revealed type: `T::Array[String]` - ["a", "b"].map(&blk) # error: Expected `T.proc.params(arg0: String).returns()` but found `T.proc.params(arg0: Integer).returns(String)` for block argument + ["a", "b"].map(&blk) # error: Expected `T.proc.params(arg0: String).returns(T.anything)` but found `T.proc.params(arg0: Integer).returns(String)` for block argument end sig {params(blk: Proc).void} diff --git a/test/testdata/infer/call_with_block_strict.rb b/test/testdata/infer/call_with_block_strict.rb index d1ab3a525e..f98e7c1c97 100644 --- a/test/testdata/infer/call_with_block_strict.rb +++ b/test/testdata/infer/call_with_block_strict.rb @@ -44,12 +44,12 @@ def baz(a, &blk) def int_map(&blk) a = [1, 2, 3].map(&blk) T.reveal_type(a) # error: Revealed type: `T::Array[String]` - ["a", "b"].map(&blk) # error: Expected `T.proc.params(arg0: String).returns()` but found `T.proc.params(arg0: Integer).returns(String)` for block argument + ["a", "b"].map(&blk) # error: Expected `T.proc.params(arg0: String).returns(T.anything)` but found `T.proc.params(arg0: Integer).returns(String)` for block argument end sig {params(blk: Proc).void} def unknown_arity(&blk) - a = [1, 2, 3].map(&blk) # error: Cannot use a `Proc` with unknown arity as a `T.proc.params(arg0: Integer).returns()` + a = [1, 2, 3].map(&blk) # error: Cannot use a `Proc` with unknown arity as a `T.proc.params(arg0: Integer).returns(T.anything)` T.reveal_type(a) # error: Revealed type: `T::Array[T.untyped]` end diff --git a/test/testdata/infer/class.rb b/test/testdata/infer/class.rb index f94cd84675..c049123da7 100644 --- a/test/testdata/infer/class.rb +++ b/test/testdata/infer/class.rb @@ -10,9 +10,8 @@ def instance_method # Test that Class's methods work self.class.allocate - T.assert_type!(self.class.new, TestCase) - T.assert_type!(self.class, T.class_of(TestCase)) - T.assert_type!(self.class, Class) - T.assert_type!(self.class.class, Class) + T.reveal_type(self.class.new) # error: `TestCase` + T.reveal_type(self.class) # error: `T.class_of(TestCase)` + T.reveal_type(self.class.class) # error: `T::Class[T.class_of(TestCase)]` end end diff --git a/test/testdata/infer/class_not_class_of.rb b/test/testdata/infer/class_not_class_of.rb index e138a1a45c..dac6d61560 100644 --- a/test/testdata/infer/class_not_class_of.rb +++ b/test/testdata/infer/class_not_class_of.rb @@ -4,6 +4,6 @@ sig {params(c: Class).void} def take_class(c) - T.reveal_type(c) # error: Revealed type: `Class` + T.reveal_type(c) # error: Revealed type: `T::Class[T.anything]` c === c end diff --git a/test/testdata/infer/class_of_printing.rb b/test/testdata/infer/class_of_printing.rb new file mode 100644 index 0000000000..b50cba07de --- /dev/null +++ b/test/testdata/infer/class_of_printing.rb @@ -0,0 +1,18 @@ +# typed: true +extend T::Sig + +module Left; end +module Right; end +class Parent; end + +sig {params(x: T.all(T.class_of(Parent), T::Class[Left])).void} +def example1(x) + T.reveal_type(x) # error: `T.class_of(Parent)[T.all(Parent, Left)]` +end + +sig {params(x: T.all(T.class_of(Parent), T::Class[Right])).void} +def example2(x) + T.reveal_type(x) # error: `T.class_of(Parent)[T.all(Parent, Right)]` + example1(x) + # ^ error: `T.class_of(Parent)[T.all(Parent, Left)]` but found `T.class_of(Parent)[T.all(Parent, Right)]` for argument `x` +end diff --git a/test/testdata/infer/collapse_hash_nil_union.rb b/test/testdata/infer/collapse_hash_nil_union.rb new file mode 100644 index 0000000000..4059a50809 --- /dev/null +++ b/test/testdata/infer/collapse_hash_nil_union.rb @@ -0,0 +1,21 @@ +# typed: true +extend T::Sig + +sig {returns(T.nilable(T::Hash[String, String]))} +def returns_nilable_hash + {} +end + +if T.unsafe(nil) + foo = + if T.unsafe(nil) + unless T.unsafe(nil) + returns_nilable_hash + else + nil + end + else + T::Hash[String, String].new + end + T.reveal_type(foo) # error: T.nilable(T::Hash[String, String]) +end diff --git a/test/testdata/infer/control_flow/complex_implication_1.rb.cfg-text.exp b/test/testdata/infer/control_flow/complex_implication_1.rb.cfg-text.exp index d09381d659..e62d9d87fd 100644 --- a/test/testdata/infer/control_flow/complex_implication_1.rb.cfg-text.exp +++ b/test/testdata/infer/control_flow/complex_implication_1.rb.cfg-text.exp @@ -37,8 +37,8 @@ bb1[rubyRegionId=0, firstDead=-1](): bb3[rubyRegionId=2, firstDead=-1](final_attempt: T.untyped, foo: T.untyped, $4: T.untyped, $5: T.class_of()): e: T.untyped = $4 $8: T.class_of(StandardError) = alias - $9: T.untyped = e: T.untyped.is_a?($8: T.class_of(StandardError)) - $9 -> (T.untyped ? bb7 : bb8) + $9: T::Boolean = $8: T.class_of(StandardError).===(e: T.untyped) + $9 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) diff --git a/test/testdata/infer/control_flow/normalize_params.rb.cfg-text.exp b/test/testdata/infer/control_flow/normalize_params.rb.cfg-text.exp index de8f29d663..2750f7cda4 100644 --- a/test/testdata/infer/control_flow/normalize_params.rb.cfg-text.exp +++ b/test/testdata/infer/control_flow/normalize_params.rb.cfg-text.exp @@ -20,8 +20,8 @@ method ::Test#normalize_params { bb0[rubyRegionId=0, firstDead=-1](): : Test = cast(: NilClass, Test); v: T.untyped = load_arg(v) - $6: T.class_of(Hash) = alias - $3: T.untyped = v: T.untyped.is_a?($6: T.class_of(Hash)) + $6: T.class_of(Hash)[T::Hash[T.untyped, T.untyped]] = alias + $3: T.untyped = v: T.untyped.is_a?($6: T.class_of(Hash)[T::Hash[T.untyped, T.untyped]]) $3 -> (T.untyped ? bb2 : bb3) # backedges @@ -40,8 +40,8 @@ bb2[rubyRegionId=0, firstDead=-1](: Test, v: T::Hash[T.untyped, T.untyped] # backedges # - bb0(rubyRegionId=0) bb3[rubyRegionId=0, firstDead=-1](: Test, v: T.untyped): - $14: T.class_of(Array) = alias - $11: T.untyped = v: T.untyped.is_a?($14: T.class_of(Array)) + $14: T.class_of(Array)[T::Array[T.untyped]] = alias + $11: T.untyped = v: T.untyped.is_a?($14: T.class_of(Array)[T::Array[T.untyped]]) $11 -> (T.untyped ? bb4 : bb5) # backedges diff --git a/test/testdata/infer/control_flow/simple.rb.cfg-text.exp b/test/testdata/infer/control_flow/simple.rb.cfg-text.exp index f89a53b2b5..1a85fc4815 100644 --- a/test/testdata/infer/control_flow/simple.rb.cfg-text.exp +++ b/test/testdata/infer/control_flow/simple.rb.cfg-text.exp @@ -372,9 +372,9 @@ bb2[rubyRegionId=1, firstDead=-1](: T.class_of(ControlFlow), $7: Sorbet::Private::Static::Void, $8: T.class_of(ControlFlow)): $3: Sorbet::Private::Static::Void = Solve<$7, sig> : T.class_of(ControlFlow) = $8 - $26: T.class_of(Sorbet::Private::Static) = alias - $28: Sorbet::Private::Static::Void = $26: T.class_of(Sorbet::Private::Static).sig(: T.class_of(ControlFlow)) - $29: T.class_of(ControlFlow) = + $25: T.class_of(Sorbet::Private::Static) = alias + $27: Sorbet::Private::Static::Void = $25: T.class_of(Sorbet::Private::Static).sig(: T.class_of(ControlFlow)) + $28: T.class_of(ControlFlow) = -> bb6 # backedges @@ -382,277 +382,277 @@ bb3[rubyRegionId=0, firstDead=-1]($7: Sorbet::Private::Stat bb5[rubyRegionId=1, firstDead=10](: T.class_of(ControlFlow), $7: Sorbet::Private::Static::Void, $8: T.class_of(ControlFlow)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $13: Symbol(:a) = :a - $16: T.class_of(T) = alias - $18: T.class_of(Integer) = alias - $20: T.class_of(NilClass) = alias - $14: Runtime object representing type: T.nilable(Integer) = $16: T.class_of(T).any($18: T.class_of(Integer), $20: T.class_of(NilClass)) - $11: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($13: Symbol(:a), $14: Runtime object representing type: T.nilable(Integer)) - $22: T.class_of(Integer) = alias - $10: T::Private::Methods::DeclBuilder = $11: T::Private::Methods::DeclBuilder.returns($22: T.class_of(Integer)) - $23: T.noreturn = blockreturn $10: T::Private::Methods::DeclBuilder + $12: Symbol(:a) = :a + $15: T.class_of(T) = alias + $17: T.class_of(Integer) = alias + $19: T.class_of(NilClass) = alias + $13: Runtime object representing type: T.nilable(Integer) = $15: T.class_of(T).any($17: T.class_of(Integer), $19: T.class_of(NilClass)) + $10: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($12: Symbol(:a), $13: Runtime object representing type: T.nilable(Integer)) + $21: T.class_of(Integer) = alias + $9: T::Private::Methods::DeclBuilder = $10: T::Private::Methods::DeclBuilder.returns($21: T.class_of(Integer)) + $22: T.noreturn = blockreturn $9: T::Private::Methods::DeclBuilder -> bb2 # backedges # - bb3(rubyRegionId=0) # - bb9(rubyRegionId=2) -bb6[rubyRegionId=2, firstDead=-1](: T.class_of(ControlFlow), $28: Sorbet::Private::Static::Void, $29: T.class_of(ControlFlow)): +bb6[rubyRegionId=2, firstDead=-1](: T.class_of(ControlFlow), $27: Sorbet::Private::Static::Void, $28: T.class_of(ControlFlow)): # outerLoops: 1 -> (NilClass ? bb9 : bb7) # backedges # - bb6(rubyRegionId=2) -bb7[rubyRegionId=0, firstDead=-1]($28: Sorbet::Private::Static::Void, $29: T.class_of(ControlFlow)): - $24: Sorbet::Private::Static::Void = Solve<$28, sig> - : T.class_of(ControlFlow) = $29 - $42: T.class_of(Sorbet::Private::Static) = alias - $44: Sorbet::Private::Static::Void = $42: T.class_of(Sorbet::Private::Static).sig(: T.class_of(ControlFlow)) - $45: T.class_of(ControlFlow) = +bb7[rubyRegionId=0, firstDead=-1]($27: Sorbet::Private::Static::Void, $28: T.class_of(ControlFlow)): + $23: Sorbet::Private::Static::Void = Solve<$27, sig> + : T.class_of(ControlFlow) = $28 + $40: T.class_of(Sorbet::Private::Static) = alias + $42: Sorbet::Private::Static::Void = $40: T.class_of(Sorbet::Private::Static).sig(: T.class_of(ControlFlow)) + $43: T.class_of(ControlFlow) = -> bb10 # backedges # - bb6(rubyRegionId=2) -bb9[rubyRegionId=2, firstDead=7](: T.class_of(ControlFlow), $28: Sorbet::Private::Static::Void, $29: T.class_of(ControlFlow)): +bb9[rubyRegionId=2, firstDead=7](: T.class_of(ControlFlow), $27: Sorbet::Private::Static::Void, $28: T.class_of(ControlFlow)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $34: Symbol(:a) = :a + $32: Symbol(:a) = :a + $34: T.class_of(Integer) = alias + $30: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($32: Symbol(:a), $34: T.class_of(Integer)) $36: T.class_of(Integer) = alias - $32: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($34: Symbol(:a), $36: T.class_of(Integer)) - $38: T.class_of(Integer) = alias - $31: T::Private::Methods::DeclBuilder = $32: T::Private::Methods::DeclBuilder.returns($38: T.class_of(Integer)) - $39: T.noreturn = blockreturn $31: T::Private::Methods::DeclBuilder + $29: T::Private::Methods::DeclBuilder = $30: T::Private::Methods::DeclBuilder.returns($36: T.class_of(Integer)) + $37: T.noreturn = blockreturn $29: T::Private::Methods::DeclBuilder -> bb6 # backedges # - bb7(rubyRegionId=0) # - bb13(rubyRegionId=3) -bb10[rubyRegionId=3, firstDead=-1](: T.class_of(ControlFlow), $44: Sorbet::Private::Static::Void, $45: T.class_of(ControlFlow)): +bb10[rubyRegionId=3, firstDead=-1](: T.class_of(ControlFlow), $42: Sorbet::Private::Static::Void, $43: T.class_of(ControlFlow)): # outerLoops: 1 -> (NilClass ? bb13 : bb11) # backedges # - bb10(rubyRegionId=3) -bb11[rubyRegionId=0, firstDead=-1]($44: Sorbet::Private::Static::Void, $45: T.class_of(ControlFlow)): - $40: Sorbet::Private::Static::Void = Solve<$44, sig> - : T.class_of(ControlFlow) = $45 - $63: T.class_of(Sorbet::Private::Static) = alias - $65: Sorbet::Private::Static::Void = $63: T.class_of(Sorbet::Private::Static).sig(: T.class_of(ControlFlow)) - $66: T.class_of(ControlFlow) = +bb11[rubyRegionId=0, firstDead=-1]($42: Sorbet::Private::Static::Void, $43: T.class_of(ControlFlow)): + $38: Sorbet::Private::Static::Void = Solve<$42, sig> + : T.class_of(ControlFlow) = $43 + $60: T.class_of(Sorbet::Private::Static) = alias + $62: Sorbet::Private::Static::Void = $60: T.class_of(Sorbet::Private::Static).sig(: T.class_of(ControlFlow)) + $63: T.class_of(ControlFlow) = -> bb14 # backedges # - bb10(rubyRegionId=3) -bb13[rubyRegionId=3, firstDead=10](: T.class_of(ControlFlow), $44: Sorbet::Private::Static::Void, $45: T.class_of(ControlFlow)): +bb13[rubyRegionId=3, firstDead=10](: T.class_of(ControlFlow), $42: Sorbet::Private::Static::Void, $43: T.class_of(ControlFlow)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $50: Symbol(:a) = :a - $53: T.class_of(T) = alias - $55: T.class_of(Integer) = alias - $57: T.class_of(NilClass) = alias - $51: Runtime object representing type: T.nilable(Integer) = $53: T.class_of(T).any($55: T.class_of(Integer), $57: T.class_of(NilClass)) - $48: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($50: Symbol(:a), $51: Runtime object representing type: T.nilable(Integer)) - $59: T.class_of(Integer) = alias - $47: T::Private::Methods::DeclBuilder = $48: T::Private::Methods::DeclBuilder.returns($59: T.class_of(Integer)) - $60: T.noreturn = blockreturn $47: T::Private::Methods::DeclBuilder + $47: Symbol(:a) = :a + $50: T.class_of(T) = alias + $52: T.class_of(Integer) = alias + $54: T.class_of(NilClass) = alias + $48: Runtime object representing type: T.nilable(Integer) = $50: T.class_of(T).any($52: T.class_of(Integer), $54: T.class_of(NilClass)) + $45: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($47: Symbol(:a), $48: Runtime object representing type: T.nilable(Integer)) + $56: T.class_of(Integer) = alias + $44: T::Private::Methods::DeclBuilder = $45: T::Private::Methods::DeclBuilder.returns($56: T.class_of(Integer)) + $57: T.noreturn = blockreturn $44: T::Private::Methods::DeclBuilder -> bb10 # backedges # - bb11(rubyRegionId=0) # - bb17(rubyRegionId=4) -bb14[rubyRegionId=4, firstDead=-1](: T.class_of(ControlFlow), $65: Sorbet::Private::Static::Void, $66: T.class_of(ControlFlow)): +bb14[rubyRegionId=4, firstDead=-1](: T.class_of(ControlFlow), $62: Sorbet::Private::Static::Void, $63: T.class_of(ControlFlow)): # outerLoops: 1 -> (NilClass ? bb17 : bb15) # backedges # - bb14(rubyRegionId=4) -bb15[rubyRegionId=0, firstDead=-1]($65: Sorbet::Private::Static::Void, $66: T.class_of(ControlFlow)): - $61: Sorbet::Private::Static::Void = Solve<$65, sig> - : T.class_of(ControlFlow) = $66 - $84: T.class_of(Sorbet::Private::Static) = alias - $86: Sorbet::Private::Static::Void = $84: T.class_of(Sorbet::Private::Static).sig(: T.class_of(ControlFlow)) - $87: T.class_of(ControlFlow) = +bb15[rubyRegionId=0, firstDead=-1]($62: Sorbet::Private::Static::Void, $63: T.class_of(ControlFlow)): + $58: Sorbet::Private::Static::Void = Solve<$62, sig> + : T.class_of(ControlFlow) = $63 + $80: T.class_of(Sorbet::Private::Static) = alias + $82: Sorbet::Private::Static::Void = $80: T.class_of(Sorbet::Private::Static).sig(: T.class_of(ControlFlow)) + $83: T.class_of(ControlFlow) = -> bb18 # backedges # - bb14(rubyRegionId=4) -bb17[rubyRegionId=4, firstDead=10](: T.class_of(ControlFlow), $65: Sorbet::Private::Static::Void, $66: T.class_of(ControlFlow)): +bb17[rubyRegionId=4, firstDead=10](: T.class_of(ControlFlow), $62: Sorbet::Private::Static::Void, $63: T.class_of(ControlFlow)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $71: Symbol(:a) = :a - $74: T.class_of(T) = alias + $67: Symbol(:a) = :a + $70: T.class_of(T) = alias + $72: T.class_of(Integer) = alias + $74: T.class_of(NilClass) = alias + $68: Runtime object representing type: T.nilable(Integer) = $70: T.class_of(T).any($72: T.class_of(Integer), $74: T.class_of(NilClass)) + $65: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($67: Symbol(:a), $68: Runtime object representing type: T.nilable(Integer)) $76: T.class_of(Integer) = alias - $78: T.class_of(NilClass) = alias - $72: Runtime object representing type: T.nilable(Integer) = $74: T.class_of(T).any($76: T.class_of(Integer), $78: T.class_of(NilClass)) - $69: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($71: Symbol(:a), $72: Runtime object representing type: T.nilable(Integer)) - $80: T.class_of(Integer) = alias - $68: T::Private::Methods::DeclBuilder = $69: T::Private::Methods::DeclBuilder.returns($80: T.class_of(Integer)) - $81: T.noreturn = blockreturn $68: T::Private::Methods::DeclBuilder + $64: T::Private::Methods::DeclBuilder = $65: T::Private::Methods::DeclBuilder.returns($76: T.class_of(Integer)) + $77: T.noreturn = blockreturn $64: T::Private::Methods::DeclBuilder -> bb14 # backedges # - bb15(rubyRegionId=0) # - bb21(rubyRegionId=5) -bb18[rubyRegionId=5, firstDead=-1](: T.class_of(ControlFlow), $86: Sorbet::Private::Static::Void, $87: T.class_of(ControlFlow)): +bb18[rubyRegionId=5, firstDead=-1](: T.class_of(ControlFlow), $82: Sorbet::Private::Static::Void, $83: T.class_of(ControlFlow)): # outerLoops: 1 -> (NilClass ? bb21 : bb19) # backedges # - bb18(rubyRegionId=5) -bb19[rubyRegionId=0, firstDead=-1]($86: Sorbet::Private::Static::Void, $87: T.class_of(ControlFlow)): - $82: Sorbet::Private::Static::Void = Solve<$86, sig> - : T.class_of(ControlFlow) = $87 - $105: T.class_of(Sorbet::Private::Static) = alias - $107: Sorbet::Private::Static::Void = $105: T.class_of(Sorbet::Private::Static).sig(: T.class_of(ControlFlow)) - $108: T.class_of(ControlFlow) = +bb19[rubyRegionId=0, firstDead=-1]($82: Sorbet::Private::Static::Void, $83: T.class_of(ControlFlow)): + $78: Sorbet::Private::Static::Void = Solve<$82, sig> + : T.class_of(ControlFlow) = $83 + $100: T.class_of(Sorbet::Private::Static) = alias + $102: Sorbet::Private::Static::Void = $100: T.class_of(Sorbet::Private::Static).sig(: T.class_of(ControlFlow)) + $103: T.class_of(ControlFlow) = -> bb22 # backedges # - bb18(rubyRegionId=5) -bb21[rubyRegionId=5, firstDead=10](: T.class_of(ControlFlow), $86: Sorbet::Private::Static::Void, $87: T.class_of(ControlFlow)): +bb21[rubyRegionId=5, firstDead=10](: T.class_of(ControlFlow), $82: Sorbet::Private::Static::Void, $83: T.class_of(ControlFlow)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $92: Symbol(:a) = :a - $95: T.class_of(T) = alias - $97: T.class_of(Integer) = alias - $99: T.class_of(NilClass) = alias - $93: Runtime object representing type: T.nilable(Integer) = $95: T.class_of(T).any($97: T.class_of(Integer), $99: T.class_of(NilClass)) - $90: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($92: Symbol(:a), $93: Runtime object representing type: T.nilable(Integer)) - $101: T.class_of(Integer) = alias - $89: T::Private::Methods::DeclBuilder = $90: T::Private::Methods::DeclBuilder.returns($101: T.class_of(Integer)) - $102: T.noreturn = blockreturn $89: T::Private::Methods::DeclBuilder + $87: Symbol(:a) = :a + $90: T.class_of(T) = alias + $92: T.class_of(Integer) = alias + $94: T.class_of(NilClass) = alias + $88: Runtime object representing type: T.nilable(Integer) = $90: T.class_of(T).any($92: T.class_of(Integer), $94: T.class_of(NilClass)) + $85: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($87: Symbol(:a), $88: Runtime object representing type: T.nilable(Integer)) + $96: T.class_of(Integer) = alias + $84: T::Private::Methods::DeclBuilder = $85: T::Private::Methods::DeclBuilder.returns($96: T.class_of(Integer)) + $97: T.noreturn = blockreturn $84: T::Private::Methods::DeclBuilder -> bb18 # backedges # - bb19(rubyRegionId=0) # - bb25(rubyRegionId=6) -bb22[rubyRegionId=6, firstDead=-1](: T.class_of(ControlFlow), $107: Sorbet::Private::Static::Void, $108: T.class_of(ControlFlow)): +bb22[rubyRegionId=6, firstDead=-1](: T.class_of(ControlFlow), $102: Sorbet::Private::Static::Void, $103: T.class_of(ControlFlow)): # outerLoops: 1 -> (NilClass ? bb25 : bb23) # backedges # - bb22(rubyRegionId=6) -bb23[rubyRegionId=0, firstDead=-1]($107: Sorbet::Private::Static::Void, $108: T.class_of(ControlFlow)): - $103: Sorbet::Private::Static::Void = Solve<$107, sig> - : T.class_of(ControlFlow) = $108 - $126: T.class_of(Sorbet::Private::Static) = alias - $128: Sorbet::Private::Static::Void = $126: T.class_of(Sorbet::Private::Static).sig(: T.class_of(ControlFlow)) - $129: T.class_of(ControlFlow) = +bb23[rubyRegionId=0, firstDead=-1]($102: Sorbet::Private::Static::Void, $103: T.class_of(ControlFlow)): + $98: Sorbet::Private::Static::Void = Solve<$102, sig> + : T.class_of(ControlFlow) = $103 + $120: T.class_of(Sorbet::Private::Static) = alias + $122: Sorbet::Private::Static::Void = $120: T.class_of(Sorbet::Private::Static).sig(: T.class_of(ControlFlow)) + $123: T.class_of(ControlFlow) = -> bb26 # backedges # - bb22(rubyRegionId=6) -bb25[rubyRegionId=6, firstDead=10](: T.class_of(ControlFlow), $107: Sorbet::Private::Static::Void, $108: T.class_of(ControlFlow)): +bb25[rubyRegionId=6, firstDead=10](: T.class_of(ControlFlow), $102: Sorbet::Private::Static::Void, $103: T.class_of(ControlFlow)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $113: Symbol(:a) = :a - $116: T.class_of(T) = alias - $118: T.class_of(Integer) = alias - $120: T.class_of(NilClass) = alias - $114: Runtime object representing type: T.nilable(Integer) = $116: T.class_of(T).any($118: T.class_of(Integer), $120: T.class_of(NilClass)) - $111: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($113: Symbol(:a), $114: Runtime object representing type: T.nilable(Integer)) - $122: T.class_of(Integer) = alias - $110: T::Private::Methods::DeclBuilder = $111: T::Private::Methods::DeclBuilder.returns($122: T.class_of(Integer)) - $123: T.noreturn = blockreturn $110: T::Private::Methods::DeclBuilder + $107: Symbol(:a) = :a + $110: T.class_of(T) = alias + $112: T.class_of(Integer) = alias + $114: T.class_of(NilClass) = alias + $108: Runtime object representing type: T.nilable(Integer) = $110: T.class_of(T).any($112: T.class_of(Integer), $114: T.class_of(NilClass)) + $105: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($107: Symbol(:a), $108: Runtime object representing type: T.nilable(Integer)) + $116: T.class_of(Integer) = alias + $104: T::Private::Methods::DeclBuilder = $105: T::Private::Methods::DeclBuilder.returns($116: T.class_of(Integer)) + $117: T.noreturn = blockreturn $104: T::Private::Methods::DeclBuilder -> bb22 # backedges # - bb23(rubyRegionId=0) # - bb29(rubyRegionId=7) -bb26[rubyRegionId=7, firstDead=-1](: T.class_of(ControlFlow), $128: Sorbet::Private::Static::Void, $129: T.class_of(ControlFlow)): +bb26[rubyRegionId=7, firstDead=-1](: T.class_of(ControlFlow), $122: Sorbet::Private::Static::Void, $123: T.class_of(ControlFlow)): # outerLoops: 1 -> (NilClass ? bb29 : bb27) # backedges # - bb26(rubyRegionId=7) -bb27[rubyRegionId=0, firstDead=-1]($128: Sorbet::Private::Static::Void, $129: T.class_of(ControlFlow)): - $124: Sorbet::Private::Static::Void = Solve<$128, sig> - : T.class_of(ControlFlow) = $129 - $147: T.class_of(Sorbet::Private::Static) = alias - $149: Sorbet::Private::Static::Void = $147: T.class_of(Sorbet::Private::Static).sig(: T.class_of(ControlFlow)) - $150: T.class_of(ControlFlow) = +bb27[rubyRegionId=0, firstDead=-1]($122: Sorbet::Private::Static::Void, $123: T.class_of(ControlFlow)): + $118: Sorbet::Private::Static::Void = Solve<$122, sig> + : T.class_of(ControlFlow) = $123 + $140: T.class_of(Sorbet::Private::Static) = alias + $142: Sorbet::Private::Static::Void = $140: T.class_of(Sorbet::Private::Static).sig(: T.class_of(ControlFlow)) + $143: T.class_of(ControlFlow) = -> bb30 # backedges # - bb26(rubyRegionId=7) -bb29[rubyRegionId=7, firstDead=10](: T.class_of(ControlFlow), $128: Sorbet::Private::Static::Void, $129: T.class_of(ControlFlow)): +bb29[rubyRegionId=7, firstDead=10](: T.class_of(ControlFlow), $122: Sorbet::Private::Static::Void, $123: T.class_of(ControlFlow)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $134: Symbol(:a) = :a - $137: T.class_of(T) = alias - $139: T.class_of(Integer) = alias - $141: T.class_of(NilClass) = alias - $135: Runtime object representing type: T.nilable(Integer) = $137: T.class_of(T).any($139: T.class_of(Integer), $141: T.class_of(NilClass)) - $132: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($134: Symbol(:a), $135: Runtime object representing type: T.nilable(Integer)) - $143: T.class_of(Integer) = alias - $131: T::Private::Methods::DeclBuilder = $132: T::Private::Methods::DeclBuilder.returns($143: T.class_of(Integer)) - $144: T.noreturn = blockreturn $131: T::Private::Methods::DeclBuilder + $127: Symbol(:a) = :a + $130: T.class_of(T) = alias + $132: T.class_of(Integer) = alias + $134: T.class_of(NilClass) = alias + $128: Runtime object representing type: T.nilable(Integer) = $130: T.class_of(T).any($132: T.class_of(Integer), $134: T.class_of(NilClass)) + $125: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($127: Symbol(:a), $128: Runtime object representing type: T.nilable(Integer)) + $136: T.class_of(Integer) = alias + $124: T::Private::Methods::DeclBuilder = $125: T::Private::Methods::DeclBuilder.returns($136: T.class_of(Integer)) + $137: T.noreturn = blockreturn $124: T::Private::Methods::DeclBuilder -> bb26 # backedges # - bb27(rubyRegionId=0) # - bb33(rubyRegionId=8) -bb30[rubyRegionId=8, firstDead=-1](: T.class_of(ControlFlow), $149: Sorbet::Private::Static::Void, $150: T.class_of(ControlFlow)): +bb30[rubyRegionId=8, firstDead=-1](: T.class_of(ControlFlow), $142: Sorbet::Private::Static::Void, $143: T.class_of(ControlFlow)): # outerLoops: 1 -> (NilClass ? bb33 : bb31) # backedges # - bb30(rubyRegionId=8) -bb31[rubyRegionId=0, firstDead=-1]($149: Sorbet::Private::Static::Void, $150: T.class_of(ControlFlow)): - $145: Sorbet::Private::Static::Void = Solve<$149, sig> - : T.class_of(ControlFlow) = $150 - $168: T.class_of(Sorbet::Private::Static) = alias - $170: Sorbet::Private::Static::Void = $168: T.class_of(Sorbet::Private::Static).sig(: T.class_of(ControlFlow)) - $171: T.class_of(ControlFlow) = +bb31[rubyRegionId=0, firstDead=-1]($142: Sorbet::Private::Static::Void, $143: T.class_of(ControlFlow)): + $138: Sorbet::Private::Static::Void = Solve<$142, sig> + : T.class_of(ControlFlow) = $143 + $160: T.class_of(Sorbet::Private::Static) = alias + $162: Sorbet::Private::Static::Void = $160: T.class_of(Sorbet::Private::Static).sig(: T.class_of(ControlFlow)) + $163: T.class_of(ControlFlow) = -> bb34 # backedges # - bb30(rubyRegionId=8) -bb33[rubyRegionId=8, firstDead=10](: T.class_of(ControlFlow), $149: Sorbet::Private::Static::Void, $150: T.class_of(ControlFlow)): +bb33[rubyRegionId=8, firstDead=10](: T.class_of(ControlFlow), $142: Sorbet::Private::Static::Void, $143: T.class_of(ControlFlow)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $155: Symbol(:a) = :a - $158: T.class_of(T) = alias - $160: T.class_of(Integer) = alias - $162: T.class_of(NilClass) = alias - $156: Runtime object representing type: T.nilable(Integer) = $158: T.class_of(T).any($160: T.class_of(Integer), $162: T.class_of(NilClass)) - $153: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($155: Symbol(:a), $156: Runtime object representing type: T.nilable(Integer)) - $164: T.class_of(Integer) = alias - $152: T::Private::Methods::DeclBuilder = $153: T::Private::Methods::DeclBuilder.returns($164: T.class_of(Integer)) - $165: T.noreturn = blockreturn $152: T::Private::Methods::DeclBuilder + $147: Symbol(:a) = :a + $150: T.class_of(T) = alias + $152: T.class_of(Integer) = alias + $154: T.class_of(NilClass) = alias + $148: Runtime object representing type: T.nilable(Integer) = $150: T.class_of(T).any($152: T.class_of(Integer), $154: T.class_of(NilClass)) + $145: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($147: Symbol(:a), $148: Runtime object representing type: T.nilable(Integer)) + $156: T.class_of(Integer) = alias + $144: T::Private::Methods::DeclBuilder = $145: T::Private::Methods::DeclBuilder.returns($156: T.class_of(Integer)) + $157: T.noreturn = blockreturn $144: T::Private::Methods::DeclBuilder -> bb30 # backedges # - bb31(rubyRegionId=0) # - bb37(rubyRegionId=9) -bb34[rubyRegionId=9, firstDead=-1](: T.class_of(ControlFlow), $170: Sorbet::Private::Static::Void, $171: T.class_of(ControlFlow)): +bb34[rubyRegionId=9, firstDead=-1](: T.class_of(ControlFlow), $162: Sorbet::Private::Static::Void, $163: T.class_of(ControlFlow)): # outerLoops: 1 -> (NilClass ? bb37 : bb35) # backedges # - bb34(rubyRegionId=9) -bb35[rubyRegionId=0, firstDead=6]($170: Sorbet::Private::Static::Void, $171: T.class_of(ControlFlow)): - $166: Sorbet::Private::Static::Void = Solve<$170, sig> - : T.class_of(ControlFlow) = $171 - $190: T.class_of(T::Sig) = alias - $192: T.class_of(T) = alias - $187: T.class_of(ControlFlow) = : T.class_of(ControlFlow).extend($190: T.class_of(T::Sig)) +bb35[rubyRegionId=0, firstDead=6]($162: Sorbet::Private::Static::Void, $163: T.class_of(ControlFlow)): + $158: Sorbet::Private::Static::Void = Solve<$162, sig> + : T.class_of(ControlFlow) = $163 + $181: T.class_of(T::Sig) = alias + $183: T.class_of(T) = alias + $178: T.class_of(ControlFlow) = : T.class_of(ControlFlow).extend($181: T.class_of(T::Sig)) : T.noreturn = return $2: NilClass -> bb1 # backedges # - bb34(rubyRegionId=9) -bb37[rubyRegionId=9, firstDead=10](: T.class_of(ControlFlow), $170: Sorbet::Private::Static::Void, $171: T.class_of(ControlFlow)): +bb37[rubyRegionId=9, firstDead=10](: T.class_of(ControlFlow), $162: Sorbet::Private::Static::Void, $163: T.class_of(ControlFlow)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $176: Symbol(:a) = :a - $179: T.class_of(T) = alias - $181: T.class_of(Integer) = alias - $183: T.class_of(NilClass) = alias - $177: Runtime object representing type: T.nilable(Integer) = $179: T.class_of(T).any($181: T.class_of(Integer), $183: T.class_of(NilClass)) - $174: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($176: Symbol(:a), $177: Runtime object representing type: T.nilable(Integer)) - $185: T.class_of(Integer) = alias - $173: T::Private::Methods::DeclBuilder = $174: T::Private::Methods::DeclBuilder.returns($185: T.class_of(Integer)) - $186: T.noreturn = blockreturn $173: T::Private::Methods::DeclBuilder + $167: Symbol(:a) = :a + $170: T.class_of(T) = alias + $172: T.class_of(Integer) = alias + $174: T.class_of(NilClass) = alias + $168: Runtime object representing type: T.nilable(Integer) = $170: T.class_of(T).any($172: T.class_of(Integer), $174: T.class_of(NilClass)) + $165: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($167: Symbol(:a), $168: Runtime object representing type: T.nilable(Integer)) + $176: T.class_of(Integer) = alias + $164: T::Private::Methods::DeclBuilder = $165: T::Private::Methods::DeclBuilder.returns($176: T.class_of(Integer)) + $177: T.noreturn = blockreturn $164: T::Private::Methods::DeclBuilder -> bb34 } diff --git a/test/testdata/infer/dropsubtypesof.rb b/test/testdata/infer/dropsubtypesof.rb index 46709374fc..db9ebfe8a8 100644 --- a/test/testdata/infer/dropsubtypesof.rb +++ b/test/testdata/infer/dropsubtypesof.rb @@ -21,7 +21,7 @@ def foo(x) puts "got nil" else y = T.let(x, T.any(T::Array[Integer], T::Array[String])) - T.reveal_type(y) # error: Revealed type: `T::Array[T.any(String, Integer)]` + T.reveal_type(y) # error: Revealed type: `T::Array[T.any(Integer, String)]` end end @@ -32,7 +32,7 @@ def bar(x) puts "got nil" else y = T.let(x, T.any(T::Array[Integer], T::Array[String])) - T.reveal_type(y) # error: Revealed type: `T::Array[T.any(String, Integer)]` + T.reveal_type(y) # error: Revealed type: `T::Array[T.any(Integer, String)]` end end @@ -43,7 +43,7 @@ def qux(x) puts "got nil" else y = T.let(x, T.any(T::Array[Integer], T::Array[String])) - T.reveal_type(y) # error: Revealed type: `T::Array[T.any(String, Integer)]` + T.reveal_type(y) # error: Revealed type: `T::Array[T.any(Integer, String)]` end end diff --git a/test/testdata/infer/eqeq.rb b/test/testdata/infer/eqeq.rb new file mode 100644 index 0000000000..4ea5806b21 --- /dev/null +++ b/test/testdata/infer/eqeq.rb @@ -0,0 +1,129 @@ +# typed: true +extend T::Sig + +class ConvertsToInteger + def ==(other) + other.is_a?(ConvertsToInteger) || other == 0 + end +end + +class MyEnum < T::Enum + enums do + X = new + Y = new + end +end + +class A < T::Struct + const :sym, Symbol + const :str, String + const :maybe_sym, T.nilable(Symbol) + const :maybe_str, T.nilable(String) + const :str_or_sym, T.any(String, Symbol) + const :int_or_sym, T.any(Integer, Symbol) + const :str_or_int, T.any(String, Integer) + const :converts_to_integer, ConvertsToInteger + const :my_enum, MyEnum +end + +sig do + params(x: A).void +end +def example1(x) + if x.sym == x.str + # ^^ error: Comparison between `Symbol` and `String` is always false + p(x) + end + + if x.str == x.sym + # ^^ error: Comparison between `String` and `Symbol` is always false + p(x) + end + + if x.sym == x.maybe_str + # ^^ error: Comparison between `Symbol` and `T.nilable(String)` is always false + p(x) + end + + if x.str == x.maybe_sym + # ^^ error: Comparison between `String` and `T.nilable(Symbol)` is always false + p(x) + end + + if x.maybe_sym == x.str + # ^^ error: Comparison between `T.nilable(Symbol)` and `String` is always false + p(x) + end + + if x.maybe_str == x.sym + # ^^ error: Comparison between `T.nilable(String)` and `Symbol` is always false + p(x) + end + + + if x.maybe_sym == x.maybe_str + p(x) + end + + if x.str_or_sym == x.str + p(x) + end + + if x.str_or_sym == x.sym + p(x) + end + + if x.sym == x.str_or_sym + p(x) + end + + if x.str == x.str_or_sym + p(x) + end + + if x.str_or_sym == x.str_or_sym + p(x) + end + + if x.int_or_sym == x.str_or_sym + p(x) + end + + if x.str_or_int == x.maybe_sym + p(x) + end + + if x.str_or_int == x.str_or_sym + p(x) + end + + if x.int_or_sym == x.maybe_str + p(x) + end + + if x.maybe_sym == x.str_or_int + # ^^ error: Comparison between `T.nilable(Symbol)` and `T.any(String, Integer)` is always false + p(x) + end + + if x.int_or_sym == x.converts_to_integer + p(x) + end + + if x.sym == x.my_enum + # ^^ error: Comparison between `Symbol` and `MyEnum` is always false + p(x) + end + + if x.maybe_sym == x.my_enum + # ^^ error: Comparison between `T.nilable(Symbol)` and `MyEnum` is always false + p(x) + end + + if x.int_or_sym == x.my_enum + p(x) + end +end + + + diff --git a/test/testdata/infer/eqeq.rb.autocorrects.exp b/test/testdata/infer/eqeq.rb.autocorrects.exp new file mode 100644 index 0000000000..f458c1c2bd --- /dev/null +++ b/test/testdata/infer/eqeq.rb.autocorrects.exp @@ -0,0 +1,131 @@ +# -- test/testdata/infer/eqeq.rb -- +# typed: true +extend T::Sig + +class ConvertsToInteger + def ==(other) + other.is_a?(ConvertsToInteger) || other == 0 + end +end + +class MyEnum < T::Enum + enums do + X = new + Y = new + end +end + +class A < T::Struct + const :sym, Symbol + const :str, String + const :maybe_sym, T.nilable(Symbol) + const :maybe_str, T.nilable(String) + const :str_or_sym, T.any(String, Symbol) + const :int_or_sym, T.any(Integer, Symbol) + const :str_or_int, T.any(String, Integer) + const :converts_to_integer, ConvertsToInteger + const :my_enum, MyEnum +end + +sig do + params(x: A).void +end +def example1(x) + if x.sym == x.str.to_sym + # ^^ error: Comparison between `Symbol` and `String` is always false + p(x) + end + + if x.str.to_sym == x.sym + # ^^ error: Comparison between `String` and `Symbol` is always false + p(x) + end + + if x.sym == x.maybe_str + # ^^ error: Comparison between `Symbol` and `T.nilable(String)` is always false + p(x) + end + + if x.str == x.maybe_sym + # ^^ error: Comparison between `String` and `T.nilable(Symbol)` is always false + p(x) + end + + if x.maybe_sym == x.str.to_sym + # ^^ error: Comparison between `T.nilable(Symbol)` and `String` is always false + p(x) + end + + if x.maybe_str.to_sym == x.sym + # ^^ error: Comparison between `T.nilable(String)` and `Symbol` is always false + p(x) + end + + + if x.maybe_sym == x.maybe_str + p(x) + end + + if x.str_or_sym == x.str + p(x) + end + + if x.str_or_sym == x.sym + p(x) + end + + if x.sym == x.str_or_sym + p(x) + end + + if x.str == x.str_or_sym + p(x) + end + + if x.str_or_sym == x.str_or_sym + p(x) + end + + if x.int_or_sym == x.str_or_sym + p(x) + end + + if x.str_or_int == x.maybe_sym + p(x) + end + + if x.str_or_int == x.str_or_sym + p(x) + end + + if x.int_or_sym == x.maybe_str + p(x) + end + + if x.maybe_sym == x.str_or_int + # ^^ error: Comparison between `T.nilable(Symbol)` and `T.any(String, Integer)` is always false + p(x) + end + + if x.int_or_sym == x.converts_to_integer + p(x) + end + + if x.sym == x.my_enum + # ^^ error: Comparison between `Symbol` and `MyEnum` is always false + p(x) + end + + if x.maybe_sym == x.my_enum + # ^^ error: Comparison between `T.nilable(Symbol)` and `MyEnum` is always false + p(x) + end + + if x.int_or_sym == x.my_enum + p(x) + end +end + + + +# ------------------------------ diff --git a/test/testdata/infer/generics/align_base_type_args.rb b/test/testdata/infer/generics/align_base_type_args.rb new file mode 100644 index 0000000000..cb006fb67a --- /dev/null +++ b/test/testdata/infer/generics/align_base_type_args.rb @@ -0,0 +1,31 @@ +# typed: true +extend T::Sig + +module A + extend T::Generic + X = type_member +end + +module B + extend T::Generic + Y = type_member +end + +class AB + extend T::Generic + + include A + include B + X = type_member + Y = type_member +end + +sig {params(ab: T.all(AB[T.untyped, T.untyped], A[Integer], B[String])).void} +def example1(ab) + T.reveal_type(ab) # error: `AB[Integer, String]` +end + +sig {params(ba: T.all(AB[T.untyped, T.untyped], B[Float], A[Symbol])).void} +def example2(ba) + T.reveal_type(ba) # error: `AB[Symbol, Float]` +end diff --git a/test/testdata/infer/generics/apply_f.rb b/test/testdata/infer/generics/apply_f.rb index 050d7957b8..3aa7c187d3 100644 --- a/test/testdata/infer/generics/apply_f.rb +++ b/test/testdata/infer/generics/apply_f.rb @@ -50,9 +50,9 @@ def takes_any_int_string(y) end T.reveal_type(x) # error: `T.any(Integer, String)` - x = apply_f_int(y) do |x| # error: Could not find valid instantiation of type parameters for `Object#apply_f_int` + x = apply_f_int(y) do |x| # ^ error: Expected `T.all(Integer, T.type_parameter(:U))` but found `T.any(Integer, String)` for argument `x` T.reveal_type(x) # error: `Integer` end - T.reveal_type(x) # error: `T.untyped` + T.reveal_type(x) # error: `Integer` end diff --git a/test/testdata/infer/generics/arity_mismatch.rb b/test/testdata/infer/generics/arity_mismatch.rb index 702e280cbe..b25a9a907b 100644 --- a/test/testdata/infer/generics/arity_mismatch.rb +++ b/test/testdata/infer/generics/arity_mismatch.rb @@ -1,4 +1,6 @@ # typed: true +extend T::Sig + class Generic extend T::Generic @@ -6,12 +8,46 @@ class Generic T2 = type_member end -def use_it +def example0 g1 = Generic[Integer].new # ^^^^^^^ error: Wrong number of type parameters T.assert_type!(g1, Generic[Integer, T.untyped]) +end + +class NotABox + extend T::Generic +end + +sig {params(x: NotABox[Integer]).void} +# ^^^^^^^ error: `NotABox` is not a generic class, but was given type parameters +# ^^^^^^^ error: `NotABox` is not a generic class, but was given type parameters +def example1(x) + T.reveal_type(x) # error: `NotABox[Runtime object representing type: Integer] (unresolved) +end + +sig {params(x: NotABox[]).void} +# ^^ error: `NotABox` is not a generic class, but was given type parameters +# ^^ error: `NotABox` is not a generic class, but was given type parameters +def example2(x) + T.reveal_type(x) # error: Revealed type: `NotABox[] (unresolved)` +end + +class Box + extend T::Generic + + Elem = type_member +end + +sig {params(x: Box[Integer, String]).void} +# ^^^^^^^^^^^^^^^ error: Wrong number of type parameters for `Box`. Expected: `1`, got: `2` +# ^^^^^^^^^^^^^^^ error: Wrong number of type parameters for `Box`. Expected: `1`, got: `2` +def example3(x) + T.reveal_type(x) # error: Revealed type: `Box[Integer]` +end - g2 = Generic[Integer, String, Object].new - # ^^^^^^^^^^^^^^^^^^^^^^^ error: Wrong number of type parameters - T.assert_type!(g2, Generic[Integer, String]) +sig {params(x: Box[]).void} +# ^^ error: Wrong number of type parameters for `Box`. Expected: `1`, got: `0` +# ^^ error: Wrong number of type parameters for `Box`. Expected: `1`, got: `0` +def example4(x) + T.reveal_type(x) # error: Revealed type: `Box[T.untyped]` end diff --git a/test/testdata/infer/generics/attached_class_private.rb b/test/testdata/infer/generics/attached_class_private.rb new file mode 100644 index 0000000000..6a4a4fe456 --- /dev/null +++ b/test/testdata/infer/generics/attached_class_private.rb @@ -0,0 +1,73 @@ +# typed: true + +class Parent + extend T::Sig + + sig {returns(T.attached_class)} + def self.make + new + end + + sig {params(x: T.attached_class).void} # A bad type definition for `x` + private_class_method def self.consume(x) + puts "consumed" + end + + def self.example_parent + self.consume(Parent.new) + # ^^^^^^^^^^ error: Expected `T.attached_class (of Parent)` but found `Parent` for argument `x` + self.consume(Parent.make) + # ^^^^^^^^^^^ error: Expected `T.attached_class (of Parent)` but found `Parent` for argument `x` + self.consume(Child.new) + # ^^^^^^^^^ error: Expected `T.attached_class (of Parent)` but found `Child` for argument `x` + self.consume(Child.make) + # ^^^^^^^^^^ error: Expected `T.attached_class (of Parent)` but found `Child` for argument `x` + self.consume(self.new) + self.consume(self.make) + end +end + +class Child < Parent + extend T::Sig + + sig {void} + def say_hi + puts "hi" + end + + sig {params(x: T.attached_class).void} # A bad type definition for `x` + private_class_method def self.consume(x) + x.say_hi + end + + def self.example_child + self.consume(Parent.new) + # ^^^^^^^^^^ error: Expected `T.attached_class (of Child)` but found `Parent` for argument `x` + self.consume(Parent.make) + # ^^^^^^^^^^^ error: Expected `T.attached_class (of Child)` but found `Parent` for argument `x` + self.consume(Child.new) + # ^^^^^^^^^ error: Expected `T.attached_class (of Child)` but found `Child` for argument `x` + self.consume(Child.make) + # ^^^^^^^^^^ error: Expected `T.attached_class (of Child)` but found `Child` for argument `x` + self.consume(self.new) + self.consume(self.make) + end +end + +Parent.consume(Parent.new) # error: Non-private call to private method `consume` on `T.class_of(Parent)` +Child.consume(Parent.new) # error: Non-private call to private method `consume` on `T.class_of(Child)` +# ^^^^^^^^^^ error: Expected `Child` but found `Parent` for argument `x` +Parent.example_parent +Child.example_child + +class A + extend T::Sig + + sig {params(cls: T.class_of(Parent)).void} + def self.consume_parent(cls) + cls.consume(Parent.make) # error: Non-private call to private method `consume` on `T.class_of(Parent)` + end +end + +A.consume_parent(Parent) +A.consume_parent(Child) diff --git a/test/testdata/infer/generics/bare_class_generic.rb b/test/testdata/infer/generics/bare_class_generic.rb new file mode 100644 index 0000000000..a553a22961 --- /dev/null +++ b/test/testdata/infer/generics/bare_class_generic.rb @@ -0,0 +1,10 @@ +# typed: true +extend T::Sig + +sig {params(klass: Class).void} +def example(klass) + T.reveal_type(klass) # error: `T::Class[T.anything]` + instance = klass.new + T.reveal_type(instance) # error: `T.anything` + instance.foo # error: Method `foo` does not exist on `T.anything` +end diff --git a/test/testdata/infer/generics/bounds_super.rb b/test/testdata/infer/generics/bounds_super.rb index 6e6c889e12..9b0f0a1786 100644 --- a/test/testdata/infer/generics/bounds_super.rb +++ b/test/testdata/infer/generics/bounds_super.rb @@ -40,6 +40,6 @@ class C2 < A class D1 < A T1 = type_member # ^^^^^^^^^^^ error: The `lower` type bound `T.noreturn` must be a supertype of the parent's `lower` type bound `Serval` for type_member `T1` - # ^^^^^^^^^^^ error: The `upper` type bound `` must be a subtype of the parent's `upper` type bound `Animal` for type_member `T1` + # ^^^^^^^^^^^ error: The `upper` type bound `T.anything` must be a subtype of the parent's `upper` type bound `Animal` for type_member `T1` end diff --git a/test/testdata/infer/generics/box_class_of.rb b/test/testdata/infer/generics/box_class_of.rb new file mode 100644 index 0000000000..7f2de6f0ae --- /dev/null +++ b/test/testdata/infer/generics/box_class_of.rb @@ -0,0 +1,71 @@ +# typed: true +extend T::Sig + +class Box + extend T::Sig + extend T::Generic + Elem = type_member(:out) + + sig {params(val: Elem).void} + def initialize(val) + @val = val + end +end + +class BoxA < Box + Elem = type_member(:out) {{upper: A}} +end + +sig {params(klass: T.class_of(Box)).void} +def example1(klass) + T.reveal_type(klass) # error: `T.class_of(Box)[Box[T.anything]]` + instance = klass.new(0) + T.reveal_type(instance) # error: `Box[T.anything]` + + T.reveal_type(klass[Integer]) # error: `Runtime object representing type: Box[Integer]` + instance = klass[Integer].new(0) + T.reveal_type(instance) # error: `Box[Integer]` + + klass[Integer].new('') + # ^^ error: Expected `Integer` but found `String("")` for argument `val` +end + +module M; end +class A; end + +class ChildA < A + include M +end + +class BoxChildA < Box + Elem = type_member(:out) {{upper: ChildA}} +end + +sig {params(klass: T.all(T.class_of(Box), T::Class[Box[T.all(A, M)]])).void} +def example2(klass) + T.reveal_type(klass) # error: `T.class_of(Box)[Box[T.all(A, M)]]` + instance = klass.new(ChildA.new) + T.reveal_type(instance) # error: `Box[T.all(A, M)]` + + klass.new # error: Not enough arguments provided for method `Box#initialize` + + # These two are bugs in our Class_new intrinsic. We dispatch on + # attachedClass->externalType() instead of on the attached class type + # argument. This doesn't happen for the klass[Integer].new calls because the + # [] call creates a MetaType, and then the `new` dispatchCall goes to + # `initialize` on the the MetaType's wrapped type. + klass.new(A.new) + klass.new(0) + + # But I guess this still works, where you can just apply it to another type? + + T.reveal_type(klass[Integer]) # error: `Runtime object representing type: Box[Integer]` + instance = klass[Integer].new # error: Not enough arguments + T.reveal_type(instance) # error: `Box[Integer]` +end + +example2(Box) +# ^^^ error: Expected `T.class_of(Box)[Box[T.all(A, M)]]` but found `T.class_of(Box)` +example2(BoxA) +# ^^^^ error: Expected `T.class_of(Box)[Box[T.all(A, M)]]` but found `T.class_of(BoxA)` +example2(BoxChildA) diff --git a/test/testdata/infer/generics/class_and_class_of.rb b/test/testdata/infer/generics/class_and_class_of.rb new file mode 100644 index 0000000000..b85febff79 --- /dev/null +++ b/test/testdata/infer/generics/class_and_class_of.rb @@ -0,0 +1,68 @@ +# typed: true +class Module; include T::Sig; end + +module MyInterface + extend T::Helpers + + sig {returns(Integer)} + def some_instance_method; 0; end + + module ClassMethods + sig {returns(String)} + def some_class_method; ''; end + end + mixes_in_class_methods(ClassMethods) +end + +class MyClass + include MyInterface +end + +sig {params(x: T.class_of(MyClass)).void} +def example1(x) + x.new.some_instance_method # ok + x.some_class_method # ok +end + +example1(MyClass) # ok + +sig {params(x: T.class_of(MyInterface)).void} +def example2(x) + x.new.some_instance_method # error: `new` does not exist + x.some_class_method # error: `some_class_method` does not exist +end + +example2(MyClass) # error: Expected `T.class_of(MyInterface)` but found `T.class_of(MyClass)` + +sig {params(x: T.all(Class, MyInterface::ClassMethods)).void} +def example3(x) + x.new.some_instance_method # error: `some_instance_method` does not exist + x.some_class_method # ok +end + +example3(MyClass) # ok + +sig {params(x: T.all(T::Class[MyInterface], MyInterface::ClassMethods)).void} +def example4(x) + x.new.some_instance_method # ok + x.some_class_method # ok +end + +example4(MyClass) # ok + +sig {params(x: T::Class[MyClass]).void} +def example5(x) + x.new.some_instance_method # ok + x.some_class_method # error: Method `some_class_method` does not exist on `T::Class[MyClass]` +end + +example5(MyClass) # ok + +sig {params(x: T.all(T::Class[MyClass], T.class_of(MyClass))).void} +def example6(x) + T.reveal_type(x) # error: `T.class_of(MyClass)` + x.new.some_instance_method # ok + x.some_class_method # ok +end + +example6(MyClass) # ok diff --git a/test/testdata/infer/generics/covariant_class.rb b/test/testdata/infer/generics/covariant_class.rb new file mode 100644 index 0000000000..dda7a359af --- /dev/null +++ b/test/testdata/infer/generics/covariant_class.rb @@ -0,0 +1,23 @@ +# typed: true + +class ImmutableBox + extend T::Sig + extend T::Generic + + Elem = type_member(:out) + + sig {params(x: Elem).void} + def initialize(x) + @x = x + end + + sig {returns(Elem)} + def get_x; @x; end + + sig {params(x: Elem).void} + # ^ error: `type_member` `Elem` was defined as `:out` but is used in an `:in` context + def set_x(x); @x = x; end + + sig {params(x: Elem).void} + private def private_set_x(x); @x = x; end +end diff --git a/test/testdata/infer/generics/finder_methods.rb b/test/testdata/infer/generics/finder_methods.rb new file mode 100644 index 0000000000..475cb15aa2 --- /dev/null +++ b/test/testdata/infer/generics/finder_methods.rb @@ -0,0 +1,28 @@ +# typed: true + +module FinderMethods + extend T::Sig + extend T::Generic + abstract! + + has_attached_class! + + sig {abstract.returns(T.attached_class)} + def new; end + + sig {params(id: String).returns(T.attached_class)} + def find(id) + self.new + end +end + +class Charge + extend T::Sig + extend FinderMethods + + sig {returns(Integer)} + def foo; 0; end +end + +x = Charge.find('ch_123') +T.reveal_type(x.foo) # error: `Integer` diff --git a/test/testdata/infer/generics/has_attached_class_errors.rb b/test/testdata/infer/generics/has_attached_class_errors.rb new file mode 100644 index 0000000000..07b2c59ea7 --- /dev/null +++ b/test/testdata/infer/generics/has_attached_class_errors.rb @@ -0,0 +1,22 @@ +# typed: true + +module A + extend T::Generic + + has_attached_class!('') # error: Invalid param, must be a :symbol + # ^^ error: Expected `Symbol` but found `String("")` for argument `variance` + # ^^ error: Expected `Symbol` but found `String("")` for argument `variance` +end + +module B + extend T::Generic + + has_attached_class!(:nope) # error: Invalid variance kind, only `:out` and `:in` are supported +end + +module C + extend T::Generic + + has_attached_class! { {nope: Integer} } + # ^^^^ error: Unknown key `nope` provided in block to `type_member` +end diff --git a/test/testdata/infer/generics/has_attached_class_include.rb b/test/testdata/infer/generics/has_attached_class_include.rb new file mode 100644 index 0000000000..f0d31931ba --- /dev/null +++ b/test/testdata/infer/generics/has_attached_class_include.rb @@ -0,0 +1,20 @@ +# typed: true +# disable-fast-path: true + +module Parent + extend T::Generic + + has_attached_class! +end + +module ChildModule1 # error: `has_attached_class!` declared by parent `Parent` must be re-declared in `ChildModule1 + include Parent +end + +module ChildModule2 # error: `Parent` was declared `has_attached_class!` and so cannot be `extend`ed into the module `ChildModule2 + extend Parent +end + +class ChildClass # error: `Parent` was declared `has_attached_class!` and so must be `extend`ed into the class `ChildClass + include Parent +end diff --git a/test/testdata/infer/generics/has_attached_class_type_syntax.rb b/test/testdata/infer/generics/has_attached_class_type_syntax.rb new file mode 100644 index 0000000000..369818f1bc --- /dev/null +++ b/test/testdata/infer/generics/has_attached_class_type_syntax.rb @@ -0,0 +1,32 @@ +# typed: true + +class C1 + def foo + xs = T::Array[T.attached_class].new + # ^^^^^^^^^^^^^^^^ error: `T.attached_class` may only be used in singleton methods on classes or instance methods on `has_attached_class!` modules + end +end + +module M1 + def foo + xs = T::Array[T.attached_class].new + # ^^^^^^^^^^^^^^^^ error: `M1` must declare `has_attached_class!` before module instance methods can use `T.attached_class` + end +end + +module M2 + extend T::Generic + has_attached_class! + + def foo + xs = T::Array[T.attached_class].new + T.reveal_type(xs) # error: Revealed type: `T::Array[T.attached_class]` + end +end + +module M3 + def self.foo + xs = T::Array[T.attached_class].new + # ^^^^^^^^^^^^^^^^ error: `T.attached_class` cannot be used in singleton methods on modules, because modules cannot be instantiated + end +end diff --git a/test/testdata/infer/generics/initialize_class.rb b/test/testdata/infer/generics/initialize_class.rb new file mode 100644 index 0000000000..16a30b8d18 --- /dev/null +++ b/test/testdata/infer/generics/initialize_class.rb @@ -0,0 +1,57 @@ +# typed: strict +class Module; include T::Sig; end + +module Thing + extend T::Helpers + interface! + + sig {abstract.returns(Integer)} + def foo; end + + module Factory + extend T::Generic + interface! + + has_attached_class! + + sig {abstract.returns(T.attached_class)} + def new; end + end + mixes_in_class_methods(Factory) +end + +sig do + type_parameters(:Instance) + .params( + klass: Thing::Factory[T.all(Thing, T.type_parameter(:Instance))] + ) + .returns(T.type_parameter(:Instance)) +end +def instantiate_class_good(klass) + instance = klass.new + T.reveal_type(instance.foo) # error: `Integer` + instance +end + +class Child + extend T::Generic + include Thing + + sig {override.returns(Integer)} + def foo; 0; end +end + +class GrandChild < Child; end + +instance = instantiate_class_good(Child) +T.reveal_type(instance) # error: `Child` + +sig do + params(thing_factory: Thing::Factory[Child]).void +end +def example(thing_factory) +end + +# To pass, this requires declaring the type member with covariance +instance = example(GrandChild) +# ^^^^^^^^^^ error: Expected `Thing::Factory[Child]` but found `T.class_of(GrandChild)` for argument `thing_factory` diff --git a/test/testdata/infer/generics/initialize_class_bound.rb b/test/testdata/infer/generics/initialize_class_bound.rb new file mode 100644 index 0000000000..3e5fda280b --- /dev/null +++ b/test/testdata/infer/generics/initialize_class_bound.rb @@ -0,0 +1,62 @@ +# typed: strict +class Module; include T::Sig; end + +module Thing + extend T::Helpers + interface! + + sig {abstract.returns(Integer)} + def foo; end + + module Factory + extend T::Generic + interface! + + has_attached_class!(:out) { {upper: Thing} } + + sig {abstract.returns(T.attached_class)} + def make_thing; end + end + mixes_in_class_methods(Factory) +end + +sig do + type_parameters(:Instance) + .params( + klass: Thing::Factory[T.all(Thing, T.type_parameter(:Instance))] + ) + .returns(T.type_parameter(:Instance)) +end +def instantiate_class(klass) + instance = klass.make_thing + instance.foo + instance +end + +class GoodThing + extend T::Generic + include Thing + + sig {override.returns(Integer)} + def foo; 0; end + + sig {override.returns(T.attached_class)} + def self.make_thing + new + end +end + +class ChildGoodThing < GoodThing; end + +instance = ChildGoodThing.make_thing +T.reveal_type(instance) # error: `ChildGoodThing` + +sig do + params(thing_factory: Thing::Factory[GoodThing]).void +end +def example(thing_factory) +end + +# Both allowed, because of covariance +example(GoodThing) +example(ChildGoodThing) diff --git a/test/testdata/infer/generics/object_class.rb b/test/testdata/infer/generics/object_class.rb new file mode 100644 index 0000000000..10eef4615b --- /dev/null +++ b/test/testdata/infer/generics/object_class.rb @@ -0,0 +1,39 @@ +# typed: true +class Model + extend T::Sig + + sig {returns(T.attached_class)} + def self.load_one + new + end +end + +class Get + extend T::Sig + extend T::Generic + + ModelType = type_member { {upper: Model} } + + sig {params(instance: ModelType).returns(ModelType)} + def get(instance) + T.reveal_type(instance) # error: `Get::ModelType` + T.reveal_type(instance.class) # error: `T.class_of(Model)[T.all(Model, Get::ModelType)]` + x = instance.class.load_one + T.reveal_type(x) # error: `T.all(Model, Get::ModelType)` + x + end +end + +class A +end + +module M +end + +extend T::Sig + +sig {params(x: T.all(A, M)).void} +def example(x) + T.reveal_type(x.class) # error: `T.class_of(A)[T.all(A, M)]` + T.reveal_type(x.class.new) # error: `T.all(A, M)` +end diff --git a/test/testdata/infer/generics/self_class_elem.rb b/test/testdata/infer/generics/self_class_elem.rb index b69bea420e..bf3634f713 100644 --- a/test/testdata/infer/generics/self_class_elem.rb +++ b/test/testdata/infer/generics/self_class_elem.rb @@ -15,9 +15,9 @@ def initialize(val) sig {params(new_val: Elem).returns(T.self_type)} def copy_with(new_val) klass = self.class - T.reveal_type(klass) # error: `T.class_of(Box)` + T.reveal_type(klass) # error: `T.class_of(Box)[Box[Box::Elem]]` new_box = self.class.new(new_val) - T.reveal_type(new_box) # error: `Box[T.untyped]` + T.reveal_type(new_box) # error: `Box[Box::Elem]` box_elem_class = self.class[Elem] T.reveal_type(box_elem_class) # error: Runtime object representing type: Box[Box::Elem] diff --git a/test/testdata/infer/generics/specified.rb b/test/testdata/infer/generics/specified.rb index 51e2d264ae..2def8b00bd 100644 --- a/test/testdata/infer/generics/specified.rb +++ b/test/testdata/infer/generics/specified.rb @@ -82,6 +82,8 @@ class FullChild < HalfChild V = type_member {{fixed: String}} sig {params(f: FullChild).returns(FullChild[])} + # ^^ error: All type parameters for `FullChild` have already been fixed + # ^^ error: All type parameters for `FullChild` have already been fixed def f(f); f; end end diff --git a/test/testdata/infer/generics/specified.rb.autocorrects.exp b/test/testdata/infer/generics/specified.rb.autocorrects.exp new file mode 100644 index 0000000000..ff9f62c714 --- /dev/null +++ b/test/testdata/infer/generics/specified.rb.autocorrects.exp @@ -0,0 +1,135 @@ +# -- test/testdata/infer/generics/specified.rb -- +# typed: true +require_relative '../../t' + +class PreChild < Parent + extend T::Generic + + Elem = type_member {{fixed: String}} +end + +class Parent + extend T::Generic + extend T::Sig + + Elem = type_member + + sig {params(a: Elem).returns(Elem)} + def foo(a) + a + end +end + +class Child < Parent + extend T::Generic + Elem = type_member {{fixed: String}} + + def use_foo + T.assert_type!(foo("foo"), String) + end +end + +module Mixin + extend T::Generic + extend T::Sig + Elem = type_member + + sig {params(a: Elem).returns(Elem)} + def foo(a) + a + end +end + +class WithMixin + extend T::Generic + include Mixin + Elem = type_member {{fixed: String}} +end + +class NotATypeVar + NotAElem = String +end + + +class ParentWithMultiple + extend T::Generic + extend T::Sig + + K = type_member + V = type_member + + sig {params(k: K, v: V).returns(K)} + def foo(k, v) + k + end +end + +class HalfChild < ParentWithMultiple + extend T::Generic + K = type_member {{fixed: Integer}} + V = type_member +end + +class HalfChildOther < ParentWithMultiple + extend T::Generic + K = type_member + V = type_member {{fixed: Integer}} +end + +class FullChild < HalfChild + extend T::Generic + extend T::Sig + K = type_member {{fixed: Integer}} + V = type_member {{fixed: String}} + + sig {params(f: FullChild).returns(FullChild)} + # ^^ error: All type parameters for `FullChild` have already been fixed + # ^^ error: All type parameters for `FullChild` have already been fixed + def f(f); f; end +end + +class ParentEnumerable + extend T::Generic + include Enumerable + + K = type_member + V = type_member + Elem = type_member + + def each(&blk); end +end + +class ChildEnumerable < ParentEnumerable + extend T::Generic + K = type_member {{fixed: String}} + V = type_member + Elem = type_member +end + +def main + a = Child.new.foo('a') + T.assert_type!(a, String) + + a = PreChild.new.foo('a') + T.assert_type!(a, String) + + a = WithMixin.new.foo('a') + T.assert_type!(a, String) + + a = ParentWithMultiple[Symbol, String].new.foo(:a, 'b') + T.assert_type!(a, Symbol) + + a = HalfChild[String].new.foo(1, 'b') + T.assert_type!(a, Integer) + + a = HalfChildOther[String].new.foo('a', 1) + T.assert_type!(a, String) + + a = FullChild.new.foo(1, 'b') + T.assert_type!(a, Integer) + + a = ChildEnumerable[Integer, String].new.min + T.assert_type!(a, T.nilable(String)) +end +main +# ------------------------------ diff --git a/test/testdata/infer/generics/t_class.rb b/test/testdata/infer/generics/t_class.rb new file mode 100644 index 0000000000..580dde0b3e --- /dev/null +++ b/test/testdata/infer/generics/t_class.rb @@ -0,0 +1,18 @@ +# typed: strict +extend T::Sig + +class A; end + +sig do + type_parameters(:U) + .params(klass: T::Class[T.type_parameter(:U)]) + .returns(T.type_parameter(:U)) +end +def instantiate_class(klass) + instance = klass.new + T.reveal_type(instance) # error: `T.type_parameter(:U) (of Object#instantiate_class)` + instance +end + +a = instantiate_class(A) +T.reveal_type(a) # error: `A` diff --git a/test/testdata/infer/isa_generic.rb.cfg-text.exp b/test/testdata/infer/isa_generic.rb.cfg-text.exp index 5fbb803544..8e0e32dafd 100644 --- a/test/testdata/infer/isa_generic.rb.cfg-text.exp +++ b/test/testdata/infer/isa_generic.rb.cfg-text.exp @@ -141,9 +141,9 @@ bb2[rubyRegionId=1, firstDead=-1](: T.class_of(), $7: Sorbet::Private::Static::Void, $8: T.class_of()): $3: Sorbet::Private::Static::Void = Solve<$7, sig> : T.class_of() = $8 - $24: T.class_of(Sorbet::Private::Static) = alias - $26: Sorbet::Private::Static::Void = $24: T.class_of(Sorbet::Private::Static).sig(: T.class_of()) - $27: T.class_of() = + $23: T.class_of(Sorbet::Private::Static) = alias + $25: Sorbet::Private::Static::Void = $23: T.class_of(Sorbet::Private::Static).sig(: T.class_of()) + $26: T.class_of() = -> bb6 # backedges @@ -151,59 +151,59 @@ bb3[rubyRegionId=0, firstDead=-1]($7: Sorbet::Private::Stat bb5[rubyRegionId=1, firstDead=9](: T.class_of(), $7: Sorbet::Private::Static::Void, $8: T.class_of()): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $13: Symbol(:x) = :x - $16: T.class_of(T) = alias - $18: T.class_of(Concrete) = alias - $20: T.class_of(Other) = alias - $14: Runtime object representing type: T.any(Concrete, Other) = $16: T.class_of(T).any($18: T.class_of(Concrete), $20: T.class_of(Other)) - $11: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($13: Symbol(:x), $14: Runtime object representing type: T.any(Concrete, Other)) - $10: T::Private::Methods::DeclBuilder = $11: T::Private::Methods::DeclBuilder.void() - $21: T.noreturn = blockreturn $10: T::Private::Methods::DeclBuilder + $12: Symbol(:x) = :x + $15: T.class_of(T) = alias + $17: T.class_of(Concrete) = alias + $19: T.class_of(Other) = alias + $13: Runtime object representing type: T.any(Concrete, Other) = $15: T.class_of(T).any($17: T.class_of(Concrete), $19: T.class_of(Other)) + $10: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($12: Symbol(:x), $13: Runtime object representing type: T.any(Concrete, Other)) + $9: T::Private::Methods::DeclBuilder = $10: T::Private::Methods::DeclBuilder.void() + $20: T.noreturn = blockreturn $9: T::Private::Methods::DeclBuilder -> bb2 # backedges # - bb3(rubyRegionId=0) # - bb9(rubyRegionId=2) -bb6[rubyRegionId=2, firstDead=-1](: T.class_of(), $26: Sorbet::Private::Static::Void, $27: T.class_of()): +bb6[rubyRegionId=2, firstDead=-1](: T.class_of(), $25: Sorbet::Private::Static::Void, $26: T.class_of()): # outerLoops: 1 -> (NilClass ? bb9 : bb7) # backedges # - bb6(rubyRegionId=2) -bb7[rubyRegionId=0, firstDead=18]($26: Sorbet::Private::Static::Void, $27: T.class_of()): - $22: Sorbet::Private::Static::Void = Solve<$26, sig> - : T.class_of() = $27 - $44: T.class_of(T::Sig) = alias - $46: T.class_of(T) = alias - $41: T.class_of() = : T.class_of().extend($44: T.class_of(T::Sig)) - $51: T.class_of(Sorbet::Private::Static) = alias - $53: T.class_of(Base)[T.untyped] = alias - $49: Sorbet::Private::Static::Void = $51: T.class_of(Sorbet::Private::Static).keep_for_ide($53: T.class_of(Base)[T.untyped]) - $58: T.class_of(Sorbet::Private::Static) = alias - $60: T.class_of(Concrete) = alias - $56: Sorbet::Private::Static::Void = $58: T.class_of(Sorbet::Private::Static).keep_for_ide($60: T.class_of(Concrete)) - $63: T.class_of(Sorbet::Private::Static) = alias - $65: T.class_of(Base)[T.untyped] = alias - $61: Sorbet::Private::Static::Void = $63: T.class_of(Sorbet::Private::Static).keep_for_ide($65: T.class_of(Base)[T.untyped]) - $70: T.class_of(Sorbet::Private::Static) = alias - $72: T.class_of(Other) = alias - $68: Sorbet::Private::Static::Void = $70: T.class_of(Sorbet::Private::Static).keep_for_ide($72: T.class_of(Other)) +bb7[rubyRegionId=0, firstDead=18]($25: Sorbet::Private::Static::Void, $26: T.class_of()): + $21: Sorbet::Private::Static::Void = Solve<$25, sig> + : T.class_of() = $26 + $42: T.class_of(T::Sig) = alias + $44: T.class_of(T) = alias + $39: T.class_of() = : T.class_of().extend($42: T.class_of(T::Sig)) + $49: T.class_of(Sorbet::Private::Static) = alias + $51: T.class_of(Base)[T.untyped] = alias + $47: Sorbet::Private::Static::Void = $49: T.class_of(Sorbet::Private::Static).keep_for_ide($51: T.class_of(Base)[T.untyped]) + $56: T.class_of(Sorbet::Private::Static) = alias + $58: T.class_of(Concrete) = alias + $54: Sorbet::Private::Static::Void = $56: T.class_of(Sorbet::Private::Static).keep_for_ide($58: T.class_of(Concrete)) + $61: T.class_of(Sorbet::Private::Static) = alias + $63: T.class_of(Base)[T.untyped] = alias + $59: Sorbet::Private::Static::Void = $61: T.class_of(Sorbet::Private::Static).keep_for_ide($63: T.class_of(Base)[T.untyped]) + $68: T.class_of(Sorbet::Private::Static) = alias + $70: T.class_of(Other) = alias + $66: Sorbet::Private::Static::Void = $68: T.class_of(Sorbet::Private::Static).keep_for_ide($70: T.class_of(Other)) : T.noreturn = return $2: NilClass -> bb1 # backedges # - bb6(rubyRegionId=2) -bb9[rubyRegionId=2, firstDead=9](: T.class_of(), $26: Sorbet::Private::Static::Void, $27: T.class_of()): +bb9[rubyRegionId=2, firstDead=9](: T.class_of(), $25: Sorbet::Private::Static::Void, $26: T.class_of()): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $32: Symbol(:x) = :x - $35: T.class_of(T) = alias - $37: T.class_of(Base)[T.untyped] = alias - $39: T.class_of(Other) = alias - $33: Runtime object representing type: T.any(Base, Other) = $35: T.class_of(T).any($37: T.class_of(Base)[T.untyped], $39: T.class_of(Other)) - $30: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($32: Symbol(:x), $33: Runtime object representing type: T.any(Base, Other)) - $29: T::Private::Methods::DeclBuilder = $30: T::Private::Methods::DeclBuilder.void() - $40: T.noreturn = blockreturn $29: T::Private::Methods::DeclBuilder + $30: Symbol(:x) = :x + $33: T.class_of(T) = alias + $35: T.class_of(Base)[T.untyped] = alias + $37: T.class_of(Other) = alias + $31: Runtime object representing type: T.any(Base, Other) = $33: T.class_of(T).any($35: T.class_of(Base)[T.untyped], $37: T.class_of(Other)) + $28: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($30: Symbol(:x), $31: Runtime object representing type: T.any(Base, Other)) + $27: T::Private::Methods::DeclBuilder = $28: T::Private::Methods::DeclBuilder.void() + $38: T.noreturn = blockreturn $27: T::Private::Methods::DeclBuilder -> bb6 } @@ -263,11 +263,11 @@ bb3[rubyRegionId=0, firstDead=2]($12: Sorbet::Private::Stat bb5[rubyRegionId=1, firstDead=6](: T.class_of(Concrete), $12: Sorbet::Private::Static::Void, $13: T.class_of(Concrete)): # outerLoops: 1 : T.class_of(Concrete) = loadSelf(type_template) - $16: Symbol(:fixed) = :fixed - $18: T.class_of(String) = alias - $19: T.class_of() = alias > - $15: {fixed: T.class_of(String)} = $19: T.class_of().($16: Symbol(:fixed), $18: T.class_of(String)) - $20: T.noreturn = blockreturn $15: {fixed: T.class_of(String)} + $15: Symbol(:fixed) = :fixed + $17: T.class_of(String) = alias + $18: T.class_of() = alias > + $14: {fixed: T.class_of(String)} = $18: T.class_of().($15: Symbol(:fixed), $17: T.class_of(String)) + $19: T.noreturn = blockreturn $14: {fixed: T.class_of(String)} -> bb2 } diff --git a/test/testdata/infer/kwsplat_is_sometimes_okay.rb.autocorrects.exp b/test/testdata/infer/kwsplat_is_sometimes_okay.rb.autocorrects.exp index 230b3ce5a2..9fd27589fd 100644 --- a/test/testdata/infer/kwsplat_is_sometimes_okay.rb.autocorrects.exp +++ b/test/testdata/infer/kwsplat_is_sometimes_okay.rb.autocorrects.exp @@ -23,10 +23,13 @@ def takes_some_required_untyped(x:, y: ''); end sig {params(x: String, y: String).void} def takes_all_optional_untyped(x: '', y: ''); end +extend T::Sig sig { params(x: T.untyped, y: T.untyped).returns(NilClass) } def takes_all_required_unsigged(x:, y:); end # error: does not have a `sig` +extend T::Sig sig { params(x: T.untyped, y: T.untyped).returns(NilClass) } def takes_some_required_unsigged(x:, y: ''); end # error: does not have a `sig` +extend T::Sig sig { params(x: T.untyped, y: T.untyped).returns(NilClass) } def takes_all_optional_unsigged(x: '', y: ''); end # error: does not have a `sig` diff --git a/test/testdata/infer/lub_tuples.rb.symbol-table-raw.exp b/test/testdata/infer/lub_tuples.rb.symbol-table-raw.exp index cc6b029fa5..2de5c9ca5f 100644 --- a/test/testdata/infer/lub_tuples.rb.symbol-table-raw.exp +++ b/test/testdata/infer/lub_tuples.rb.symbol-table-raw.exp @@ -11,16 +11,13 @@ class >> < > () argument @ Loc {file=test/testdata/infer/lub_tuples.rb start=??? end=???} method >::>::>::># : private () -> TupleType { 0 = TrueClass | FalseClass 1 = String | NilClass } @ Loc {file=test/testdata/infer/lub_tuples.rb start=13:11 end=13:54} argument -> T.untyped @ Loc {file=test/testdata/infer/lub_tuples.rb start=??? end=???} - method >::>::>::># () @ Loc {file=test/testdata/infer/lub_tuples.rb start=5:3 end=5:19} + method >::>::>::># : private () @ Loc {file=test/testdata/infer/lub_tuples.rb start=5:3 end=5:19} argument @ Loc {file=test/testdata/infer/lub_tuples.rb start=??? end=???} class >::>::>::> $1>[>>] < > $1> (>) @ Loc {file=test/testdata/infer/lub_tuples.rb start=2:1 end=2:51} type-member(+) >::>::>::> $1>::>> -> LambdaParam(>::>::>::> $1>::>>, lower=T.noreturn, upper=Opus::CIBot::Tasks::NotifySlackBuildComplete) @ Loc {file=test/testdata/infer/lub_tuples.rb start=2:1 end=2:51} method >::>::>::> $1>#> () @ Loc {file=test/testdata/infer/lub_tuples.rb start=2:1 end=16:4} argument @ Loc {file=test/testdata/infer/lub_tuples.rb start=??? end=???} - class >::>::> $1>[>>] < > () @ Loc {file=test/testdata/infer/lub_tuples.rb start=2:7 end=2:25} - type-member(+) >::>::> $1>::>> -> LambdaParam(>::>::> $1>::>>, lower=T.noreturn, upper=Opus::CIBot::Tasks) @ Loc {file=test/testdata/infer/lub_tuples.rb start=2:7 end=2:25} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/infer/lub_tuples.rb start=2:7 end=2:18} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Opus::CIBot) @ Loc {file=test/testdata/infer/lub_tuples.rb start=2:7 end=2:18} - class > $1>[>>] < > () @ Loc {file=test/testdata/infer/lub_tuples.rb start=2:7 end=2:11} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Opus) @ Loc {file=test/testdata/infer/lub_tuples.rb start=2:7 end=2:11} + class >::>::> $1> < > () @ Loc {file=test/testdata/infer/lub_tuples.rb start=2:7 end=2:25} + class >::> $1> < > () @ Loc {file=test/testdata/infer/lub_tuples.rb start=2:7 end=2:18} + class > $1> < > () @ Loc {file=test/testdata/infer/lub_tuples.rb start=2:7 end=2:11} diff --git a/test/testdata/infer/magic_suggest_untyped.rb b/test/testdata/infer/magic_suggest_untyped.rb new file mode 100644 index 0000000000..2508bf6cba --- /dev/null +++ b/test/testdata/infer/magic_suggest_untyped.rb @@ -0,0 +1,12 @@ +# typed: strict + +class A + extend T::Sig + + X = T.unsafe(nil) # error: Constants must have type annotations + + sig {void} + def initialize + @x = T.unsafe(nil) # error: must be declared using `T.let` + end +end diff --git a/test/testdata/infer/magic_suggest_untyped.rb.autocorrects.exp b/test/testdata/infer/magic_suggest_untyped.rb.autocorrects.exp new file mode 100644 index 0000000000..07414cb055 --- /dev/null +++ b/test/testdata/infer/magic_suggest_untyped.rb.autocorrects.exp @@ -0,0 +1,14 @@ +# -- test/testdata/infer/magic_suggest_untyped.rb -- +# typed: strict + +class A + extend T::Sig + + X = T.unsafe(nil) # error: Constants must have type annotations + + sig {void} + def initialize + @x = T.unsafe(nil) # error: must be declared using `T.let` + end +end +# ------------------------------ diff --git a/test/testdata/infer/magic_suggest_untyped_unsafe.rb b/test/testdata/infer/magic_suggest_untyped_unsafe.rb new file mode 100644 index 0000000000..cd7607c689 --- /dev/null +++ b/test/testdata/infer/magic_suggest_untyped_unsafe.rb @@ -0,0 +1,13 @@ +# typed: strict +# enable-suggest-unsafe: true + +class A + extend T::Sig + + X = T.unsafe(nil) # error: Constants must have type annotations + + sig {void} + def initialize + @x = T.unsafe(nil) # error: must be declared using `T.let` + end +end diff --git a/test/testdata/infer/magic_suggest_untyped_unsafe.rb.autocorrects.exp b/test/testdata/infer/magic_suggest_untyped_unsafe.rb.autocorrects.exp new file mode 100644 index 0000000000..48ea7190d1 --- /dev/null +++ b/test/testdata/infer/magic_suggest_untyped_unsafe.rb.autocorrects.exp @@ -0,0 +1,15 @@ +# -- test/testdata/infer/magic_suggest_untyped_unsafe.rb -- +# typed: strict +# enable-suggest-unsafe: true + +class A + extend T::Sig + + X = T.let(T.unsafe(nil), T.untyped) # error: Constants must have type annotations + + sig {void} + def initialize + @x = T.let(T.unsafe(nil), T.untyped) # error: must be declared using `T.let` + end +end +# ------------------------------ diff --git a/test/testdata/infer/no_valid_instantiation_result.rb b/test/testdata/infer/no_valid_instantiation_result.rb new file mode 100644 index 0000000000..c6e5104284 --- /dev/null +++ b/test/testdata/infer/no_valid_instantiation_result.rb @@ -0,0 +1,22 @@ +# typed: true +extend T::Sig + +sig do + type_parameters(:U) + .params( + x: T.type_parameter(:U), + f: T.proc.params(x: T.type_parameter(:U)).void, + ) + .returns(T.type_parameter(:U)) +end +def apply_f(x, f) + x +end + +sig {params(str: String).void} +def main(str) + f = T.let(->(y) { y }, T.proc.params(x: Integer).void) + res = apply_f(str, f) + # ^^^^^^^ error: Could not find valid instantiation of type parameters for `Object#apply_f` + T.reveal_type(res) # error: `T.untyped` +end diff --git a/test/testdata/infer/overloads_test.rb b/test/testdata/infer/overloads_test.rb index c265ab3213..2e64325ad9 100644 --- a/test/testdata/infer/overloads_test.rb +++ b/test/testdata/infer/overloads_test.rb @@ -79,7 +79,7 @@ def test T.assert_type!(h.overloaded(self.class), Symbol) h.overloaded(1) # error: Expected `String` but found `Integer(1)` for argument `arg0` # should ask for string - h.overloaded("1", 2) # error: Expected `Class` but found `String("1")` for argument `arg0` + h.overloaded("1", 2) # error: Expected `T::Class[T.anything]` but found `String("1")` for argument `arg0` # ^ error: Expected `String` but found `Integer(2)` for argument `arg1` g = OverloadAndGenerics[Integer].new diff --git a/test/testdata/infer/private_class_methods.rb.symbol-table.exp b/test/testdata/infer/private_class_methods.rb.symbol-table.exp index ed49cbcf4f..7ef08d01c2 100644 --- a/test/testdata/infer/private_class_methods.rb.symbol-table.exp +++ b/test/testdata/infer/private_class_methods.rb.symbol-table.exp @@ -13,8 +13,7 @@ class :: < ::Object () argument @ Loc {file=test/testdata/infer/private_class_methods.rb start=??? end=???} method ::Test::ClassMethods#method_i : private () @ test/testdata/infer/private_class_methods.rb:31 argument @ Loc {file=test/testdata/infer/private_class_methods.rb start=??? end=???} - class ::Test::[] < ::Module () @ test/testdata/infer/private_class_methods.rb:23 - type-member(+) ::Test:::: -> T.attached_class (of Test::ClassMethods) @ test/testdata/infer/private_class_methods.rb:23 + class ::Test:: < ::Module () @ test/testdata/infer/private_class_methods.rb:23 method ::Test::# () @ test/testdata/infer/private_class_methods.rb:23 argument @ Loc {file=test/testdata/infer/private_class_methods.rb start=??? end=???} class ::[] < :: (ClassMethods) @ test/testdata/infer/private_class_methods.rb:12 @@ -33,7 +32,7 @@ class :: < ::Object () argument @ Loc {file=test/testdata/infer/private_class_methods.rb start=??? end=???} method ::#method_f : private () @ test/testdata/infer/private_class_methods.rb:20 argument @ Loc {file=test/testdata/infer/private_class_methods.rb start=??? end=???} - class ::>[] < ::> () @ test/testdata/infer/private_class_methods.rb:12 + class ::>[] < ::Class () @ test/testdata/infer/private_class_methods.rb:12 type-member(+) ::>:: -> T.attached_class (of T.class_of(Test)) @ test/testdata/infer/private_class_methods.rb:12 method ::># () @ test/testdata/infer/private_class_methods.rb:12 argument @ Loc {file=test/testdata/infer/private_class_methods.rb start=??? end=???} diff --git a/test/testdata/infer/private_constant.rb.symbol-table.exp b/test/testdata/infer/private_constant.rb.symbol-table.exp index 3123563805..5896fadee1 100644 --- a/test/testdata/infer/private_constant.rb.symbol-table.exp +++ b/test/testdata/infer/private_constant.rb.symbol-table.exp @@ -12,8 +12,7 @@ class :: < ::Object () method ::Foo::# () @ test/testdata/infer/private_constant.rb:22 argument @ Loc {file=test/testdata/infer/private_constant.rb start=??? end=???} module ::Foo::AnotherPrivateModule < ::Sorbet::Private::Static::ImplicitModuleSuperclass () : private @ test/testdata/infer/private_constant.rb:39 - class ::Foo::[] < ::Module () @ test/testdata/infer/private_constant.rb:39 - type-member(+) ::Foo:::: -> T.attached_class (of Foo::AnotherPrivateModule) @ test/testdata/infer/private_constant.rb:39 + class ::Foo:: < ::Module () @ test/testdata/infer/private_constant.rb:39 method ::Foo::# () @ test/testdata/infer/private_constant.rb:39 argument @ Loc {file=test/testdata/infer/private_constant.rb start=??? end=???} class ::Foo::PrivateClass < ::Object () : private @ test/testdata/infer/private_constant.rb:19 @@ -31,14 +30,12 @@ class :: < ::Object () argument @ Loc {file=test/testdata/infer/private_constant.rb start=??? end=???} method ::Foo::PrivateModule::#also_ok_private_usage () @ test/testdata/infer/private_constant.rb:34 argument @ Loc {file=test/testdata/infer/private_constant.rb start=??? end=???} - class ::Foo::[] < ::Module () @ test/testdata/infer/private_constant.rb:25 - type-member(+) ::Foo:::: -> T.attached_class (of Foo::PrivateModule) @ test/testdata/infer/private_constant.rb:25 + class ::Foo:: < ::Module () @ test/testdata/infer/private_constant.rb:25 method ::Foo::# () @ test/testdata/infer/private_constant.rb:25 argument @ Loc {file=test/testdata/infer/private_constant.rb start=??? end=???} method ::Foo::#ok_private_usage () @ test/testdata/infer/private_constant.rb:26 argument @ Loc {file=test/testdata/infer/private_constant.rb start=??? end=???} - class ::[] < ::Module (Sig) @ test/testdata/infer/private_constant.rb:3 - type-member(+) :::: -> T.attached_class (of Foo) @ test/testdata/infer/private_constant.rb:3 + class :: < ::Module (Sig) @ test/testdata/infer/private_constant.rb:3 method ::# () @ test/testdata/infer/private_constant.rb:3 argument @ Loc {file=test/testdata/infer/private_constant.rb start=??? end=???} method ::#not_ok_private_usage () @ test/testdata/infer/private_constant.rb:50 diff --git a/test/testdata/infer/private_constant_in_rbi.symbol-table.exp b/test/testdata/infer/private_constant_in_rbi.symbol-table.exp index 0e2b232084..886c84a698 100644 --- a/test/testdata/infer/private_constant_in_rbi.symbol-table.exp +++ b/test/testdata/infer/private_constant_in_rbi.symbol-table.exp @@ -14,8 +14,7 @@ class :: < ::Object () method ::Foo::# () @ test/testdata/infer/private_constant_in_rbi__1.rbi:22 argument @ Loc {file=test/testdata/infer/private_constant_in_rbi__1.rbi start=??? end=???} module ::Foo::AnotherPrivateModule < ::Sorbet::Private::Static::ImplicitModuleSuperclass () : private @ test/testdata/infer/private_constant_in_rbi__1.rbi:35 - class ::Foo::[] < ::Module () @ test/testdata/infer/private_constant_in_rbi__1.rbi:35 - type-member(+) ::Foo:::: -> T.attached_class (of Foo::AnotherPrivateModule) @ test/testdata/infer/private_constant_in_rbi__1.rbi:35 + class ::Foo:: < ::Module () @ test/testdata/infer/private_constant_in_rbi__1.rbi:35 method ::Foo::# () @ test/testdata/infer/private_constant_in_rbi__1.rbi:35 argument @ Loc {file=test/testdata/infer/private_constant_in_rbi__1.rbi start=??? end=???} class ::Foo::PrivateClass < ::Object () : private @ test/testdata/infer/private_constant_in_rbi__1.rbi:19 @@ -33,14 +32,12 @@ class :: < ::Object () argument @ Loc {file=test/testdata/infer/private_constant_in_rbi__1.rbi start=??? end=???} method ::Foo::PrivateModule::#also_ok_private_usage () @ test/testdata/infer/private_constant_in_rbi__1.rbi:30 argument @ Loc {file=test/testdata/infer/private_constant_in_rbi__1.rbi start=??? end=???} - class ::Foo::[] < ::Module () @ test/testdata/infer/private_constant_in_rbi__1.rbi:25 - type-member(+) ::Foo:::: -> T.attached_class (of Foo::PrivateModule) @ test/testdata/infer/private_constant_in_rbi__1.rbi:25 + class ::Foo:: < ::Module () @ test/testdata/infer/private_constant_in_rbi__1.rbi:25 method ::Foo::# () @ test/testdata/infer/private_constant_in_rbi__1.rbi:25 argument @ Loc {file=test/testdata/infer/private_constant_in_rbi__1.rbi start=??? end=???} method ::Foo::#ok_private_usage () @ test/testdata/infer/private_constant_in_rbi__1.rbi:26 argument @ Loc {file=test/testdata/infer/private_constant_in_rbi__1.rbi start=??? end=???} - class ::[] < ::Module (Sig) @ test/testdata/infer/private_constant_in_rbi__1.rbi:3 - type-member(+) :::: -> T.attached_class (of Foo) @ test/testdata/infer/private_constant_in_rbi__1.rbi:3 + class :: < ::Module (Sig) @ test/testdata/infer/private_constant_in_rbi__1.rbi:3 method ::# () @ test/testdata/infer/private_constant_in_rbi__1.rbi:3 argument @ Loc {file=test/testdata/infer/private_constant_in_rbi__1.rbi start=??? end=???} method ::#not_ok_private_usage () -> Foo::PrivateClass @ test/testdata/infer/private_constant_in_rbi__1.rbi:39 diff --git a/test/testdata/infer/private_initialize.rb b/test/testdata/infer/private_initialize.rb new file mode 100644 index 0000000000..8bf5bd73fd --- /dev/null +++ b/test/testdata/infer/private_initialize.rb @@ -0,0 +1,44 @@ +# typed: true +extend T::Sig + +class A + private def initialize + puts 'hello' + end +end + +a = A.new +a.initialize # error: Non-private call to private method `initialize` + +class B + def self.new + end + private_class_method :new +end + +b = B.new # error: Non-private call to private method `new` +b.initialize + +class C + # At time of writing, Sorbet doesn't support changing visibility of an + # inherited method. + private_class_method :new +end + +c = C.new +c.initialize + +class D + def self.initialize + end +end + +D.initialize + +class E +end + +sig {params(xs: T::Array[T.class_of(E)]).void} +def example(xs) + xs.map(&:new) +end diff --git a/test/testdata/infer/private_methods_any_all.rb b/test/testdata/infer/private_methods_any_all.rb index 5a70747bcc..66729170fd 100644 --- a/test/testdata/infer/private_methods_any_all.rb +++ b/test/testdata/infer/private_methods_any_all.rb @@ -37,11 +37,11 @@ def qux; end sig {params(bar_and_qux: T.all(PrivateBar, PublicQux)).void} def test_bar_and_qux(bar_and_qux) - bar_and_qux.bar # error: Non-private call to private method `bar` on `T.all(PrivateBar, PublicQux)` + bar_and_qux.bar # error: Non-private call to private method `bar` on `PrivateBar` component of `T.all(PrivateBar, PublicQux)` end sig {params(qux_and_bar: T.all(PublicQux, PrivateBar)).void} def test_qux_and_bar(qux_and_bar) - qux_and_bar.bar # error: Non-private call to private method `bar` on `T.all(PublicQux, PrivateBar)` + qux_and_bar.bar # error: Non-private call to private method `bar` on `PrivateBar` component of `T.all(PublicQux, PrivateBar)` end diff --git a/test/testdata/infer/rebind.rb.cfg-text.exp b/test/testdata/infer/rebind.rb.cfg-text.exp index 8cefa932ff..be07ea4710 100644 --- a/test/testdata/infer/rebind.rb.cfg-text.exp +++ b/test/testdata/infer/rebind.rb.cfg-text.exp @@ -93,9 +93,9 @@ bb2[rubyRegionId=1, firstDead=-1](: T.class_of(B), $7 bb3[rubyRegionId=0, firstDead=6]($7: Sorbet::Private::Static::Void, $8: T.class_of(B)): $3: Sorbet::Private::Static::Void = Solve<$7, sig> : T.class_of(B) = $8 - $25: T.class_of(T::Sig) = alias - $27: T.class_of(T) = alias - $22: T.class_of(B) = : T.class_of(B).extend($25: T.class_of(T::Sig)) + $24: T.class_of(T::Sig) = alias + $26: T.class_of(T) = alias + $21: T.class_of(B) = : T.class_of(B).extend($24: T.class_of(T::Sig)) : T.noreturn = return $2: NilClass -> bb1 @@ -104,15 +104,15 @@ bb3[rubyRegionId=0, firstDead=6]($7: Sorbet::Private::Stati bb5[rubyRegionId=1, firstDead=10](: T.class_of(B), $7: Sorbet::Private::Static::Void, $8: T.class_of(B)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $13: Symbol(:blk) = :blk - $18: T.class_of(T) = alias - $16: T.class_of(T.proc) = $18: T.class_of(T).proc() - $20: T.class_of(C) = alias - $15: T.class_of(T.proc) = $16: T.class_of(T.proc).bind($20: T.class_of(C)) - $14: Runtime object representing type: T.proc.void = $15: T.class_of(T.proc).void() - $11: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($13: Symbol(:blk), $14: Runtime object representing type: T.proc.void) - $10: T::Private::Methods::DeclBuilder = $11: T::Private::Methods::DeclBuilder.void() - $21: T.noreturn = blockreturn $10: T::Private::Methods::DeclBuilder + $12: Symbol(:blk) = :blk + $17: T.class_of(T) = alias + $15: T.class_of(T.proc) = $17: T.class_of(T).proc() + $19: T.class_of(C) = alias + $14: T.class_of(T.proc) = $15: T.class_of(T.proc).bind($19: T.class_of(C)) + $13: Runtime object representing type: T.proc.void = $14: T.class_of(T.proc).void() + $10: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($12: Symbol(:blk), $13: Runtime object representing type: T.proc.void) + $9: T::Private::Methods::DeclBuilder = $10: T::Private::Methods::DeclBuilder.void() + $20: T.noreturn = blockreturn $9: T::Private::Methods::DeclBuilder -> bb2 } @@ -157,9 +157,9 @@ bb2[rubyRegionId=1, firstDead=-1](: T.class_of(A), $7 bb3[rubyRegionId=0, firstDead=6]($7: Sorbet::Private::Static::Void, $8: T.class_of(A)): $3: Sorbet::Private::Static::Void = Solve<$7, sig> : T.class_of(A) = $8 - $25: T.class_of(T::Sig) = alias - $27: T.class_of(T) = alias - $22: T.class_of(A) = : T.class_of(A).extend($25: T.class_of(T::Sig)) + $24: T.class_of(T::Sig) = alias + $26: T.class_of(T) = alias + $21: T.class_of(A) = : T.class_of(A).extend($24: T.class_of(T::Sig)) : T.noreturn = return $2: NilClass -> bb1 @@ -168,15 +168,15 @@ bb3[rubyRegionId=0, firstDead=6]($7: Sorbet::Private::Stati bb5[rubyRegionId=1, firstDead=10](: T.class_of(A), $7: Sorbet::Private::Static::Void, $8: T.class_of(A)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $13: Symbol(:blk) = :blk - $18: T.class_of(T) = alias - $16: T.class_of(T.proc) = $18: T.class_of(T).proc() - $20: T.class_of(B) = alias - $15: T.class_of(T.proc) = $16: T.class_of(T.proc).bind($20: T.class_of(B)) - $14: Runtime object representing type: T.proc.void = $15: T.class_of(T.proc).void() - $11: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($13: Symbol(:blk), $14: Runtime object representing type: T.proc.void) - $10: T::Private::Methods::DeclBuilder = $11: T::Private::Methods::DeclBuilder.void() - $21: T.noreturn = blockreturn $10: T::Private::Methods::DeclBuilder + $12: Symbol(:blk) = :blk + $17: T.class_of(T) = alias + $15: T.class_of(T.proc) = $17: T.class_of(T).proc() + $19: T.class_of(B) = alias + $14: T.class_of(T.proc) = $15: T.class_of(T.proc).bind($19: T.class_of(B)) + $13: Runtime object representing type: T.proc.void = $14: T.class_of(T.proc).void() + $10: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($12: Symbol(:blk), $13: Runtime object representing type: T.proc.void) + $9: T::Private::Methods::DeclBuilder = $10: T::Private::Methods::DeclBuilder.void() + $20: T.noreturn = blockreturn $9: T::Private::Methods::DeclBuilder -> bb2 } @@ -227,8 +227,8 @@ bb3[rubyRegionId=0, firstDead=2]($4: Sorbet::Private::Stati bb5[rubyRegionId=1, firstDead=3](: Use, $4: Sorbet::Private::Static::Void, $5: Use): # outerLoops: 1 : Use = loadSelf(only_on_Use) - $7: Integer(1) = 1 - $8: T.noreturn = blockreturn $7: Integer(1) + $6: Integer(1) = 1 + $7: T.noreturn = blockreturn $6: Integer(1) -> bb2 } @@ -266,36 +266,36 @@ bb3[rubyRegionId=0, firstDead=2]($5: Sorbet::Private::Stati bb5[rubyRegionId=1, firstDead=-1](: Use, $5: Sorbet::Private::Static::Void, $6: Use): # outerLoops: 1 : B = loadSelf(mySig) - $9: T.untyped = : B.only_on_Use() - $11: T.untyped = : B.mySig() - $14: Sorbet::Private::Static::Void = : B.only_on_B() - $15: B = + $8: T.untyped = : B.only_on_Use() + $10: T.untyped = : B.mySig() + $13: Sorbet::Private::Static::Void = : B.only_on_B() + $14: B = -> bb6 # backedges # - bb5(rubyRegionId=1) # - bb9(rubyRegionId=2) -bb6[rubyRegionId=2, firstDead=-1](: B, $5: Sorbet::Private::Static::Void, $6: Use, $14: Sorbet::Private::Static::Void, $15: B): +bb6[rubyRegionId=2, firstDead=-1](: B, $5: Sorbet::Private::Static::Void, $6: Use, $13: Sorbet::Private::Static::Void, $14: B): # outerLoops: 2 -> (NilClass ? bb9 : bb7) # backedges # - bb6(rubyRegionId=2) -bb7[rubyRegionId=1, firstDead=3](: B, $5: Sorbet::Private::Static::Void, $6: Use, $14: Sorbet::Private::Static::Void, $15: B): +bb7[rubyRegionId=1, firstDead=3](: B, $5: Sorbet::Private::Static::Void, $6: Use, $13: Sorbet::Private::Static::Void, $14: B): # outerLoops: 1 - $8: Sorbet::Private::Static::Void = Solve<$14, only_on_B> - : B = $15 - $22: T.noreturn = blockreturn $8: Sorbet::Private::Static::Void + $7: Sorbet::Private::Static::Void = Solve<$13, only_on_B> + : B = $14 + $20: T.noreturn = blockreturn $7: Sorbet::Private::Static::Void -> bb2 # backedges # - bb6(rubyRegionId=2) -bb9[rubyRegionId=2, firstDead=4](: B, $5: Sorbet::Private::Static::Void, $6: Use, $14: Sorbet::Private::Static::Void, $15: B): +bb9[rubyRegionId=2, firstDead=4](: B, $5: Sorbet::Private::Static::Void, $6: Use, $13: Sorbet::Private::Static::Void, $14: B): # outerLoops: 2 : C = loadSelf(only_on_B) - $18: T.untyped = : C.only_on_B() - $17: T.untyped = : C.only_on_C() - $21: T.noreturn = blockreturn $17: T.untyped + $16: T.untyped = : C.only_on_B() + $15: T.untyped = : C.only_on_C() + $19: T.noreturn = blockreturn $15: T.untyped -> bb6 } diff --git a/test/testdata/infer/result.rb b/test/testdata/infer/result.rb index 371e0fde9e..f946d10616 100644 --- a/test/testdata/infer/result.rb +++ b/test/testdata/infer/result.rb @@ -5,11 +5,25 @@ module Result extend T::Sig extend T::Generic sealed! + abstract! OkType = type_member(:out) ErrType = type_member(:out) + sig do + abstract + .type_parameters(:Ok, :Err) + .params( + blk: T.proc.params(arg0: OkType) + .returns(Result[T.type_parameter(:Ok), T.type_parameter(:Err)]) + ) + .returns(Result[T.type_parameter(:Ok), T.any(ErrType, T.type_parameter(:Err))]) + end + def and_then(&blk) + end + class Ok < T::Struct + extend T::Sig extend T::Generic include Result @@ -17,9 +31,23 @@ class Ok < T::Struct ErrType = type_member {{fixed: T.noreturn}} prop :val, OkType + + sig do + override + .type_parameters(:Ok, :Err) + .params( + blk: T.proc.params(arg0: OkType) + .returns(Result[T.type_parameter(:Ok), T.type_parameter(:Err)]) + ) + .returns(Result[T.type_parameter(:Ok), T.any(ErrType, T.type_parameter(:Err))]) + end + def and_then(&blk) + yield self.val + end end class Err < T::Struct + extend T::Sig extend T::Generic include Result @@ -27,13 +55,26 @@ class Err < T::Struct ErrType = type_member prop :error, ErrType + + sig do + override + .type_parameters(:Ok, :Err) + .params( + blk: T.proc.params(arg0: OkType) + .returns(Result[T.type_parameter(:Ok), T.type_parameter(:Err)]) + ) + .returns(Result[T.type_parameter(:Ok), T.any(ErrType, T.type_parameter(:Err))]) + end + def and_then(&blk) + self + end end end class SomethingElse; end sig {params(res: Result[Integer, TypeError]).void} -def example(res) +def example1(res) case res when Result::Ok T.reveal_type(res) # error: `Result::Ok[Integer]` @@ -51,3 +92,26 @@ def example(res) T.absurd(res) end end + +sig {params(res: Result[Integer, TypeError]).void} +def example2(res) + res1 = res.and_then do |arg0| + T.reveal_type(arg0) # error: `Integer` + ok = Result::Ok[String].new(val: arg0.to_s) + T.reveal_type(ok) # error: `Result::Ok[String]` + ok + end + T.reveal_type(res1) # error: `Result[String, TypeError]` + + res2 = res.and_then do |arg0| + T.reveal_type(arg0) # error: `Integer` + new_res = if arg0.even? + Result::Err[ArgumentError].new(error: ArgumentError.new("Don't give an even number")) + else + Result::Ok[String].new(val: arg0.to_s) + end + T.reveal_type(new_res) # error: `T.any(Result::Err[ArgumentError], Result::Ok[String])` + new_res + end + T.reveal_type(res2) # error: `Result[String, T.any(TypeError, ArgumentError)]` +end diff --git a/test/testdata/infer/ruby3_keyword_args.rb b/test/testdata/infer/ruby3_keyword_args.rb index 8d26be7a9a..c9387d3b01 100644 --- a/test/testdata/infer/ruby3_keyword_args.rb +++ b/test/testdata/infer/ruby3_keyword_args.rb @@ -11,3 +11,19 @@ def takes_kwargs(x, y:) takes_kwargs(99, arghash) # error: Keyword argument hash without `**` is deprecated takes_kwargs(99, **arghash) + +sig do + params( + name: String, + tags: T::Hash[T.untyped, T.untyped], + x: Integer, + blk: T.proc.returns(String) + ) + .returns(String) +end +def foo(name, tags: {}, x: 42, &blk) + yield +end + +blk = Proc.new +foo("", tags: {}, x: 1, &blk) diff --git a/test/testdata/infer/self_type.rb.cfg-text.exp b/test/testdata/infer/self_type.rb.cfg-text.exp index d6499667c6..d43b401fd9 100644 --- a/test/testdata/infer/self_type.rb.cfg-text.exp +++ b/test/testdata/infer/self_type.rb.cfg-text.exp @@ -83,8 +83,8 @@ bb2[rubyRegionId=0, firstDead=-1](: T.class_of(), a: T.all(Generic[S # - bb2(rubyRegionId=0) bb4[rubyRegionId=0, firstDead=-1](: T.class_of()): $88: T.class_of(Sorbet::Private::Static) = alias - $90: T.class_of(Array) = alias - $86: Sorbet::Private::Static::Void = $88: T.class_of(Sorbet::Private::Static).keep_for_ide($90: T.class_of(Array)) + $90: T.class_of(Array)[T::Array[T.untyped]] = alias + $86: Sorbet::Private::Static::Void = $88: T.class_of(Sorbet::Private::Static).keep_for_ide($90: T.class_of(Array)[T::Array[T.untyped]]) $94: T.class_of(Integer) = alias $96: T.class_of(Integer) = alias $97: T.class_of() = alias > @@ -181,9 +181,9 @@ bb2[rubyRegionId=1, firstDead=-1](: T.class_of(Parent), $7: Sorbet::Private::Static::Void, $8: T.class_of(Parent)): $3: Sorbet::Private::Static::Void = Solve<$7, sig> : T.class_of(Parent) = $8 - $19: T.class_of(T::Sig) = alias - $21: T.class_of(T) = alias - $16: T.class_of(Parent) = : T.class_of(Parent).extend($19: T.class_of(T::Sig)) + $18: T.class_of(T::Sig) = alias + $20: T.class_of(T) = alias + $15: T.class_of(Parent) = : T.class_of(Parent).extend($18: T.class_of(T::Sig)) : T.noreturn = return $2: NilClass -> bb1 @@ -192,10 +192,10 @@ bb3[rubyRegionId=0, firstDead=6]($7: Sorbet::Private::Stati bb5[rubyRegionId=1, firstDead=5](: T.class_of(Parent), $7: Sorbet::Private::Static::Void, $8: T.class_of(Parent)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $14: T.class_of(T) = alias - $12: Runtime object representing type: T.untyped = $14: T.class_of(T).self_type() - $10: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.returns($12: Runtime object representing type: T.untyped) - $15: T.noreturn = blockreturn $10: T::Private::Methods::DeclBuilder + $13: T.class_of(T) = alias + $11: Runtime object representing type: T.untyped = $13: T.class_of(T).self_type() + $9: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.returns($11: Runtime object representing type: T.untyped) + $14: T.noreturn = blockreturn $9: T::Private::Methods::DeclBuilder -> bb2 } @@ -236,7 +236,7 @@ bb1[rubyRegionId=0, firstDead=-1](): method ::# { bb0[rubyRegionId=0, firstDead=-1](): - $26: Runtime object representing type: Generic::TM = alias + $25: Runtime object representing type: Generic::TM = alias : T.class_of(Generic) = cast(: NilClass, T.class_of(Generic)); $5: T.class_of(Sorbet::Private::Static) = alias $7: Sorbet::Private::Static::Void = $5: T.class_of(Sorbet::Private::Static).sig(: T.class_of(Generic)) @@ -260,10 +260,10 @@ bb2[rubyRegionId=1, firstDead=-1](: T.class_of(Generic), $7: Sorbet::Private::Static::Void, $8: T.class_of(Generic)): $3: Sorbet::Private::Static::Void = Solve<$7, sig> : T.class_of(Generic) = $8 - $22: T.class_of(T::Generic) = alias - $24: T.class_of(T) = alias - $19: T.class_of(Generic) = : T.class_of(Generic).extend($22: T.class_of(T::Generic)) - $26: T::Types::TypeMember = : T.class_of(Generic).type_member() + $21: T.class_of(T::Generic) = alias + $23: T.class_of(T) = alias + $18: T.class_of(Generic) = : T.class_of(Generic).extend($21: T.class_of(T::Generic)) + $25: T::Types::TypeMember = : T.class_of(Generic).type_member() : T.noreturn = return $2: NilClass -> bb1 @@ -272,12 +272,12 @@ bb3[rubyRegionId=0, firstDead=7]($7: Sorbet::Private::Stati bb5[rubyRegionId=1, firstDead=7](: T.class_of(Generic), $7: Sorbet::Private::Static::Void, $8: T.class_of(Generic)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $14: T.class_of(Generic) = alias - $17: T.class_of(T) = alias - $15: Runtime object representing type: T.untyped = $17: T.class_of(T).self_type() - $12: Runtime object representing type: Generic[T.untyped] = $14: T.class_of(Generic).[]($15: Runtime object representing type: T.untyped) - $10: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.returns($12: Runtime object representing type: Generic[T.untyped]) - $18: T.noreturn = blockreturn $10: T::Private::Methods::DeclBuilder + $13: T.class_of(Generic) = alias + $16: T.class_of(T) = alias + $14: Runtime object representing type: T.untyped = $16: T.class_of(T).self_type() + $11: Runtime object representing type: Generic[T.untyped] = $13: T.class_of(Generic).[]($14: Runtime object representing type: T.untyped) + $9: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.returns($11: Runtime object representing type: Generic[T.untyped]) + $17: T.noreturn = blockreturn $9: T::Private::Methods::DeclBuilder -> bb2 } @@ -337,9 +337,9 @@ bb2[rubyRegionId=1, firstDead=-1](: T.class_of(Array), $7: Sorbet::Private::Static::Void, $8: T.class_of(Array)): $3: Sorbet::Private::Static::Void = Solve<$7, sig> : T.class_of(Array) = $8 - $19: T.class_of(T::Sig) = alias - $21: T.class_of(T) = alias - $16: T.class_of(Array) = : T.class_of(Array).extend($19: T.class_of(T::Sig)) + $18: T.class_of(T::Sig) = alias + $20: T.class_of(T) = alias + $15: T.class_of(Array) = : T.class_of(Array).extend($18: T.class_of(T::Sig)) : T.noreturn = return $2: NilClass -> bb1 @@ -348,10 +348,10 @@ bb3[rubyRegionId=0, firstDead=6]($7: Sorbet::Private::Stati bb5[rubyRegionId=1, firstDead=5](: T.class_of(Array), $7: Sorbet::Private::Static::Void, $8: T.class_of(Array)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $14: T.class_of(T) = alias - $12: Runtime object representing type: T.untyped = $14: T.class_of(T).self_type() - $10: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.returns($12: Runtime object representing type: T.untyped) - $15: T.noreturn = blockreturn $10: T::Private::Methods::DeclBuilder + $13: T.class_of(T) = alias + $11: Runtime object representing type: T.untyped = $13: T.class_of(T).self_type() + $9: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.returns($11: Runtime object representing type: T.untyped) + $14: T.noreturn = blockreturn $9: T::Private::Methods::DeclBuilder -> bb2 } @@ -411,9 +411,9 @@ bb2[rubyRegionId=1, firstDead=-1](: T.class_of(B), $7 bb3[rubyRegionId=0, firstDead=6]($7: Sorbet::Private::Static::Void, $8: T.class_of(B)): $3: Sorbet::Private::Static::Void = Solve<$7, sig> : T.class_of(B) = $8 - $19: T.class_of(T::Sig) = alias - $21: T.class_of(T) = alias - $16: T.class_of(B) = : T.class_of(B).extend($19: T.class_of(T::Sig)) + $18: T.class_of(T::Sig) = alias + $20: T.class_of(T) = alias + $15: T.class_of(B) = : T.class_of(B).extend($18: T.class_of(T::Sig)) : T.noreturn = return $2: NilClass -> bb1 @@ -422,10 +422,10 @@ bb3[rubyRegionId=0, firstDead=6]($7: Sorbet::Private::Stati bb5[rubyRegionId=1, firstDead=5](: T.class_of(B), $7: Sorbet::Private::Static::Void, $8: T.class_of(B)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $14: T.class_of(T) = alias - $12: Runtime object representing type: T.untyped = $14: T.class_of(T).self_type() - $10: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.returns($12: Runtime object representing type: T.untyped) - $15: T.noreturn = blockreturn $10: T::Private::Methods::DeclBuilder + $13: T.class_of(T) = alias + $11: Runtime object representing type: T.untyped = $13: T.class_of(T).self_type() + $9: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.returns($11: Runtime object representing type: T.untyped) + $14: T.noreturn = blockreturn $9: T::Private::Methods::DeclBuilder -> bb2 } diff --git a/test/testdata/infer/strong_block_return.rb b/test/testdata/infer/strong_block_return.rb new file mode 100644 index 0000000000..8f7d7744e6 --- /dev/null +++ b/test/testdata/infer/strong_block_return.rb @@ -0,0 +1,36 @@ +# typed: strong +extend T::Sig + +sig {params(blk: T.proc.void).void} +def example1(&blk) +end + +sig {params(blk: T.proc.returns(Integer)).void} +def example2(&blk) +end + +sig do + type_parameters(:U) + .params(blk: T.proc.returns(T.type_parameter(:U))) + .returns(T.type_parameter(:U)) +end +def example3(&blk) + yield +end + +example1 do + T.unsafe(nil) +end + +example2 do + T.unsafe(nil) +# ^^^^^^^^^^^^^ error: Value returned from block is `T.untyped` +end + +# I think that ideally, we would not report the error inside the block, and +# only report an error when you try to use `res` +res = example3 do + T.unsafe(nil) +# ^^^^^^^^^^^^^ error: Value returned from block is `T.untyped` +end +T.reveal_type(res) # error: `T.untyped` diff --git a/test/testdata/infer/strong_rescue.rb b/test/testdata/infer/strong_rescue.rb new file mode 100644 index 0000000000..3943f5c5ac --- /dev/null +++ b/test/testdata/infer/strong_rescue.rb @@ -0,0 +1,67 @@ +# typed: strong +extend T::Sig + +# Sorbet's implementation of exceptions currently heavily relies on T.untyped +# Fixing that involves more work than I have time for, so let's at least just +# not spam an entire method with untyped squiggles. + +sig {void} +def example1 + begin + rescue TypeError => e +# ^^^^^^ error: Conditional branch on `T.untyped` +# ^^^^^^ error: Conditional branch on `T.untyped` + # ^^^^^^^^^ error: Argument passed to parameter `other` is `T.untyped` + T.reveal_type(e) # error: `TypeError` + else + ensure + end +end + +sig {void} +def example2 + begin + rescue; puts("") +# ^^^^^^ error: Conditional branch on `T.untyped` +# ^^^^^^ error: Conditional branch on `T.untyped` +# ^^^^^^ error: Argument passed to parameter `other` is `T.untyped` + ensure + end +end + +sig {void} +def example3 + begin + rescue => e; T.reveal_type(e) +# ^^^^^^ error: Conditional branch on `T.untyped` +# ^^^^^^ error: Conditional branch on `T.untyped` + # ^ error: Argument passed to parameter `other` is `T.untyped` + # ^^^^^^^^^^^^^^^^ error: `StandardError` + ensure + end +end + +sig {void} +def example4 + begin + puts("here we are") + rescue; puts("") +# ^^^^^^ error: Conditional branch on `T.untyped` +# ^^^^^^ error: Conditional branch on `T.untyped` +# ^^^^^^ error: Argument passed to parameter `other` is `T.untyped` + ensure + end +end + +sig {void} +def example5 + begin + rescue Exception => e +# ^^^^^^ error: Conditional branch on `T.untyped` +# ^^^^^^ error: Conditional branch on `T.untyped` + # ^^^^^^^^^ error: Argument passed to parameter `other` is `T.untyped` + T.reveal_type(e) # error: `Exception` + else + ensure + end +end diff --git a/test/testdata/infer/strong_splat.rb b/test/testdata/infer/strong_splat.rb new file mode 100644 index 0000000000..34dbeea9a0 --- /dev/null +++ b/test/testdata/infer/strong_splat.rb @@ -0,0 +1,24 @@ +# typed: strong +extend T::Sig + +sig {void} +def example + T.unsafe(Kernel).exec(["pay", "pay"], *ARGV) +# ^^^^^^^^^^^^^^^^ error: Call to method `exec` on `T.untyped` + + Kernel.exec(*T.unsafe([])) +# ^ error: Call to method `to_a` on `T.untyped` +# ^ error: Call to method `exec` with `T.untyped` splat arguments + + f = ->() {} + T.unsafe(Kernel).exec(["pay", "pay"], &f) +# ^^^^^^^^^^^^^^^^ error: Call to method `exec` on `T.untyped` + + f = ->() {} + T.unsafe(Kernel).exec(["pay", "pay"], *ARGV, &f) +# ^^^^^^^^^^^^^^^^ error: Call to method `exec` on `T.untyped` + + Kernel.exec(*T.unsafe([]), &f) +# ^ error: Call to method `to_a` on `T.untyped` +# ^ error: Call to method `exec` with `T.untyped` splat arguments +end diff --git a/test/testdata/infer/suggest_attached_class.rb b/test/testdata/infer/suggest_attached_class.rb new file mode 100644 index 0000000000..2a6963e760 --- /dev/null +++ b/test/testdata/infer/suggest_attached_class.rb @@ -0,0 +1,7 @@ +# typed: strict + +class Foo + def self.generate # error: does not have a `sig` + self.new + end +end diff --git a/test/testdata/infer/suggest_attached_class.rb.autocorrects.exp b/test/testdata/infer/suggest_attached_class.rb.autocorrects.exp new file mode 100644 index 0000000000..28e847631c --- /dev/null +++ b/test/testdata/infer/suggest_attached_class.rb.autocorrects.exp @@ -0,0 +1,11 @@ +# -- test/testdata/infer/suggest_attached_class.rb -- +# typed: strict + +class Foo + extend T::Sig + sig { returns(T.attached_class) } + def self.generate # error: does not have a `sig` + self.new + end +end +# ------------------------------ diff --git a/test/testdata/infer/suggest_useless_sig.rb b/test/testdata/infer/suggest_useless_sig.rb new file mode 100644 index 0000000000..1cef980827 --- /dev/null +++ b/test/testdata/infer/suggest_useless_sig.rb @@ -0,0 +1,6 @@ +# typed: strict +# enable-suggest-unsafe: true + +def foo(x) # error: does not have a `sig` + T.unsafe(nil) +end diff --git a/test/testdata/infer/suggest_useless_sig.rb.autocorrects.exp b/test/testdata/infer/suggest_useless_sig.rb.autocorrects.exp new file mode 100644 index 0000000000..6690c99602 --- /dev/null +++ b/test/testdata/infer/suggest_useless_sig.rb.autocorrects.exp @@ -0,0 +1,10 @@ +# -- test/testdata/infer/suggest_useless_sig.rb -- +# typed: strict +# enable-suggest-unsafe: true + +extend T::Sig +sig { params(x: T.untyped).returns(T.untyped) } +def foo(x) # error: does not have a `sig` + T.unsafe(nil) +end +# ------------------------------ diff --git a/test/testdata/infer/t_class.rb b/test/testdata/infer/t_class.rb new file mode 100644 index 0000000000..d920cd18f1 --- /dev/null +++ b/test/testdata/infer/t_class.rb @@ -0,0 +1,7 @@ +# typed: true + +class Parent; end + +x = T::Class[Parent].new +# ^^^ error: mistakes a type for a value +T.reveal_type(x) # error: `T.untyped` diff --git a/test/testdata/infer/t_class_any.rb b/test/testdata/infer/t_class_any.rb new file mode 100644 index 0000000000..a0dbbc0dce --- /dev/null +++ b/test/testdata/infer/t_class_any.rb @@ -0,0 +1,36 @@ +# typed: true +extend T::Sig + +class A; end +class B; end + +sig do + type_parameters(:Instance) + .params(klass: T::Class[T.type_parameter(:Instance)]) + .returns(T.type_parameter(:Instance)) +end +def instantiate_class(klass) + instance = klass.new + puts("Instantiated: #{instance}") + instance +end + +sig {params(klass: T::Class[T.any(A, B)]).void} +def instantiate_class_a_b(klass) + instance = klass.new + T.reveal_type(instance) # error: `T.any(A, B)` + case instance + when A + when B + else + T.absurd(instance) + end +end + + +sig {params(klass: T.any(T.class_of(A), T.class_of(B))).void} +def example(klass) + x = instantiate_class(klass) + T.reveal_type(x) # error: `T.any(A, B)` + x +end diff --git a/test/testdata/infer/top.rb b/test/testdata/infer/top.rb new file mode 100644 index 0000000000..464a01b94d --- /dev/null +++ b/test/testdata/infer/top.rb @@ -0,0 +1,24 @@ +# typed: strict +extend T::Sig + +sig {params(x: T.anything).returns(T.anything)} +def example(x) + x.nil? + # ^^^^ error: Method `nil?` does not exist on `T.anything` + T.reveal_type(x) # error: `T.anything` + x +end + +sig do + type_parameters(:U) + .params(x: T.type_parameter(:U)) + .returns(T.type_parameter(:U)) +end +def id(x) + res = example(x) # this is ok + res +# ^^^ error: Expected `T.type_parameter(:U) (of Object#id)` but found `T.anything` for method result type +end + +xs = T::Array[T.anything].new +T.reveal_type(xs) # error: `T::Array[T.anything]` diff --git a/test/testdata/infer/transitive.rb.cfg-text.exp b/test/testdata/infer/transitive.rb.cfg-text.exp index 02d85226f0..1018ef9340 100644 --- a/test/testdata/infer/transitive.rb.cfg-text.exp +++ b/test/testdata/infer/transitive.rb.cfg-text.exp @@ -61,9 +61,9 @@ bb2[rubyRegionId=1, firstDead=-1](: T.class_of(A), $7 bb3[rubyRegionId=0, firstDead=6]($7: Sorbet::Private::Static::Void, $8: T.class_of(A)): $3: Sorbet::Private::Static::Void = Solve<$7, sig> : T.class_of(A) = $8 - $18: T.class_of(T::Sig) = alias - $20: T.class_of(T) = alias - $15: T.class_of(A) = : T.class_of(A).extend($18: T.class_of(T::Sig)) + $17: T.class_of(T::Sig) = alias + $19: T.class_of(T) = alias + $14: T.class_of(A) = : T.class_of(A).extend($17: T.class_of(T::Sig)) : T.noreturn = return $2: NilClass -> bb1 @@ -72,9 +72,9 @@ bb3[rubyRegionId=0, firstDead=6]($7: Sorbet::Private::Stati bb5[rubyRegionId=1, firstDead=4](: T.class_of(A), $7: Sorbet::Private::Static::Void, $8: T.class_of(A)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $13: T.class_of(Integer) = alias - $10: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.returns($13: T.class_of(Integer)) - $14: T.noreturn = blockreturn $10: T::Private::Methods::DeclBuilder + $12: T.class_of(Integer) = alias + $9: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.returns($12: T.class_of(Integer)) + $13: T.noreturn = blockreturn $9: T::Private::Methods::DeclBuilder -> bb2 } @@ -120,9 +120,9 @@ bb2[rubyRegionId=1, firstDead=-1](: T.class_of(Bar), bb3[rubyRegionId=0, firstDead=6]($7: Sorbet::Private::Static::Void, $8: T.class_of(Bar)): $3: Sorbet::Private::Static::Void = Solve<$7, sig> : T.class_of(Bar) = $8 - $22: T.class_of(T::Sig) = alias - $24: T.class_of(T) = alias - $19: T.class_of(Bar) = : T.class_of(Bar).extend($22: T.class_of(T::Sig)) + $21: T.class_of(T::Sig) = alias + $23: T.class_of(T) = alias + $18: T.class_of(Bar) = : T.class_of(Bar).extend($21: T.class_of(T::Sig)) : T.noreturn = return $2: NilClass -> bb1 @@ -131,12 +131,12 @@ bb3[rubyRegionId=0, firstDead=6]($7: Sorbet::Private::Stati bb5[rubyRegionId=1, firstDead=7](: T.class_of(Bar), $7: Sorbet::Private::Static::Void, $8: T.class_of(Bar)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $13: Symbol(:arg) = :arg - $15: T.class_of(Integer) = alias - $11: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($13: Symbol(:arg), $15: T.class_of(Integer)) - $17: T.class_of(Integer) = alias - $10: T::Private::Methods::DeclBuilder = $11: T::Private::Methods::DeclBuilder.returns($17: T.class_of(Integer)) - $18: T.noreturn = blockreturn $10: T::Private::Methods::DeclBuilder + $12: Symbol(:arg) = :arg + $14: T.class_of(Integer) = alias + $10: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($12: Symbol(:arg), $14: T.class_of(Integer)) + $16: T.class_of(Integer) = alias + $9: T::Private::Methods::DeclBuilder = $10: T::Private::Methods::DeclBuilder.returns($16: T.class_of(Integer)) + $17: T.noreturn = blockreturn $9: T::Private::Methods::DeclBuilder -> bb2 } diff --git a/test/testdata/infer/untyped.rb b/test/testdata/infer/untyped.rb new file mode 100644 index 0000000000..665ab90361 --- /dev/null +++ b/test/testdata/infer/untyped.rb @@ -0,0 +1,52 @@ +# typed: strong +class Module; include T::Sig; end + +class A + sig {void} + def foo; end +end + +sig {returns(T.untyped)} +def returns_untyped + A.new +end + +sig {params(x: Integer).void} +def takes_integer(x) +end + +if T.unsafe(nil) + #^^^^^^^^^^^^^ error: Conditional branch on `T.untyped` + x = returns_untyped +else + x = 0 +end + +x.foo +# ^^^ error: Call to method `foo` on `T.untyped` + +y = T.let(x, A) +y.foo + +takes_integer( + T.unsafe(0) +# ^^^^^^^^^^^ error: Argument passed to parameter `x` is `T.untyped` +) + +sig {params(blk: T.proc.void).void} +def example(&blk) +end +f = ->(){} +example(&f) + +class Parent + def foo # error: does not have a `sig` + end +end + +class Child < Parent + def foo # error: does not have a `sig` + super + nil + end +end diff --git a/test/testdata/intrinsics/kernel_raise.rb b/test/testdata/intrinsics/kernel_raise.rb new file mode 100644 index 0000000000..1e92faf764 --- /dev/null +++ b/test/testdata/intrinsics/kernel_raise.rb @@ -0,0 +1,103 @@ +# typed: true +extend T::Sig + +class MyError < StandardError + extend T::Sig + + sig { params(input: String).void } + def initialize(input:) + @input = input + end +end + +class MultipleRequired < StandardError + extend T::Sig + + sig { params(input: String, another: Integer).void } + def initialize(input, another) + end +end + +class DefinesToException + extend T::Sig + + sig { params(arg0: String).void } + def initialize(arg0) + end + + sig { returns(Exception) } + def exception + TypeError.new("hello") + end +end + +class DoesNotDefineToException + extend T::Sig + + sig { params(arg0: String).void } + def initialize(arg0) + end +end + +sig {params(cls: T.any(T.class_of(TypeError), T.class_of(MyError))).void} +def example(cls) + 0.times do + raise MyError + # ^ error: Missing required keyword argument `input` for method `MyError#initialize` + end + + 0.times do + raise cls + # ^ error: Missing required keyword argument `input` for method `MyError#initialize` + end + + 0.times do + raise MyError, "arg" + # ^^^^^ error: Missing required keyword argument `input` for method `MyError#initialize` + # ^^^^^ error: Too many positional arguments provided for method `MyError#initialize`. Expected: `0`, got: `1` + end + + 0.times do + raise cls, "arg" + # ^^^^^ error: Missing required keyword argument `input` for method `MyError#initialize` + # ^^^^^ error: Too many positional arguments provided for method `MyError#initialize`. Expected: `0`, got: `1` + end + + 0.times do + raise MyError.new + # ^ error: Missing required keyword argument `input` for method `MyError#initialize` + end + + 0.times do + raise T.unsafe(MyError) + end + + 0.times do + # If this did not define `exception`, it would dispatch to Exception.exception, + # which forwards to `new` and then `initialize`, which requires a single pos arg + # that isn't provided here. + raise DefinesToException + end + + 0.times do + # This doesn't define `exception`, but we don't currently detect that, (and + # probably can't, because of subtyping), opting instead to skip the intrinsic + raise DoesNotDefineToException + end + + 0.times do + raise TypeError, "one", "two" + # ^^^^^ error: Expected `T.nilable(T::Array[String])` but found `String("two")` for argument `arg2` + end + + 0.times do + raise MultipleRequired, "one", "two" + # ^^^^^ error: Expected `T.nilable(T::Array[String])` but found `String("two")` for argument `arg2` + # ^^^^^ error: Not enough arguments provided for method `MultipleRequired#initialize`. Expected: `2`, got: `1` + end + + 0.times do + fail MyError + # ^ error: Missing required keyword argument `input` for method `MyError#initialize` + end +end diff --git a/test/testdata/intrinsics/kernel_raise.rb.autocorrects.exp b/test/testdata/intrinsics/kernel_raise.rb.autocorrects.exp new file mode 100644 index 0000000000..a95aab0afe --- /dev/null +++ b/test/testdata/intrinsics/kernel_raise.rb.autocorrects.exp @@ -0,0 +1,105 @@ +# -- test/testdata/intrinsics/kernel_raise.rb -- +# typed: true +extend T::Sig + +class MyError < StandardError + extend T::Sig + + sig { params(input: String).void } + def initialize(input:) + @input = input + end +end + +class MultipleRequired < StandardError + extend T::Sig + + sig { params(input: String, another: Integer).void } + def initialize(input, another) + end +end + +class DefinesToException + extend T::Sig + + sig { params(arg0: String).void } + def initialize(arg0) + end + + sig { returns(Exception) } + def exception + TypeError.new("hello") + end +end + +class DoesNotDefineToException + extend T::Sig + + sig { params(arg0: String).void } + def initialize(arg0) + end +end + +sig {params(cls: T.any(T.class_of(TypeError), T.class_of(MyError))).void} +def example(cls) + 0.times do + raise MyError + # ^ error: Missing required keyword argument `input` for method `MyError#initialize` + end + + 0.times do + raise cls + # ^ error: Missing required keyword argument `input` for method `MyError#initialize` + end + + 0.times do + raise MyError + # ^^^^^ error: Missing required keyword argument `input` for method `MyError#initialize` + # ^^^^^ error: Too many positional arguments provided for method `MyError#initialize`. Expected: `0`, got: `1` + end + + 0.times do + raise cls + # ^^^^^ error: Missing required keyword argument `input` for method `MyError#initialize` + # ^^^^^ error: Too many positional arguments provided for method `MyError#initialize`. Expected: `0`, got: `1` + end + + 0.times do + raise MyError.new + # ^ error: Missing required keyword argument `input` for method `MyError#initialize` + end + + 0.times do + raise T.unsafe(MyError) + end + + 0.times do + # If this did not define `exception`, it would dispatch to Exception.exception, + # which forwards to `new` and then `initialize`, which requires a single pos arg + # that isn't provided here. + raise DefinesToException + end + + 0.times do + # This doesn't define `exception`, but we don't currently detect that, (and + # probably can't, because of subtyping), opting instead to skip the intrinsic + raise DoesNotDefineToException + end + + 0.times do + raise TypeError, "one", "two" + # ^^^^^ error: Expected `T.nilable(T::Array[String])` but found `String("two")` for argument `arg2` + end + + 0.times do + raise MultipleRequired, "one", "two" + # ^^^^^ error: Expected `T.nilable(T::Array[String])` but found `String("two")` for argument `arg2` + # ^^^^^ error: Not enough arguments provided for method `MultipleRequired#initialize`. Expected: `2`, got: `1` + end + + 0.times do + fail MyError + # ^ error: Missing required keyword argument `input` for method `MyError#initialize` + end +end +# ------------------------------ diff --git a/test/testdata/lsp/ambiguous_definitions.1.rbupdate b/test/testdata/lsp/ambiguous_definitions.1.rbupdate new file mode 100644 index 0000000000..bdfbd7ae25 --- /dev/null +++ b/test/testdata/lsp/ambiguous_definitions.1.rbupdate @@ -0,0 +1,10 @@ +# typed: strict +# assert-fast-path: ambiguous_definitions.rb + +module Opus + module Foo + module Foo::Bar # error: Definition of `Bar` is ambiguous + end + end +end + diff --git a/test/testdata/lsp/ambiguous_definitions.rb b/test/testdata/lsp/ambiguous_definitions.rb new file mode 100644 index 0000000000..77970212cb --- /dev/null +++ b/test/testdata/lsp/ambiguous_definitions.rb @@ -0,0 +1,9 @@ +# typed: strict + +# no-op comment +module Opus + module Foo + module Foo::Bar # error: Definition of `Bar` is ambiguous + end + end +end diff --git a/test/testdata/lsp/bad_alias_in_result_type.rb b/test/testdata/lsp/bad_alias_in_result_type.rb index 5cf3751dd3..2fd5f9b5c5 100644 --- a/test/testdata/lsp/bad_alias_in_result_type.rb +++ b/test/testdata/lsp/bad_alias_in_result_type.rb @@ -3,6 +3,6 @@ A = Does::Not::Exist # error: Unable to resolve constant -sig {returns(A)} # error: Constant `A` is not a class or type alias +sig {returns(A)} def example end diff --git a/test/testdata/lsp/code_actions/convert_to_singleton_method/block_pass.A.rbedited b/test/testdata/lsp/code_actions/convert_to_singleton_method/block_pass.A.rbedited new file mode 100644 index 0000000000..64cd9fc654 --- /dev/null +++ b/test/testdata/lsp/code_actions/convert_to_singleton_method/block_pass.A.rbedited @@ -0,0 +1,16 @@ +# typed: true +# selective-apply-code-action: refactor.rewrite +extend T::Sig + +class A + extend T::Sig + + def self.takes_block(this, x, &blk) + # ^ apply-code-action: [A] Convert to singleton class method (best effort) + puts "Hello, peter." + end +end + +# Our methods are hard. +f = ->(){} +A.new.takes_block(0, &f) diff --git a/test/testdata/lsp/code_actions/convert_to_singleton_method/block_pass.rb b/test/testdata/lsp/code_actions/convert_to_singleton_method/block_pass.rb new file mode 100644 index 0000000000..abd22d9d4a --- /dev/null +++ b/test/testdata/lsp/code_actions/convert_to_singleton_method/block_pass.rb @@ -0,0 +1,16 @@ +# typed: true +# selective-apply-code-action: refactor.rewrite +extend T::Sig + +class A + extend T::Sig + + def takes_block(x, &blk) + # ^ apply-code-action: [A] Convert to singleton class method (best effort) + puts "Hello, peter." + end +end + +# Our methods are hard. +f = ->(){} +A.new.takes_block(0, &f) diff --git a/test/testdata/lsp/code_actions/convert_to_singleton_method/no_sig.A.rbedited b/test/testdata/lsp/code_actions/convert_to_singleton_method/no_sig.A.rbedited new file mode 100644 index 0000000000..ac16bf21f9 --- /dev/null +++ b/test/testdata/lsp/code_actions/convert_to_singleton_method/no_sig.A.rbedited @@ -0,0 +1,12 @@ +# typed: true +# selective-apply-code-action: refactor.rewrite +extend T::Sig + +class A + extend T::Sig + + def self.no_sig(this, x) + # ^ apply-code-action: [A] Convert to singleton class method (best effort) + puts "Hello, peter." + end +end diff --git a/test/testdata/lsp/code_actions/convert_to_singleton_method/no_sig.rb b/test/testdata/lsp/code_actions/convert_to_singleton_method/no_sig.rb new file mode 100644 index 0000000000..4706372ac5 --- /dev/null +++ b/test/testdata/lsp/code_actions/convert_to_singleton_method/no_sig.rb @@ -0,0 +1,12 @@ +# typed: true +# selective-apply-code-action: refactor.rewrite +extend T::Sig + +class A + extend T::Sig + + def no_sig(x) + # ^ apply-code-action: [A] Convert to singleton class method (best effort) + puts "Hello, peter." + end +end diff --git a/test/testdata/lsp/code_actions/convert_to_singleton_method/nullary.A.rbedited b/test/testdata/lsp/code_actions/convert_to_singleton_method/nullary.A.rbedited new file mode 100644 index 0000000000..25cc24d646 --- /dev/null +++ b/test/testdata/lsp/code_actions/convert_to_singleton_method/nullary.A.rbedited @@ -0,0 +1,37 @@ +# typed: true +# selective-apply-code-action: refactor.rewrite +extend T::Sig + +class A + extend T::Sig + + sig {params(this: A).void} + def self.nullary(this) + # ^ apply-code-action: [A] Convert to singleton class method (best effort) + puts "Hello, peter." + end + + def example + A.nullary(self) + A.nullary(self) + A.nullary(self) {} + A.nullary(self) {} + A.nullary(self) do + end + A.nullary(self) do + end + end + + sig {params(other: A).returns(T.self_type)} + def +(other); self; end +end + +A.nullary(A.new) do +end +A.nullary(A.new) {} +A.nullary(A.new) do +end + +(T.unsafe(A.new)).nullary + +A.nullary(A.new + A.new) diff --git a/test/testdata/lsp/code_actions/convert_to_singleton_method/nullary.rb b/test/testdata/lsp/code_actions/convert_to_singleton_method/nullary.rb new file mode 100644 index 0000000000..2ce0753b30 --- /dev/null +++ b/test/testdata/lsp/code_actions/convert_to_singleton_method/nullary.rb @@ -0,0 +1,37 @@ +# typed: true +# selective-apply-code-action: refactor.rewrite +extend T::Sig + +class A + extend T::Sig + + sig {void} + def nullary + # ^ apply-code-action: [A] Convert to singleton class method (best effort) + puts "Hello, peter." + end + + def example + nullary + nullary() + nullary {} + nullary() {} + nullary do + end + nullary() do + end + end + + sig {params(other: A).returns(T.self_type)} + def +(other); self; end +end + +A.new.nullary do +end +A.new.nullary {} +A.new.nullary() do +end + +(T.unsafe(A.new)).nullary + +(A.new + A.new).nullary diff --git a/test/testdata/lsp/code_actions/convert_to_singleton_method/unary.A.rbedited b/test/testdata/lsp/code_actions/convert_to_singleton_method/unary.A.rbedited new file mode 100644 index 0000000000..b47f38647e --- /dev/null +++ b/test/testdata/lsp/code_actions/convert_to_singleton_method/unary.A.rbedited @@ -0,0 +1,34 @@ +# typed: true +# selective-apply-code-action: refactor.rewrite +extend T::Sig + +class A + extend T::Sig + + sig {params(this: A, x: Integer).void} + def self.unary(this, x) + # ^ apply-code-action: [A] Convert to singleton class method (best effort) + puts "Hello, peter." + end + + def example + A.unary(self) # error: Not enough arguments provided + A.unary(self, 0) + A.unary(self, 0)) {} + A.unary(self, 0)) do + end + end + + sig {params(other: A).returns(T.self_type)} + def +(other); self; end +end + +A.unary(A.new) # error: Not enough arguments provided +A.unary(A.new, 0) +A.unary(A.new, 0) +A.unary(A.new, 0)) do +end + +(T.unsafe(A.new)).unary(0) + +A.unary(A.new + A.new, 0) diff --git a/test/testdata/lsp/code_actions/convert_to_singleton_method/unary.rb b/test/testdata/lsp/code_actions/convert_to_singleton_method/unary.rb new file mode 100644 index 0000000000..c8426ec636 --- /dev/null +++ b/test/testdata/lsp/code_actions/convert_to_singleton_method/unary.rb @@ -0,0 +1,36 @@ +# typed: true +# selective-apply-code-action: refactor.rewrite +extend T::Sig + +class A + extend T::Sig + + sig {params(x: Integer).void} + def unary(x) + # ^ apply-code-action: [A] Convert to singleton class method (best effort) + puts "Hello, peter." + end + + def example + unary # error: Not enough arguments provided + unary(0) + unary(0) {} + unary(0) do + end + end + + sig {params(other: A).returns(T.self_type)} + def +(other); self; end +end + +A.new.unary # error: Not enough arguments provided +A.new.unary(0) +A.new.unary( + 0 +) +A.new.unary(0) do +end + +(T.unsafe(A.new)).unary(0) + +(A.new + A.new).unary(0) diff --git a/test/testdata/lsp/code_actions/convert_to_singleton_method/wacky.rb b/test/testdata/lsp/code_actions/convert_to_singleton_method/wacky.rb new file mode 100644 index 0000000000..55d7ec7be3 --- /dev/null +++ b/test/testdata/lsp/code_actions/convert_to_singleton_method/wacky.rb @@ -0,0 +1,14 @@ +# typed: true +# selective-apply-code-action: refactor.rewrite +# assert-no-code-action: refactor.rewrite +extend T::Sig + +class A < T::Struct + extend T::Sig + + attr_reader :foo + # ^ apply-code-action: [A] Convert to singleton class method (best effort) + + prop :foo, Integer + # ^ apply-code-action: [A] Convert to singleton class method (best effort) +end diff --git a/test/testdata/lsp/code_actions/delete_unsafe.A.rbedited b/test/testdata/lsp/code_actions/delete_unsafe.A.rbedited new file mode 100644 index 0000000000..285829f5ec --- /dev/null +++ b/test/testdata/lsp/code_actions/delete_unsafe.A.rbedited @@ -0,0 +1,15 @@ +# typed: true +# selective-apply-code-action: refactor.rewrite + +0.even? +# ^ apply-code-action: [A] Delete T.unsafe + +T.unsafe( +# ^ apply-code-action: [B] Delete T.unsafe + 0 +).even? + +_foo = T + .unsafe(0) + # ^ apply-code-action: [C] Delete T.unsafe + .even? diff --git a/test/testdata/lsp/code_actions/delete_unsafe.B.rbedited b/test/testdata/lsp/code_actions/delete_unsafe.B.rbedited new file mode 100644 index 0000000000..329ab4f767 --- /dev/null +++ b/test/testdata/lsp/code_actions/delete_unsafe.B.rbedited @@ -0,0 +1,12 @@ +# typed: true +# selective-apply-code-action: refactor.rewrite + +T.unsafe(0).even? +# ^ apply-code-action: [A] Delete T.unsafe + +0.even? + +_foo = T + .unsafe(0) + # ^ apply-code-action: [C] Delete T.unsafe + .even? diff --git a/test/testdata/lsp/code_actions/delete_unsafe.C.rbedited b/test/testdata/lsp/code_actions/delete_unsafe.C.rbedited new file mode 100644 index 0000000000..08035c9a37 --- /dev/null +++ b/test/testdata/lsp/code_actions/delete_unsafe.C.rbedited @@ -0,0 +1,14 @@ +# typed: true +# selective-apply-code-action: refactor.rewrite + +T.unsafe(0).even? +# ^ apply-code-action: [A] Delete T.unsafe + +T.unsafe( +# ^ apply-code-action: [B] Delete T.unsafe + 0 +).even? + +_foo = 0 + # ^ apply-code-action: [C] Delete T.unsafe + .even? diff --git a/test/testdata/lsp/code_actions/delete_unsafe.rb b/test/testdata/lsp/code_actions/delete_unsafe.rb new file mode 100644 index 0000000000..17ee628d73 --- /dev/null +++ b/test/testdata/lsp/code_actions/delete_unsafe.rb @@ -0,0 +1,15 @@ +# typed: true +# selective-apply-code-action: refactor.rewrite + +T.unsafe(0).even? +# ^ apply-code-action: [A] Delete T.unsafe + +T.unsafe( +# ^ apply-code-action: [B] Delete T.unsafe + 0 +).even? + +_foo = T + .unsafe(0) + # ^ apply-code-action: [C] Delete T.unsafe + .even? diff --git a/test/testdata/lsp/completion/keywords.rb b/test/testdata/lsp/completion/keywords.rb index 59684ff336..4b7a04763b 100644 --- a/test/testdata/lsp/completion/keywords.rb +++ b/test/testdata/lsp/completion/keywords.rb @@ -44,7 +44,7 @@ d # error: does not exist #^ completion: def, defined?, do, ... -# `else` is more common--be sureit comes before `ensure` +# `else` is more common--be sure it comes before `ensure` els # error: does not exist # ^ completion: else, elsif diff --git a/test/testdata/lsp/completion/send_with_block.rb b/test/testdata/lsp/completion/send_with_block.rb new file mode 100644 index 0000000000..8506b76aa9 --- /dev/null +++ b/test/testdata/lsp/completion/send_with_block.rb @@ -0,0 +1,14 @@ +# typed: true + +class A + extend T::Sig + 1.times do end + # ^ completion: (nothing) + # end +end + +class B + extend T::Sig + sig do end # error-with-dupes: Malformed `sig` + # ^ completion: (nothing) +end diff --git a/test/testdata/lsp/constant_completion.rb b/test/testdata/lsp/constant_completion.rb new file mode 100644 index 0000000000..9b6e252b01 --- /dev/null +++ b/test/testdata/lsp/constant_completion.rb @@ -0,0 +1,13 @@ +# typed: true + +class A + module Namespace; end + Namespac # error: Unable to resolve constant `Namespac` + # ^ completion: Namespace +end + +class B + module Namespace; end + X = Namespac # error: Unable to resolve constant `Namespac` + # ^ completion: Namespace +end diff --git a/test/testdata/lsp/fast_path/alias_stub_module__1.1.rbupdate b/test/testdata/lsp/fast_path/alias_stub_module__1.1.rbupdate new file mode 100644 index 0000000000..4c93745960 --- /dev/null +++ b/test/testdata/lsp/fast_path/alias_stub_module__1.1.rbupdate @@ -0,0 +1,4 @@ +# typed: true +# assert-fast-path: alias_stub_module__1.rb,alias_stub_module__2.rb + +DoesNotExist = 1 diff --git a/test/testdata/lsp/fast_path/alias_stub_module__1.2.rbupdate b/test/testdata/lsp/fast_path/alias_stub_module__1.2.rbupdate new file mode 100644 index 0000000000..49f887c510 --- /dev/null +++ b/test/testdata/lsp/fast_path/alias_stub_module__1.2.rbupdate @@ -0,0 +1,4 @@ +# typed: true +# assert-slow-path: true + +class DoesNotExist; end diff --git a/test/testdata/lsp/fast_path/alias_stub_module__1.rb b/test/testdata/lsp/fast_path/alias_stub_module__1.rb new file mode 100644 index 0000000000..7244f843f9 --- /dev/null +++ b/test/testdata/lsp/fast_path/alias_stub_module__1.rb @@ -0,0 +1,3 @@ +# typed: true + +# DoesNotExist = 1 diff --git a/test/testdata/lsp/fast_path/alias_stub_module__2.1.rbupdate b/test/testdata/lsp/fast_path/alias_stub_module__2.1.rbupdate new file mode 100644 index 0000000000..c36558b38a --- /dev/null +++ b/test/testdata/lsp/fast_path/alias_stub_module__2.1.rbupdate @@ -0,0 +1,3 @@ +# typed: true +# exclude-from-file-update: true +A = DoesNotExist diff --git a/test/testdata/lsp/fast_path/alias_stub_module__2.2.rbupdate b/test/testdata/lsp/fast_path/alias_stub_module__2.2.rbupdate new file mode 100644 index 0000000000..c36558b38a --- /dev/null +++ b/test/testdata/lsp/fast_path/alias_stub_module__2.2.rbupdate @@ -0,0 +1,3 @@ +# typed: true +# exclude-from-file-update: true +A = DoesNotExist diff --git a/test/testdata/lsp/fast_path/alias_stub_module__2.rb b/test/testdata/lsp/fast_path/alias_stub_module__2.rb new file mode 100644 index 0000000000..fa111a778d --- /dev/null +++ b/test/testdata/lsp/fast_path/alias_stub_module__2.rb @@ -0,0 +1,3 @@ +# typed: true +# spacer for exclude-from-file-update +A = DoesNotExist # error: Unable to resolve constant diff --git a/test/testdata/lsp/fast_path/alias_stub_module__3.1.rbupdate b/test/testdata/lsp/fast_path/alias_stub_module__3.1.rbupdate new file mode 100644 index 0000000000..7e7f7cb473 --- /dev/null +++ b/test/testdata/lsp/fast_path/alias_stub_module__3.1.rbupdate @@ -0,0 +1,8 @@ +# typed: true +# exclude-from-file-update: true +extend T::Sig + +sig {params(x: A).void} +def example(x) + T.reveal_type(x) # error: `T.untyped` +end diff --git a/test/testdata/lsp/fast_path/alias_stub_module__3.2.rbupdate b/test/testdata/lsp/fast_path/alias_stub_module__3.2.rbupdate new file mode 100644 index 0000000000..ffa3d45492 --- /dev/null +++ b/test/testdata/lsp/fast_path/alias_stub_module__3.2.rbupdate @@ -0,0 +1,8 @@ +# typed: true +# exclude-from-file-update: true +extend T::Sig + +sig {params(x: A).void} +def example(x) + T.reveal_type(x) # error: `DoesNotExist` +end diff --git a/test/testdata/lsp/fast_path/alias_stub_module__3.rb b/test/testdata/lsp/fast_path/alias_stub_module__3.rb new file mode 100644 index 0000000000..9445a75cb3 --- /dev/null +++ b/test/testdata/lsp/fast_path/alias_stub_module__3.rb @@ -0,0 +1,8 @@ +# typed: true +# spacer for exclude-from-file-update +extend T::Sig + +sig {params(x: A).void} +def example(x) + T.reveal_type(x) # error: `T.untyped` +end diff --git a/test/testdata/lsp/fast_path/change_variance__1.1.rbupdate b/test/testdata/lsp/fast_path/change_variance__1.1.rbupdate new file mode 100644 index 0000000000..c27c65e1eb --- /dev/null +++ b/test/testdata/lsp/fast_path/change_variance__1.1.rbupdate @@ -0,0 +1,7 @@ +# typed: true +# assert-fast-path: change_variance__1.rb,change_variance__2.rb + +class Box + extend T::Generic + Elem = type_member(:out) +end diff --git a/test/testdata/lsp/fast_path/change_variance__1.rb b/test/testdata/lsp/fast_path/change_variance__1.rb new file mode 100644 index 0000000000..c83b6a775d --- /dev/null +++ b/test/testdata/lsp/fast_path/change_variance__1.rb @@ -0,0 +1,6 @@ +# typed: true + +class Box + extend T::Generic + Elem = type_member +end diff --git a/test/testdata/lsp/fast_path/change_variance__2.1.rbupdate b/test/testdata/lsp/fast_path/change_variance__2.1.rbupdate new file mode 100644 index 0000000000..645f2bc21a --- /dev/null +++ b/test/testdata/lsp/fast_path/change_variance__2.1.rbupdate @@ -0,0 +1,7 @@ +# typed: true +# exclude-from-file-update: true + +class Parent; end +class Child < Parent; end + +T.let(Box[Child].new, Box[Parent]) diff --git a/test/testdata/lsp/fast_path/change_variance__2.rb b/test/testdata/lsp/fast_path/change_variance__2.rb new file mode 100644 index 0000000000..14be171f29 --- /dev/null +++ b/test/testdata/lsp/fast_path/change_variance__2.rb @@ -0,0 +1,7 @@ +# typed: true +# spacer for exclude-from-file-update + +class Parent; end +class Child < Parent; end + +T.let(Box[Child].new, Box[Parent]) # error: does not have asserted type diff --git a/test/testdata/lsp/fast_path/fixed_type_member.1.rbupdate b/test/testdata/lsp/fast_path/fixed_type_member.1.rbupdate index 105b0c8e55..58a43e32aa 100644 --- a/test/testdata/lsp/fast_path/fixed_type_member.1.rbupdate +++ b/test/testdata/lsp/fast_path/fixed_type_member.1.rbupdate @@ -26,4 +26,4 @@ Box[Integer].new.parent_ex('') # ^^ error: Expected `Integer` but found `String("")` for argument `x` IntBox.new.parent_ex('') IntBox[Integer].new -# ^^^^^^^ error: Wrong number of type parameters +# ^^^^^^^ error: All type parameters for `IntBox` have already been fixed diff --git a/test/testdata/lsp/fast_path/fixed_type_member.rb b/test/testdata/lsp/fast_path/fixed_type_member.rb index c373328b07..d672d60528 100644 --- a/test/testdata/lsp/fast_path/fixed_type_member.rb +++ b/test/testdata/lsp/fast_path/fixed_type_member.rb @@ -26,4 +26,4 @@ def child_ex(x) IntBox.new.parent_ex('') # ^^ error: Expected `Integer` but found `String("")` for argument `x` IntBox[Integer].new -# ^^^^^^^ error: Wrong number of type parameters +# ^^^^^^^ error: All type parameters for `IntBox` have already been fixed diff --git a/test/testdata/lsp/fast_path/has_attached_class__1.1.rbupdate b/test/testdata/lsp/fast_path/has_attached_class__1.1.rbupdate new file mode 100644 index 0000000000..6e0130437e --- /dev/null +++ b/test/testdata/lsp/fast_path/has_attached_class__1.1.rbupdate @@ -0,0 +1,13 @@ +# typed: true +# assert-fast-path: has_attached_class__1.rb,has_attached_class__2.rb +extend T::Sig + +module Inheritable + extend T::Helpers + + module ClassMethods + extend T::Generic + has_attached_class!(:out) + end + mixes_in_class_methods(ClassMethods) +end diff --git a/test/testdata/lsp/fast_path/has_attached_class__1.rb b/test/testdata/lsp/fast_path/has_attached_class__1.rb new file mode 100644 index 0000000000..0b4dbdd664 --- /dev/null +++ b/test/testdata/lsp/fast_path/has_attached_class__1.rb @@ -0,0 +1,12 @@ +# typed: true +extend T::Sig + +module Inheritable + extend T::Helpers + + module ClassMethods + extend T::Generic + has_attached_class! + end + mixes_in_class_methods(ClassMethods) +end diff --git a/test/testdata/lsp/fast_path/has_attached_class__2.1.rbupdate b/test/testdata/lsp/fast_path/has_attached_class__2.1.rbupdate new file mode 100644 index 0000000000..0c2d412593 --- /dev/null +++ b/test/testdata/lsp/fast_path/has_attached_class__2.1.rbupdate @@ -0,0 +1,9 @@ +# typed: true +# exclude-from-file-update: true + +class AbstractModel; end +class Parent < AbstractModel + include Inheritable +end + +T.let(Parent, Inheritable::ClassMethods[AbstractModel]) diff --git a/test/testdata/lsp/fast_path/has_attached_class__2.rb b/test/testdata/lsp/fast_path/has_attached_class__2.rb new file mode 100644 index 0000000000..b6a16353e5 --- /dev/null +++ b/test/testdata/lsp/fast_path/has_attached_class__2.rb @@ -0,0 +1,9 @@ +# typed: true +# spacer for exclude-from-file-update + +class AbstractModel; end +class Parent < AbstractModel + include Inheritable +end + +T.let(Parent, Inheritable::ClassMethods[AbstractModel]) # error: does not have asserted type diff --git a/test/testdata/lsp/fast_path/multiple_behavior_defs_fix__1.1.rbupdate b/test/testdata/lsp/fast_path/multiple_behavior_defs_fix__1.1.rbupdate new file mode 100644 index 0000000000..9c821df38d --- /dev/null +++ b/test/testdata/lsp/fast_path/multiple_behavior_defs_fix__1.1.rbupdate @@ -0,0 +1,9 @@ +# typed: false +# stripe-mode: true +# assert-fast-path: multiple_behavior_defs_fix__2.rb +# exclude-from-file-update: true + +# This error does not go away on the fast path, even though it should. + +module Foo # error: has behavior defined in multiple files +end diff --git a/test/testdata/lsp/fast_path/multiple_behavior_defs_fix__1.rb b/test/testdata/lsp/fast_path/multiple_behavior_defs_fix__1.rb new file mode 100644 index 0000000000..02085edb2c --- /dev/null +++ b/test/testdata/lsp/fast_path/multiple_behavior_defs_fix__1.rb @@ -0,0 +1,10 @@ +# typed: false +# stripe-mode: true +# spacer for assert-fast-path +# spacer for exclude-from-file-update + +# This error does not go away on the fast path, even though it should. + +module Foo # error: has behavior defined in multiple files + def method1; end +end diff --git a/test/testdata/lsp/fast_path/multiple_behavior_defs_fix__2.1.rbupdate b/test/testdata/lsp/fast_path/multiple_behavior_defs_fix__2.1.rbupdate new file mode 100644 index 0000000000..af119f6017 --- /dev/null +++ b/test/testdata/lsp/fast_path/multiple_behavior_defs_fix__2.1.rbupdate @@ -0,0 +1,4 @@ +# typed: false + +module Foo +end diff --git a/test/testdata/lsp/fast_path/multiple_behavior_defs_fix__2.rb b/test/testdata/lsp/fast_path/multiple_behavior_defs_fix__2.rb new file mode 100644 index 0000000000..dfbd41eb08 --- /dev/null +++ b/test/testdata/lsp/fast_path/multiple_behavior_defs_fix__2.rb @@ -0,0 +1,5 @@ +# typed: false + +module Foo + def method2; end +end diff --git a/test/testdata/lsp/fast_path/multiple_behavior_defs_introduce__1.1.rbupdate b/test/testdata/lsp/fast_path/multiple_behavior_defs_introduce__1.1.rbupdate new file mode 100644 index 0000000000..0dfc73f59b --- /dev/null +++ b/test/testdata/lsp/fast_path/multiple_behavior_defs_introduce__1.1.rbupdate @@ -0,0 +1,7 @@ +# typed: false +# stripe-mode: true +# exclude-from-file-update: true + +module Foo + def meth; end # Defines behavior +end diff --git a/test/testdata/lsp/fast_path/multiple_behavior_defs_introduce__1.rb b/test/testdata/lsp/fast_path/multiple_behavior_defs_introduce__1.rb new file mode 100644 index 0000000000..4a6ce025d9 --- /dev/null +++ b/test/testdata/lsp/fast_path/multiple_behavior_defs_introduce__1.rb @@ -0,0 +1,7 @@ +# typed: false +# stripe-mode: true +# spacer for exclude-from-file-update + +module Foo + def meth; end # Defines behavior +end diff --git a/test/testdata/lsp/fast_path/multiple_behavior_defs_introduce__2.1.rbupdate b/test/testdata/lsp/fast_path/multiple_behavior_defs_introduce__2.1.rbupdate new file mode 100644 index 0000000000..9eeb4beb7b --- /dev/null +++ b/test/testdata/lsp/fast_path/multiple_behavior_defs_introduce__2.1.rbupdate @@ -0,0 +1,9 @@ +# typed: false +# assert-fast-path: multiple_behavior_defs_introduce__2.rb + +# Introducing a behavior def problem here will not be reported on the fast path + +module Foo + def meth2; end +end + diff --git a/test/testdata/lsp/fast_path/multiple_behavior_defs_introduce__2.rb b/test/testdata/lsp/fast_path/multiple_behavior_defs_introduce__2.rb new file mode 100644 index 0000000000..4661ee53d7 --- /dev/null +++ b/test/testdata/lsp/fast_path/multiple_behavior_defs_introduce__2.rb @@ -0,0 +1,6 @@ +# typed: false + +# Introducing a behavior def problem here will not be reported on the fast path + +module Foo +end diff --git a/test/testdata/lsp/fast_path/out_of_order.1.rbupdate b/test/testdata/lsp/fast_path/out_of_order.1.rbupdate new file mode 100644 index 0000000000..ff3d478ae7 --- /dev/null +++ b/test/testdata/lsp/fast_path/out_of_order.1.rbupdate @@ -0,0 +1,7 @@ +# typed: true +# assert-fast-path: out_of_order.rb + +X = A + +class A::Foo +end diff --git a/test/testdata/lsp/fast_path/out_of_order.rb b/test/testdata/lsp/fast_path/out_of_order.rb new file mode 100644 index 0000000000..c18a20a74a --- /dev/null +++ b/test/testdata/lsp/fast_path/out_of_order.rb @@ -0,0 +1,26 @@ +# check-out-of-order-constant-references: true +# typed: true + +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. + +X = A + +class A::Foo +end diff --git a/test/testdata/lsp/fast_path/static_field_change_type__def.1.rbupdate b/test/testdata/lsp/fast_path/static_field_change_type__def.1.rbupdate index eb434f5c38..0dc41ee4c9 100644 --- a/test/testdata/lsp/fast_path/static_field_change_type__def.1.rbupdate +++ b/test/testdata/lsp/fast_path/static_field_change_type__def.1.rbupdate @@ -1,5 +1,5 @@ # typed: strict # assert-fast-path: static_field_change_type__def.rb,static_field_change_type__use.rb -STATIC_FIELD = T.let({}, T::Hash[Symbol, ]) # error-with-dupes: Not enough arguments provided +STATIC_FIELD = T.let({}, T::Hash[Symbol, ]) # error: Not enough arguments provided # ^^^^^^ error-with-dupes: Wrong number of type parameters diff --git a/test/testdata/lsp/fast_path/type_member_add_remove.2.rbupdate b/test/testdata/lsp/fast_path/type_member_add_remove.2.rbupdate index 96cbe67b74..942a0ccb13 100644 --- a/test/testdata/lsp/fast_path/type_member_add_remove.2.rbupdate +++ b/test/testdata/lsp/fast_path/type_member_add_remove.2.rbupdate @@ -14,4 +14,5 @@ class Parent end parent = Parent[Integer].new +# ^^^^^^^ error: `Parent` is not a generic class, but was given type parameters T.reveal_type(parent.example(0)) # error: `T.untyped` diff --git a/test/testdata/lsp/fast_path/type_member_add_remove.rb b/test/testdata/lsp/fast_path/type_member_add_remove.rb index adfaef7671..0a3945314a 100644 --- a/test/testdata/lsp/fast_path/type_member_add_remove.rb +++ b/test/testdata/lsp/fast_path/type_member_add_remove.rb @@ -13,4 +13,5 @@ def example(x) end parent = Parent[Integer].new +# ^^^^^^^ error: `Parent` is not a generic class, but was given type parameters T.reveal_type(parent.example(0)) # error: `T.untyped` diff --git a/test/testdata/lsp/highlight_untyped.rb b/test/testdata/lsp/highlight_untyped.rb new file mode 100644 index 0000000000..26449354be --- /dev/null +++ b/test/testdata/lsp/highlight_untyped.rb @@ -0,0 +1,90 @@ +# typed: true + +# highlight-untyped-values: true + +extend T::Sig + +sig { returns(Integer) } +def foo + my_map = T.let({ foo: 1, bar: 'baz' }, T::Hash[Symbol, T.untyped]) + my_map[:foo] +# ^^^^^^^^^^^^ untyped: Value returned from method is `T.untyped` +end + +sig { params(x: Integer, y: String).returns(Integer) } +def bar(x, y) + if x > y.length + x + else + y.length + end +end + +sig { returns(T.untyped) } +def baz + T.let(5, T.untyped) +end + +# assign untyped thing to variable +b = baz + +# use an untyped variable +b.length +# ^^^^^^ untyped: Call to method `length` on `T.untyped` + +b.foo(0).bar(1).baz(2) +# ^^^ untyped: Call to method `foo` on `T.untyped` +# ^^^ untyped: Call to method `bar` on `T.untyped` +# ^^^ untyped: Call to method `baz` on `T.untyped` + +T.let(b, Integer) == 6 + + +my_map = T.let({:foo => 5, :bar => "foo"}, T::Hash[Symbol, T.untyped]) +# untyped argument +bar(my_map[:foo], T.let("foo", T.untyped)) +# ^^^^^^^^^^^^ untyped: Argument passed to parameter `x` is `T.untyped` +# ^^^^^^^^^^^^^^^^^^^^^^^ untyped: Argument passed to parameter `y` is `T.untyped` + +# if condition +if my_map[:foo] + #^^^^^^^^^^^^ untyped: Conditional branch on `T.untyped` + puts(6) +end + +# case statement +case my_map[:bar] +# ^^^^^^^^^^^^ untyped: Argument passed to parameter `arg0` is `T.untyped` +# ^^^^^^^^^^^^ untyped: Argument passed to parameter `arg0` is `T.untyped` +when "x" + "x" +when "y" + "y" +when b +# ^ untyped: Call to method `===` on `T.untyped` + "b" +end + +# use of super +class Base + extend T::Sig + + sig { overridable.returns(String) } + def foo + "foo" + end +end + +class Derived < Base + extend T::Sig + sig { override.returns(String) } + def foo + super +# ^^^^^ untyped: Value returned from method is `T.untyped` + end +end + +sig { params(x: Integer, y: String).returns(Integer) } +def binary_method(x, y) + 4 +end diff --git a/test/testdata/lsp/highlight_untyped_disabled.rb b/test/testdata/lsp/highlight_untyped_disabled.rb new file mode 100644 index 0000000000..c51f6c9b78 --- /dev/null +++ b/test/testdata/lsp/highlight_untyped_disabled.rb @@ -0,0 +1,86 @@ +# typed: strict + +# highlight-untyped-values: false + +extend T::Sig + +sig { returns(Integer) } +def foo + my_map = T.let({ foo: 1, bar: 'baz' }, T::Hash[Symbol, T.untyped]) + my_map[:foo] +end + +sig { params(x: Integer, y: String).returns(Integer) } +def bar(x, y) + if x > y.length + x + else + y.length + end +end + +sig { returns(T.untyped) } +def baz + T.let(5, T.untyped) +end + +# assign untyped thing to variable +b = baz + +# use an untyped variable + b.length +T.let(b, Integer) == 6 + + +my_map = T.let({:foo => 5, :bar => "foo"}, T::Hash[Symbol, T.untyped]) +# untyped argument +bar(my_map[:foo], T.let("foo", T.untyped)) + +# if condition +if my_map[:foo] + 6 +end + +# case statement +case my_map[:bar] +when "x" + "x" +when "y" + "y" +end + +# use of super +class Base + extend T::Sig + + sig { overridable.returns(String) } + def foo + "foo" + end +end + +class Derived < Base + extend T::Sig + sig { override.returns(String) } + def foo + super + end +end + +# untyped varargs +sig { params(args: T.untyped).void } +def args_fn(*args) + args.length +end + +# untyped kwargs +sig { params(kwargs: T.untyped).void } +def kwargs_fn(**kwargs) + kwargs.keys +end + +# use of &blk with untyped blk +sig {params(blk: T.untyped).returns(T.untyped)} +def blk_fun(&blk) + yield "x" +end \ No newline at end of file diff --git a/test/testdata/lsp/highlight_untyped_typed_strong.rb b/test/testdata/lsp/highlight_untyped_typed_strong.rb new file mode 100644 index 0000000000..5ff4ec27e1 --- /dev/null +++ b/test/testdata/lsp/highlight_untyped_typed_strong.rb @@ -0,0 +1,95 @@ +# typed: strong + +# highlight-untyped-values: false + +extend T::Sig + +sig { returns(Integer) } +def foo + my_map = T.let({ foo: 1, bar: 'baz' }, T::Hash[Symbol, T.untyped]) + my_map[:foo] +# ^^^^^^^^^^^^ error: Value returned from method is `T.untyped` +end + +sig { params(x: Integer, y: String).returns(Integer) } +def bar(x, y) + if x > y.length + x + else + y.length + end +end + +sig { returns(T.untyped) } +def baz + T.let(5, T.untyped) +end + +# assign untyped thing to variable +b = baz + +# use an untyped variable +b.length +# ^^^^^^ error: Call to method `length` on `T.untyped` +T.let(b, Integer) == 6 + + +my_map = T.let({:foo => 5, :bar => "foo"}, T::Hash[Symbol, T.untyped]) +# untyped argument +bar(my_map[:foo], T.let("foo", T.untyped)) +# ^^^^^^^^^^^^ error: Argument passed to parameter `x` is `T.untyped` +# ^^^^^^^^^^^^^^^^^^^^^^^ error: Argument passed to parameter `y` is `T.untyped` + +# if condition +if my_map[:foo] +# ^^^^^^^^^^^^ error: Conditional branch on `T.untyped` + puts(6) +end + +# case statement +case my_map[:bar] +# ^^^^^^^^^^^^ error: Argument passed to parameter `arg0` is `T.untyped` +# ^^^^^^^^^^^^ error: Argument passed to parameter `arg0` is `T.untyped` +when "x" + "x" +when "y" + "y" +end + +# use of super +class Base + extend T::Sig + + sig { overridable.returns(String) } + def foo + "foo" + end +end + +class Derived < Base + extend T::Sig + sig { override.returns(String) } + def foo + super +# ^^^^^ error: Value returned from method is `T.untyped` + end +end + +# untyped varargs +sig { params(args: T.untyped).void } +def args_fn(*args) + args.length +end + +# untyped kwargs +sig { params(kwargs: T.untyped).void } +def kwargs_fn(**kwargs) + kwargs.keys +end + +# use of &blk with untyped blk +sig {params(blk: T.untyped).returns(T.untyped)} +def blk_fun(&blk) + yield "x" +# ^^^^^^^^^ error: Call to method `call` on `T.untyped` +end diff --git a/test/testdata/lsp/hover.rb b/test/testdata/lsp/hover.rb index a6c659905c..1d28be9f6d 100644 --- a/test/testdata/lsp/hover.rb +++ b/test/testdata/lsp/hover.rb @@ -81,11 +81,11 @@ def self.anotherFunc() # Tests return markdown output sig {void} def tests_return_markdown - # ^^^^^^^^^^^^^^^^^^^^^ hover: ```ruby - # ^^^^^^^^^^^^^^^^^^^^^ hover: sig {void} - # ^^^^^^^^^^^^^^^^^^^^^ hover: ``` - # ^^^^^^^^^^^^^^^^^^^^^ hover: --- - # ^^^^^^^^^^^^^^^^^^^^^ hover: Tests return markdown output + # ^^^^^^^^^^^^^^^^^^^^^ hover-line: 1 ```ruby + # ^^^^^^^^^^^^^^^^^^^^^ hover-line: 2 sig {void} + # ^^^^^^^^^^^^^^^^^^^^^ hover-line: 4 ``` + # ^^^^^^^^^^^^^^^^^^^^^ hover-line: 6 --- + # ^^^^^^^^^^^^^^^^^^^^^ hover-line: 8 Tests return markdown output end end @@ -105,9 +105,9 @@ def main # Test primitive types n = nil # ^ hover: NilClass - t = true + t = true # ^ hover: TrueClass - f = false + f = false # ^ hover: FalseClass r = // # ^ hover: Regexp @@ -144,5 +144,5 @@ def main hoo = BigFoo::LittleFoo1.new # ^^^ hover: sig {returns(BigFoo::LittleFoo1)} raise "error message" -# ^ hover: sig {params(arg0: String).returns(T.noreturn)} + # ^ hover-line: 4 arg0: T.any(T::Class[T.anything], Exception, String) end diff --git a/test/testdata/lsp/hover_method_includes_defs.rb b/test/testdata/lsp/hover_method_includes_defs.rb index c98eb37cd0..0d1449b57b 100644 --- a/test/testdata/lsp/hover_method_includes_defs.rb +++ b/test/testdata/lsp/hover_method_includes_defs.rb @@ -34,7 +34,7 @@ class InnerClass extend T::Sig sig {params(name: String).void} def initialize(name) - # ^ hover: def initialize(name); end + # ^ hover: private def initialize(name); end @name = name end @@ -99,7 +99,7 @@ def multiple_arg_types(pos, *splat, required_key:, optional_key: "Jane", **kwarg def class_usages s = "Foo" qualified = InnerClass.new(s) - # ^ hover: def initialize(name); end + # ^ hover: private def initialize(name); end [ no_args_return_void, # ^ hover: def no_args_return_void; end diff --git a/test/testdata/lsp/hover_untyped_lambda.rb b/test/testdata/lsp/hover_untyped_lambda.rb new file mode 100644 index 0000000000..f514d056be --- /dev/null +++ b/test/testdata/lsp/hover_untyped_lambda.rb @@ -0,0 +1,9 @@ +# typed: strong + +->(arg0) { 1 } +# ^ hover: T.untyped + +->(arg0) do + arg0.foo + # ^^^ error: Call to method `foo` on `T.untyped` +end diff --git a/test/testdata/lsp/rbi_definition__1.rb b/test/testdata/lsp/rbi_definition__1.rb new file mode 100644 index 0000000000..8f69a6b205 --- /dev/null +++ b/test/testdata/lsp/rbi_definition__1.rb @@ -0,0 +1,4 @@ +# typed: true + +p(MyClass) +# ^^^^^^^ usage: MyClass diff --git a/test/testdata/lsp/rbi_definition__2.rbi b/test/testdata/lsp/rbi_definition__2.rbi new file mode 100644 index 0000000000..c2238efdbb --- /dev/null +++ b/test/testdata/lsp/rbi_definition__2.rbi @@ -0,0 +1,5 @@ +# typed: true + +class MyClass + # ^^^^^^^ def: MyClass +end diff --git a/test/testdata/lsp/rbi_definition__3.rbi b/test/testdata/lsp/rbi_definition__3.rbi new file mode 100644 index 0000000000..c2238efdbb --- /dev/null +++ b/test/testdata/lsp/rbi_definition__3.rbi @@ -0,0 +1,5 @@ +# typed: true + +class MyClass + # ^^^^^^^ def: MyClass +end diff --git a/test/testdata/lsp/references_with_sig.rb b/test/testdata/lsp/references_with_sig.rb new file mode 100644 index 0000000000..3ecbfd5e9c --- /dev/null +++ b/test/testdata/lsp/references_with_sig.rb @@ -0,0 +1,13 @@ +# typed: true + +class A + extend T::Sig + + sig { params(arg0: T.untyped, arg1: T.untyped, arg2: T.untyped, arg3: T.untyped).returns(T::Array[T.untyped]) } + # ^^^^ usage: arg0 + def example1(arg0, arg1 = nil, arg2:, arg3: nil) + # ^^^^ def: arg0 + [arg0, arg1, arg2, arg3] + #^^^^ usage: arg0 + end +end diff --git a/test/testdata/lsp/rename/method_arguments.A.rbedited b/test/testdata/lsp/rename/method_arguments.A.rbedited index 0ab39e447d..b6dbbec747 100644 --- a/test/testdata/lsp/rename/method_arguments.A.rbedited +++ b/test/testdata/lsp/rename/method_arguments.A.rbedited @@ -2,7 +2,7 @@ extend T::Sig -sig { params(arg0: T.untyped, arg1: T.untyped, arg2: T.untyped, arg3: T.untyped).returns(T::Array[T.untyped]) } +sig { params(target: T.untyped, arg1: T.untyped, arg2: T.untyped, arg3: T.untyped).returns(T::Array[T.untyped]) } def example1(target, arg1 = nil, arg2:, arg3: nil) # ^ apply-rename: [A] newName: target placeholderText: arg0 # ^ apply-rename: [B] newName: target placeholderText: arg1 diff --git a/test/testdata/lsp/rename/method_arguments.B.rbedited b/test/testdata/lsp/rename/method_arguments.B.rbedited index 7bd01a1bb6..b51029b935 100644 --- a/test/testdata/lsp/rename/method_arguments.B.rbedited +++ b/test/testdata/lsp/rename/method_arguments.B.rbedited @@ -2,7 +2,7 @@ extend T::Sig -sig { params(arg0: T.untyped, arg1: T.untyped, arg2: T.untyped, arg3: T.untyped).returns(T::Array[T.untyped]) } +sig { params(arg0: T.untyped, target: T.untyped, arg2: T.untyped, arg3: T.untyped).returns(T::Array[T.untyped]) } def example1(arg0, target = nil, arg2:, arg3: nil) # ^ apply-rename: [A] newName: target placeholderText: arg0 # ^ apply-rename: [B] newName: target placeholderText: arg1 diff --git a/test/testdata/lsp/rename/method_arguments.C.rbedited b/test/testdata/lsp/rename/method_arguments.C.rbedited index 65d544d9ac..351bc821d5 100644 --- a/test/testdata/lsp/rename/method_arguments.C.rbedited +++ b/test/testdata/lsp/rename/method_arguments.C.rbedited @@ -2,7 +2,7 @@ extend T::Sig -sig { params(arg0: T.untyped, arg1: T.untyped, arg2: T.untyped, arg3: T.untyped).returns(T::Array[T.untyped]) } +sig { params(arg0: T.untyped, arg1: T.untyped, target: T.untyped, arg3: T.untyped).returns(T::Array[T.untyped]) } def example1(arg0, arg1 = nil, target:, arg3: nil) # ^ apply-rename: [A] newName: target placeholderText: arg0 # ^ apply-rename: [B] newName: target placeholderText: arg1 diff --git a/test/testdata/lsp/rename/method_arguments.D.rbedited b/test/testdata/lsp/rename/method_arguments.D.rbedited index 92c99f1752..6e1ea79d64 100644 --- a/test/testdata/lsp/rename/method_arguments.D.rbedited +++ b/test/testdata/lsp/rename/method_arguments.D.rbedited @@ -2,7 +2,7 @@ extend T::Sig -sig { params(arg0: T.untyped, arg1: T.untyped, arg2: T.untyped, arg3: T.untyped).returns(T::Array[T.untyped]) } +sig { params(arg0: T.untyped, arg1: T.untyped, arg2: T.untyped, target: T.untyped).returns(T::Array[T.untyped]) } def example1(arg0, arg1 = nil, arg2:, target: nil) # ^ apply-rename: [A] newName: target placeholderText: arg0 # ^ apply-rename: [B] newName: target placeholderText: arg1 diff --git a/test/testdata/lsp/rename/method_arguments.E.rbedited b/test/testdata/lsp/rename/method_arguments.E.rbedited index a73d05116e..9c4b063823 100644 --- a/test/testdata/lsp/rename/method_arguments.E.rbedited +++ b/test/testdata/lsp/rename/method_arguments.E.rbedited @@ -11,7 +11,7 @@ def example1(arg0, arg1 = nil, arg2:, arg3: nil) [arg0, arg1, arg2, arg3] end -sig { params(args: T.untyped, kwargs: T.untyped).returns(T::Array[T.untyped]) } +sig { params(target: T.untyped, kwargs: T.untyped).returns(T::Array[T.untyped]) } def example2(*target, **kwargs) # ^ apply-rename: [E] newName: target placeholderText: args # ^ apply-rename: [F] newName: target placeholderText: kwargs diff --git a/test/testdata/lsp/rename/method_arguments.F.rbedited b/test/testdata/lsp/rename/method_arguments.F.rbedited index 4142749e81..f87eb4472a 100644 --- a/test/testdata/lsp/rename/method_arguments.F.rbedited +++ b/test/testdata/lsp/rename/method_arguments.F.rbedited @@ -11,7 +11,7 @@ def example1(arg0, arg1 = nil, arg2:, arg3: nil) [arg0, arg1, arg2, arg3] end -sig { params(args: T.untyped, kwargs: T.untyped).returns(T::Array[T.untyped]) } +sig { params(args: T.untyped, target: T.untyped).returns(T::Array[T.untyped]) } def example2(*args, **target) # ^ apply-rename: [E] newName: target placeholderText: args # ^ apply-rename: [F] newName: target placeholderText: kwargs diff --git a/test/testdata/lsp/rename/overloads_test.A.rbedited b/test/testdata/lsp/rename/overloads_test.A.rbedited new file mode 100644 index 0000000000..c5339ed99e --- /dev/null +++ b/test/testdata/lsp/rename/overloads_test.A.rbedited @@ -0,0 +1,15 @@ +# typed: true + +# Note: findSignature doesn't handle overloaded methods all that well right now + +class I; end +class S; end +class A + extend T::Sig + sig {params(x: I).void} + sig {params(target: S).void} + def my_method(target) + # ^ apply-rename: [A] newName: target placeholderText: x + puts(target) + end +end diff --git a/test/testdata/lsp/rename/overloads_test.rb b/test/testdata/lsp/rename/overloads_test.rb new file mode 100644 index 0000000000..d1cc49bef7 --- /dev/null +++ b/test/testdata/lsp/rename/overloads_test.rb @@ -0,0 +1,15 @@ +# typed: true + +# Note: findSignature doesn't handle overloaded methods all that well right now + +class I; end +class S; end +class A + extend T::Sig + sig {params(x: I).void} + sig {params(x: S).void} + def my_method(x) + # ^ apply-rename: [A] newName: target placeholderText: x + puts(x) + end +end diff --git a/test/testdata/lsp/rename/sig_attr_arg.A.rbedited b/test/testdata/lsp/rename/sig_attr_arg.A.rbedited new file mode 100644 index 0000000000..0836784730 --- /dev/null +++ b/test/testdata/lsp/rename/sig_attr_arg.A.rbedited @@ -0,0 +1,23 @@ +# typed: true + +# The behavior on this test is terrible, but that's because +# find-all-references is terrible on attr_reader: +# +# https://github.com/sorbet/sorbet/issues/4093 +# +# When we fix that bug, this test should magically start +# working. In the mean time, I'm not too worried about +# blocking on fixing that to support this for normal methods. + +class A + extend T::Sig + + sig {params(target: T.nilable(Integer)).returns(T.nilable(Integer))} + # ^ apply-rename: [A] newName: target placeholderText: x + targetarget + # ^ apply-rename: [B] newName: target placeholderText: @x + + sig {returns(T.nilable(Integer))} + attr_accessor :y + # ^ apply-rename: [C] newName: target placeholderText: @y +end diff --git a/test/testdata/lsp/rename/sig_attr_arg.B.rbedited b/test/testdata/lsp/rename/sig_attr_arg.B.rbedited new file mode 100644 index 0000000000..efc2ead185 --- /dev/null +++ b/test/testdata/lsp/rename/sig_attr_arg.B.rbedited @@ -0,0 +1,23 @@ +# typed: true + +# The behavior on this test is terrible, but that's because +# find-all-references is terrible on attr_reader: +# +# https://github.com/sorbet/sorbet/issues/4093 +# +# When we fix that bug, this test should magically start +# working. In the mean time, I'm not too worried about +# blocking on fixing that to support this for normal methods. + +class A + extend T::Sig + + sig {params(x: T.nilable(Integer)).returns(T.nilable(Integer))} + # ^ apply-rename: [A] newName: target placeholderText: x + attr_writer :target + # ^ apply-rename: [B] newName: target placeholderText: @x + + sig {returns(T.nilable(Integer))} + attr_accessor :y + # ^ apply-rename: [C] newName: target placeholderText: @y +end diff --git a/test/testdata/lsp/rename/sig_attr_arg.C.rbedited b/test/testdata/lsp/rename/sig_attr_arg.C.rbedited new file mode 100644 index 0000000000..1a2cbdcd0f --- /dev/null +++ b/test/testdata/lsp/rename/sig_attr_arg.C.rbedited @@ -0,0 +1,23 @@ +# typed: true + +# The behavior on this test is terrible, but that's because +# find-all-references is terrible on attr_reader: +# +# https://github.com/sorbet/sorbet/issues/4093 +# +# When we fix that bug, this test should magically start +# working. In the mean time, I'm not too worried about +# blocking on fixing that to support this for normal methods. + +class A + extend T::Sig + + sig {params(x: T.nilable(Integer)).returns(T.nilable(Integer))} + # ^ apply-rename: [A] newName: target placeholderText: x + attr_writer :x + # ^ apply-rename: [B] newName: target placeholderText: @x + + sig {returns(T.nilable(Integer))} + attr_accessor :target + # ^ apply-rename: [C] newName: target placeholderText: @y +end diff --git a/test/testdata/lsp/rename/sig_attr_arg.rb b/test/testdata/lsp/rename/sig_attr_arg.rb new file mode 100644 index 0000000000..651922b862 --- /dev/null +++ b/test/testdata/lsp/rename/sig_attr_arg.rb @@ -0,0 +1,23 @@ +# typed: true + +# The behavior on this test is terrible, but that's because +# find-all-references is terrible on attr_reader: +# +# https://github.com/sorbet/sorbet/issues/4093 +# +# When we fix that bug, this test should magically start +# working. In the mean time, I'm not too worried about +# blocking on fixing that to support this for normal methods. + +class A + extend T::Sig + + sig {params(x: T.nilable(Integer)).returns(T.nilable(Integer))} + # ^ apply-rename: [A] newName: target placeholderText: x + attr_writer :x + # ^ apply-rename: [B] newName: target placeholderText: @x + + sig {returns(T.nilable(Integer))} + attr_accessor :y + # ^ apply-rename: [C] newName: target placeholderText: @y +end diff --git a/test/testdata/lsp/requires_ancestor_ab.rb.symbol-table.exp b/test/testdata/lsp/requires_ancestor_ab.rb.symbol-table.exp index 951a2b7dd8..726774656d 100644 --- a/test/testdata/lsp/requires_ancestor_ab.rb.symbol-table.exp +++ b/test/testdata/lsp/requires_ancestor_ab.rb.symbol-table.exp @@ -7,8 +7,7 @@ class :: < ::Object () argument -> T.untyped @ Loc {file=test/testdata/lsp/requires_ancestor_ab.rb start=??? end=???} method ::A#only_on_a () @ test/testdata/lsp/requires_ancestor_ab.rb:7 argument @ Loc {file=test/testdata/lsp/requires_ancestor_ab.rb start=??? end=???} - class ::[] < ::Module (Sig) @ test/testdata/lsp/requires_ancestor_ab.rb:4 - type-member(+) :::: -> T.attached_class (of A) @ test/testdata/lsp/requires_ancestor_ab.rb:4 + class :: < ::Module (Sig) @ test/testdata/lsp/requires_ancestor_ab.rb:4 method ::# () @ test/testdata/lsp/requires_ancestor_ab.rb:4 argument @ Loc {file=test/testdata/lsp/requires_ancestor_ab.rb start=??? end=???} module ::B < ::Sorbet::Private::Static::ImplicitModuleSuperclass () @ test/testdata/lsp/requires_ancestor_ab.rb:13 @@ -16,8 +15,7 @@ class :: < ::Object () argument -> T.untyped @ Loc {file=test/testdata/lsp/requires_ancestor_ab.rb start=??? end=???} method ::B#only_on_b () @ test/testdata/lsp/requires_ancestor_ab.rb:16 argument @ Loc {file=test/testdata/lsp/requires_ancestor_ab.rb start=??? end=???} - class ::[] < ::Module (Sig) @ test/testdata/lsp/requires_ancestor_ab.rb:13 - type-member(+) :::: -> T.attached_class (of B) @ test/testdata/lsp/requires_ancestor_ab.rb:13 + class :: < ::Module (Sig) @ test/testdata/lsp/requires_ancestor_ab.rb:13 method ::# () @ test/testdata/lsp/requires_ancestor_ab.rb:13 argument @ Loc {file=test/testdata/lsp/requires_ancestor_ab.rb start=??? end=???} class ::C < ::Object (Target) @ test/testdata/lsp/requires_ancestor_ab.rb:35 @@ -34,8 +32,7 @@ class :: < ::Object () argument <> -> [Target] @ Loc {file=??? start=??? end=???} method ::Target#foo_b () @ test/testdata/lsp/requires_ancestor_ab.rb:27 argument @ Loc {file=test/testdata/lsp/requires_ancestor_ab.rb start=??? end=???} - class ::[] < ::Module (Helpers) @ test/testdata/lsp/requires_ancestor_ab.rb:22 - type-member(+) :::: -> T.attached_class (of Target) @ test/testdata/lsp/requires_ancestor_ab.rb:22 + class :: < ::Module (Helpers) @ test/testdata/lsp/requires_ancestor_ab.rb:22 method ::# () @ test/testdata/lsp/requires_ancestor_ab.rb:22 argument @ Loc {file=test/testdata/lsp/requires_ancestor_ab.rb start=??? end=???} diff --git a/test/testdata/namer/alias_method.rb.symbol-table-raw.exp b/test/testdata/namer/alias_method.rb.symbol-table-raw.exp index 95731dbdd5..20982a1d21 100644 --- a/test/testdata/namer/alias_method.rb.symbol-table-raw.exp +++ b/test/testdata/namer/alias_method.rb.symbol-table-raw.exp @@ -13,8 +13,7 @@ class >> < > () argument f @ Loc {file=test/testdata/namer/alias_method.rb start=6:17 end=6:18} method ># (f) -> AliasType { symbol = ># } @ Loc {file=test/testdata/namer/alias_method.rb start=7:3 end=7:30} argument f @ Loc {file=test/testdata/namer/alias_method.rb start=7:28 end=7:30} - class > $1>[>>] < > () @ Loc {file=test/testdata/namer/alias_method.rb start=2:1 end=2:13} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Alias) @ Loc {file=test/testdata/namer/alias_method.rb start=2:1 end=2:13} + class > $1> < > () @ Loc {file=test/testdata/namer/alias_method.rb start=2:1 end=2:13} method > $1>#> () @ Loc {file=test/testdata/namer/alias_method.rb start=2:1 end=21:4} argument @ Loc {file=test/testdata/namer/alias_method.rb start=??? end=???} diff --git a/test/testdata/namer/all_constant_redefinitions.rb.symbol-table-raw.exp b/test/testdata/namer/all_constant_redefinitions.rb.symbol-table-raw.exp index 9718f7b220..a199b59860 100644 --- a/test/testdata/namer/all_constant_redefinitions.rb.symbol-table-raw.exp +++ b/test/testdata/namer/all_constant_redefinitions.rb.symbol-table-raw.exp @@ -5,48 +5,47 @@ class >> < > () module >[> $2>, > $2>, > $2>, > $1>, > $1>, > $1>] < >::>::>::> () @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=3:1 end=3:15} class >::> < > () @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=6:3 end=6:10} static-field >::> $1> -> Integer @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=7:3 end=7:4} - type-member(=) >::> $2> -> LambdaParam(>::> $2>, lower=T.noreturn, upper=) @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=8:3 end=8:18} + type-member(=) >::> $2> -> LambdaParam(>::> $2>, lower=T.noreturn, upper=T.anything) @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=8:3 end=8:18} class >::> $1>[>>] < > $1> () @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=6:3 end=6:10} type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Wrapper::A) @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=6:3 end=6:10} method >::> $1>#> () @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=6:3 end=6:15} argument @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=??? end=???} class >::> < > () @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=11:3 end=11:10} static-field >::> $1> -> Integer @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=10:3 end=10:4} - type-member(=) >::> $2> -> LambdaParam(>::> $2>, lower=T.noreturn, upper=) @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=12:3 end=12:18} + type-member(=) >::> $2> -> LambdaParam(>::> $2>, lower=T.noreturn, upper=T.anything) @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=12:3 end=12:18} class >::> $1>[>>] < > $1> () @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=11:3 end=11:10} type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Wrapper::B) @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=11:3 end=11:10} method >::> $1>#> () @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=11:3 end=11:15} argument @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=??? end=???} class >::> < > () @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=16:3 end=16:10} static-field >::> $1> -> Integer @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=14:3 end=14:4} - type-member(=) >::> $2> -> LambdaParam(>::> $2>, lower=T.noreturn, upper=) @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=15:3 end=15:18} + type-member(=) >::> $2> -> LambdaParam(>::> $2>, lower=T.noreturn, upper=T.anything) @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=15:3 end=15:18} class >::> $1>[>>] < > $1> () @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=16:3 end=16:10} type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Wrapper::C) @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=16:3 end=16:10} method >::> $1>#> () @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=16:3 end=16:15} argument @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=??? end=???} class >::> < > () @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=18:3 end=18:10} - type-member(=) >::> $1> -> LambdaParam(>::> $1>, lower=T.noreturn, upper=) @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=19:3 end=19:18} + type-member(=) >::> $1> -> LambdaParam(>::> $1>, lower=T.noreturn, upper=T.anything) @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=19:3 end=19:18} static-field >::> $2> -> Integer @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=20:3 end=20:4} class >::> $1>[>>] < > $1> () @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=18:3 end=18:10} type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Wrapper::D) @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=18:3 end=18:10} method >::> $1>#> () @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=18:3 end=18:15} argument @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=??? end=???} class >::> < > () @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=23:3 end=23:10} - type-member(=) >::> $1> -> LambdaParam(>::> $1>, lower=T.noreturn, upper=) @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=22:3 end=22:18} + type-member(=) >::> $1> -> LambdaParam(>::> $1>, lower=T.noreturn, upper=T.anything) @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=22:3 end=22:18} static-field >::> $2> -> Integer @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=24:3 end=24:4} class >::> $1>[>>] < > $1> () @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=23:3 end=23:10} type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Wrapper::E) @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=23:3 end=23:10} method >::> $1>#> () @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=23:3 end=23:15} argument @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=??? end=???} class >::> < > () @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=28:3 end=28:10} - type-member(=) >::> $1> -> LambdaParam(>::> $1>, lower=T.noreturn, upper=) @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=26:3 end=26:18} + type-member(=) >::> $1> -> LambdaParam(>::> $1>, lower=T.noreturn, upper=T.anything) @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=26:3 end=26:18} static-field >::> $2> -> Integer @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=27:3 end=27:4} class >::> $1>[>>] < > $1> () @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=28:3 end=28:10} type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Wrapper::F) @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=28:3 end=28:10} method >::> $1>#> () @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=28:3 end=28:15} argument @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=??? end=???} - class > $1>[>>] < > (>, >) @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=3:1 end=3:15} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > targs = [ > $2> = T.untyped > $2> = T.untyped > $2> = T.untyped > $1> = T.untyped > $1> = T.untyped > $1> = T.untyped ] }) @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=3:1 end=3:15} + class > $1> < > (>, >) @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=3:1 end=3:15} method > $1>#> () @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=3:1 end=29:4} argument @ Loc {file=test/testdata/namer/all_constant_redefinitions.rb start=??? end=???} diff --git a/test/testdata/namer/ancestors.rb.symbol-table-raw.exp b/test/testdata/namer/ancestors.rb.symbol-table-raw.exp index afb5532b65..04de95e395 100644 --- a/test/testdata/namer/ancestors.rb.symbol-table-raw.exp +++ b/test/testdata/namer/ancestors.rb.symbol-table-raw.exp @@ -8,13 +8,11 @@ class >> < > () method > $1>#> () @ Loc {file=test/testdata/namer/ancestors.rb start=9:1 end=11:4} argument @ Loc {file=test/testdata/namer/ancestors.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/namer/ancestors.rb start=2:1 end=2:14} - class > $1>[>>] < > () @ Loc {file=test/testdata/namer/ancestors.rb start=2:1 end=2:14} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Mixin1) @ Loc {file=test/testdata/namer/ancestors.rb start=2:1 end=2:14} + class > $1> < > () @ Loc {file=test/testdata/namer/ancestors.rb start=2:1 end=2:14} method > $1>#> () @ Loc {file=test/testdata/namer/ancestors.rb start=2:1 end=2:19} argument @ Loc {file=test/testdata/namer/ancestors.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/namer/ancestors.rb start=3:1 end=3:14} - class > $1>[>>] < > () @ Loc {file=test/testdata/namer/ancestors.rb start=3:1 end=3:14} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Mixin2) @ Loc {file=test/testdata/namer/ancestors.rb start=3:1 end=3:14} + class > $1> < > () @ Loc {file=test/testdata/namer/ancestors.rb start=3:1 end=3:14} method > $1>#> () @ Loc {file=test/testdata/namer/ancestors.rb start=3:1 end=3:19} argument @ Loc {file=test/testdata/namer/ancestors.rb start=??? end=???} class > < > (>, >) @ Loc {file=test/testdata/namer/ancestors.rb start=13:1 end=13:22} diff --git a/test/testdata/namer/circular_mixin.rb.symbol-table-raw.exp b/test/testdata/namer/circular_mixin.rb.symbol-table-raw.exp index 7cb4482fc7..45e86188e3 100644 --- a/test/testdata/namer/circular_mixin.rb.symbol-table-raw.exp +++ b/test/testdata/namer/circular_mixin.rb.symbol-table-raw.exp @@ -3,13 +3,11 @@ class >> < > () method >> $1>#> $CENSORED> () @ Loc {file=test/testdata/namer/circular_mixin.rb start=2:1 end=13:4} argument @ Loc {file=test/testdata/namer/circular_mixin.rb start=??? end=???} module > < >::>::>::> (>, >) @ Loc {file=test/testdata/namer/circular_mixin.rb start=11:1 end=11:9} - class > $1>[>>] < > () @ Loc {file=test/testdata/namer/circular_mixin.rb start=11:1 end=11:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/namer/circular_mixin.rb start=2:1 end=2:9} + class > $1> < > () @ Loc {file=test/testdata/namer/circular_mixin.rb start=11:1 end=11:9} method > $1>#> () @ Loc {file=test/testdata/namer/circular_mixin.rb start=2:1 end=2:14} argument @ Loc {file=test/testdata/namer/circular_mixin.rb start=??? end=???} module > < >::>::>::> (>) @ Loc {file=test/testdata/namer/circular_mixin.rb start=7:1 end=7:9} - class > $1>[>>] < > () @ Loc {file=test/testdata/namer/circular_mixin.rb start=7:1 end=7:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=B) @ Loc {file=test/testdata/namer/circular_mixin.rb start=3:1 end=3:9} + class > $1> < > () @ Loc {file=test/testdata/namer/circular_mixin.rb start=7:1 end=7:9} method > $1>#> () @ Loc {file=test/testdata/namer/circular_mixin.rb start=3:1 end=3:14} argument @ Loc {file=test/testdata/namer/circular_mixin.rb start=??? end=???} diff --git a/test/testdata/namer/class_alias_inside_method.rb.symbol-table-raw.exp b/test/testdata/namer/class_alias_inside_method.rb.symbol-table-raw.exp index 7e8b2d5dc9..1077495b11 100644 --- a/test/testdata/namer/class_alias_inside_method.rb.symbol-table-raw.exp +++ b/test/testdata/namer/class_alias_inside_method.rb.symbol-table-raw.exp @@ -11,8 +11,7 @@ class >> < > () method ># (x, ) -> NilClass @ Loc {file=test/testdata/namer/class_alias_inside_method.rb start=8:3 end=8:11} argument x<> -> A::C1 @ Loc {file=test/testdata/namer/class_alias_inside_method.rb start=7:15 end=7:16} argument -> T.untyped @ Loc {file=test/testdata/namer/class_alias_inside_method.rb start=??? end=???} - class > $1>[>>] < > (>) @ Loc {file=test/testdata/namer/class_alias_inside_method.rb start=2:1 end=2:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/namer/class_alias_inside_method.rb start=2:1 end=2:9} + class > $1> < > (>) @ Loc {file=test/testdata/namer/class_alias_inside_method.rb start=2:1 end=2:9} method > $1>#> () @ Loc {file=test/testdata/namer/class_alias_inside_method.rb start=2:1 end=12:4} argument @ Loc {file=test/testdata/namer/class_alias_inside_method.rb start=??? end=???} diff --git a/test/testdata/namer/class_self_private_class_method.rb.symbol-table-raw.exp b/test/testdata/namer/class_self_private_class_method.rb.symbol-table-raw.exp index b7c33fbaca..e73dc51c0c 100644 --- a/test/testdata/namer/class_self_private_class_method.rb.symbol-table-raw.exp +++ b/test/testdata/namer/class_self_private_class_method.rb.symbol-table-raw.exp @@ -7,7 +7,7 @@ class >> < > () type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/namer/class_self_private_class_method.rb start=3:1 end=3:8} method > $1>#> () @ Loc {file=test/testdata/namer/class_self_private_class_method.rb start=3:1 end=7:4} argument @ Loc {file=test/testdata/namer/class_self_private_class_method.rb start=??? end=???} - class > $1> $1>[>>] < > $1> $1> () @ Loc {file=test/testdata/namer/class_self_private_class_method.rb start=4:3 end=4:8} + class > $1> $1>[>>] < > () @ Loc {file=test/testdata/namer/class_self_private_class_method.rb start=4:3 end=4:8} type-member(+) > $1> $1>::>> -> LambdaParam(> $1> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > $1> targs = [ >> = A ] }) @ Loc {file=test/testdata/namer/class_self_private_class_method.rb start=4:3 end=4:8} method > $1> $1>#> () @ Loc {file=test/testdata/namer/class_self_private_class_method.rb start=4:3 end=6:6} argument @ Loc {file=test/testdata/namer/class_self_private_class_method.rb start=??? end=???} diff --git a/test/testdata/namer/conflicting_names.rb.symbol-table-raw.exp b/test/testdata/namer/conflicting_names.rb.symbol-table-raw.exp index 0935079a72..3828e2f277 100644 --- a/test/testdata/namer/conflicting_names.rb.symbol-table-raw.exp +++ b/test/testdata/namer/conflicting_names.rb.symbol-table-raw.exp @@ -10,8 +10,7 @@ class >> < > () argument @ Loc {file=test/testdata/namer/conflicting_names.rb start=??? end=???} method ># () @ Loc {file=test/testdata/namer/conflicting_names.rb start=3:3 end=3:12} argument @ Loc {file=test/testdata/namer/conflicting_names.rb start=??? end=???} - class > $1>[>>] < > () @ Loc {file=test/testdata/namer/conflicting_names.rb start=2:1 end=2:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/namer/conflicting_names.rb start=2:1 end=2:9} + class > $1> < > () @ Loc {file=test/testdata/namer/conflicting_names.rb start=2:1 end=2:9} method > $1>#> () @ Loc {file=test/testdata/namer/conflicting_names.rb start=2:1 end=8:4} argument @ Loc {file=test/testdata/namer/conflicting_names.rb start=??? end=???} diff --git a/test/testdata/namer/constant_types.rb.symbol-table-raw.exp b/test/testdata/namer/constant_types.rb.symbol-table-raw.exp index 5288024f14..c932acd96b 100644 --- a/test/testdata/namer/constant_types.rb.symbol-table-raw.exp +++ b/test/testdata/namer/constant_types.rb.symbol-table-raw.exp @@ -9,8 +9,7 @@ class >> < > () static-field >::> -> Float @ Loc {file=test/testdata/namer/constant_types.rb start=7:3 end=7:4} static-field >::> -> AppliedType { klass = > targs = [ > = Integer ] } @ Loc {file=test/testdata/namer/constant_types.rb start=8:3 end=8:4} static-field >::> -> AppliedType { klass = > targs = [ > = String ] } @ Loc {file=test/testdata/namer/constant_types.rb start=9:3 end=9:4} - class > $1>[>>] < > () @ Loc {file=test/testdata/namer/constant_types.rb start=3:1 end=3:17} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Constants) @ Loc {file=test/testdata/namer/constant_types.rb start=3:1 end=3:17} + class > $1> < > () @ Loc {file=test/testdata/namer/constant_types.rb start=3:1 end=3:17} method > $1>#> () @ Loc {file=test/testdata/namer/constant_types.rb start=3:1 end=10:4} argument @ Loc {file=test/testdata/namer/constant_types.rb start=??? end=???} diff --git a/test/testdata/namer/constants.rb.symbol-table-raw.exp b/test/testdata/namer/constants.rb.symbol-table-raw.exp index 24ddcc905a..0fa278e272 100644 --- a/test/testdata/namer/constants.rb.symbol-table-raw.exp +++ b/test/testdata/namer/constants.rb.symbol-table-raw.exp @@ -6,12 +6,10 @@ class >> < > () module >::> < >::>::>::> () @ Loc {file=test/testdata/namer/constants.rb start=5:3 end=5:11} static-field >::>::> -> Integer @ Loc {file=test/testdata/namer/constants.rb start=3:3 end=3:7} static-field >::>::> -> Integer @ Loc {file=test/testdata/namer/constants.rb start=8:5 end=8:6} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/namer/constants.rb start=5:3 end=5:11} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=A::B) @ Loc {file=test/testdata/namer/constants.rb start=3:3 end=3:4} + class >::> $1> < > () @ Loc {file=test/testdata/namer/constants.rb start=5:3 end=5:11} method >::> $1>#> () @ Loc {file=test/testdata/namer/constants.rb start=5:3 end=9:6} argument @ Loc {file=test/testdata/namer/constants.rb start=??? end=???} - class > $1>[>>] < > () @ Loc {file=test/testdata/namer/constants.rb start=2:1 end=2:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/namer/constants.rb start=2:1 end=2:9} + class > $1> < > () @ Loc {file=test/testdata/namer/constants.rb start=2:1 end=2:9} method > $1>#> () @ Loc {file=test/testdata/namer/constants.rb start=2:1 end=10:4} argument @ Loc {file=test/testdata/namer/constants.rb start=??? end=???} diff --git a/test/testdata/namer/extend.rb.symbol-table-raw.exp b/test/testdata/namer/extend.rb.symbol-table-raw.exp index 2803c00473..5c2cc834de 100644 --- a/test/testdata/namer/extend.rb.symbol-table-raw.exp +++ b/test/testdata/namer/extend.rb.symbol-table-raw.exp @@ -10,15 +10,13 @@ class >> < > () module > < >::>::>::> () @ Loc {file=test/testdata/namer/extend.rb start=2:1 end=2:14} method ># () @ Loc {file=test/testdata/namer/extend.rb start=3:3 end=3:16} argument @ Loc {file=test/testdata/namer/extend.rb start=??? end=???} - class > $1>[>>] < > () @ Loc {file=test/testdata/namer/extend.rb start=2:1 end=2:14} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Mixin1) @ Loc {file=test/testdata/namer/extend.rb start=2:1 end=2:14} + class > $1> < > () @ Loc {file=test/testdata/namer/extend.rb start=2:1 end=2:14} method > $1>#> () @ Loc {file=test/testdata/namer/extend.rb start=2:1 end=5:4} argument @ Loc {file=test/testdata/namer/extend.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/namer/extend.rb start=7:1 end=7:14} method ># () @ Loc {file=test/testdata/namer/extend.rb start=8:3 end=8:18} argument @ Loc {file=test/testdata/namer/extend.rb start=??? end=???} - class > $1>[>>] < > () @ Loc {file=test/testdata/namer/extend.rb start=7:1 end=7:14} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Mixin2) @ Loc {file=test/testdata/namer/extend.rb start=7:1 end=7:14} + class > $1> < > () @ Loc {file=test/testdata/namer/extend.rb start=7:1 end=7:14} method > $1>#> () @ Loc {file=test/testdata/namer/extend.rb start=7:1 end=10:4} argument @ Loc {file=test/testdata/namer/extend.rb start=??? end=???} diff --git a/test/testdata/namer/fuzz_class_in_field.rb.symbol-table-raw.exp b/test/testdata/namer/fuzz_class_in_field.rb.symbol-table-raw.exp index 2bd23c8535..551a52210b 100644 --- a/test/testdata/namer/fuzz_class_in_field.rb.symbol-table-raw.exp +++ b/test/testdata/namer/fuzz_class_in_field.rb.symbol-table-raw.exp @@ -10,10 +10,8 @@ class >> < > () method >::>::> $1>#> () @ Loc {file=test/testdata/namer/fuzz_class_in_field.rb start=4:3 end=4:18} argument @ Loc {file=test/testdata/namer/fuzz_class_in_field.rb start=??? end=???} static-field >::> $1> -> Integer @ Loc {file=test/testdata/namer/fuzz_class_in_field.rb start=3:3 end=3:4} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/namer/fuzz_class_in_field.rb start=4:9 end=4:10} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=D::D) @ Loc {file=test/testdata/namer/fuzz_class_in_field.rb start=4:9 end=4:10} - class > $1>[>>] < > () @ Loc {file=test/testdata/namer/fuzz_class_in_field.rb start=2:1 end=2:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=D) @ Loc {file=test/testdata/namer/fuzz_class_in_field.rb start=2:1 end=2:9} + class >::> $1> < > () @ Loc {file=test/testdata/namer/fuzz_class_in_field.rb start=4:9 end=4:10} + class > $1> < > () @ Loc {file=test/testdata/namer/fuzz_class_in_field.rb start=2:1 end=2:9} method > $1>#> () @ Loc {file=test/testdata/namer/fuzz_class_in_field.rb start=2:1 end=6:4} argument @ Loc {file=test/testdata/namer/fuzz_class_in_field.rb start=??? end=???} diff --git a/test/testdata/namer/fuzz_type_template_overwrite.rb.symbol-table-raw.exp b/test/testdata/namer/fuzz_type_template_overwrite.rb.symbol-table-raw.exp index fe92ba39ac..dff5cb3bcc 100644 --- a/test/testdata/namer/fuzz_type_template_overwrite.rb.symbol-table-raw.exp +++ b/test/testdata/namer/fuzz_type_template_overwrite.rb.symbol-table-raw.exp @@ -9,5 +9,5 @@ class >> < > () type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/namer/fuzz_type_template_overwrite.rb start=3:1 end=3:8} method > $1>#> () @ Loc {file=test/testdata/namer/fuzz_type_template_overwrite.rb start=3:1 end=6:4} argument @ Loc {file=test/testdata/namer/fuzz_type_template_overwrite.rb start=??? end=???} - type-member(=) > $1>::> -> LambdaParam(> $1>::>, lower=T.noreturn, upper=) @ Loc {file=test/testdata/namer/fuzz_type_template_overwrite.rb start=5:3 end=5:20} + type-member(=) > $1>::> -> LambdaParam(> $1>::>, lower=T.noreturn, upper=T.anything) @ Loc {file=test/testdata/namer/fuzz_type_template_overwrite.rb start=5:3 end=5:20} diff --git a/test/testdata/namer/has_attached_class_subclass_class.rb b/test/testdata/namer/has_attached_class_subclass_class.rb new file mode 100644 index 0000000000..f8d1335b46 --- /dev/null +++ b/test/testdata/namer/has_attached_class_subclass_class.rb @@ -0,0 +1,11 @@ +# typed: true +# disable-fast-path: true + +class A < Class + extend T::Generic + has_attached_class! # error: can only be used inside a `module` +end + +class B < Class # error: is a subclass of `Class` which is not allowed + extend T::Generic +end diff --git a/test/testdata/namer/ivar_insseq.rb b/test/testdata/namer/ivar_insseq.rb new file mode 100644 index 0000000000..553a75cc48 --- /dev/null +++ b/test/testdata/namer/ivar_insseq.rb @@ -0,0 +1,15 @@ +# typed: true + +class A + extend T::Sig + sig { returns(Integer) } + def foo + @bar ||= begin; x = 1; T.let(x, Integer); end + # ^^^^ error: The instance variable `@bar` must be declared inside `initialize` or declared nilable + # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: This code is unreachable + end + + def other + T.reveal_type(@bar) # error: `Integer` + end +end diff --git a/test/testdata/namer/module_function.rb.cfg-text.exp b/test/testdata/namer/module_function.rb.cfg-text.exp index 6818263f03..ad35424d3e 100644 --- a/test/testdata/namer/module_function.rb.cfg-text.exp +++ b/test/testdata/namer/module_function.rb.cfg-text.exp @@ -138,9 +138,9 @@ bb2[rubyRegionId=1, firstDead=-1](: T.class_of(Funcs), $7: Sorbet::Private::Static::Void, $8: T.class_of(Funcs)): $3: Sorbet::Private::Static::Void = Solve<$7, sig> : T.class_of(Funcs) = $8 - $21: T.class_of(Sorbet::Private::Static) = alias - $23: Sorbet::Private::Static::Void = $21: T.class_of(Sorbet::Private::Static).sig(: T.class_of(Funcs)) - $24: T.class_of(Funcs) = + $20: T.class_of(Sorbet::Private::Static) = alias + $22: Sorbet::Private::Static::Void = $20: T.class_of(Sorbet::Private::Static).sig(: T.class_of(Funcs)) + $23: T.class_of(Funcs) = -> bb6 # backedges @@ -148,139 +148,139 @@ bb3[rubyRegionId=0, firstDead=-1]($7: Sorbet::Private::Stat bb5[rubyRegionId=1, firstDead=7](: T.class_of(Funcs), $7: Sorbet::Private::Static::Void, $8: T.class_of(Funcs)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $13: Symbol(:x) = :x - $15: T.class_of(Integer) = alias - $11: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($13: Symbol(:x), $15: T.class_of(Integer)) - $17: T.class_of(Integer) = alias - $10: T::Private::Methods::DeclBuilder = $11: T::Private::Methods::DeclBuilder.returns($17: T.class_of(Integer)) - $18: T.noreturn = blockreturn $10: T::Private::Methods::DeclBuilder + $12: Symbol(:x) = :x + $14: T.class_of(Integer) = alias + $10: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($12: Symbol(:x), $14: T.class_of(Integer)) + $16: T.class_of(Integer) = alias + $9: T::Private::Methods::DeclBuilder = $10: T::Private::Methods::DeclBuilder.returns($16: T.class_of(Integer)) + $17: T.noreturn = blockreturn $9: T::Private::Methods::DeclBuilder -> bb2 # backedges # - bb3(rubyRegionId=0) # - bb9(rubyRegionId=2) -bb6[rubyRegionId=2, firstDead=-1](: T.class_of(Funcs), $23: Sorbet::Private::Static::Void, $24: T.class_of(Funcs)): +bb6[rubyRegionId=2, firstDead=-1](: T.class_of(Funcs), $22: Sorbet::Private::Static::Void, $23: T.class_of(Funcs)): # outerLoops: 1 -> (NilClass ? bb9 : bb7) # backedges # - bb6(rubyRegionId=2) -bb7[rubyRegionId=0, firstDead=-1]($23: Sorbet::Private::Static::Void, $24: T.class_of(Funcs)): - $19: Sorbet::Private::Static::Void = Solve<$23, sig> - : T.class_of(Funcs) = $24 - $37: T.class_of(Sorbet::Private::Static) = alias - $39: Sorbet::Private::Static::Void = $37: T.class_of(Sorbet::Private::Static).sig(: T.class_of(Funcs)) - $40: T.class_of(Funcs) = +bb7[rubyRegionId=0, firstDead=-1]($22: Sorbet::Private::Static::Void, $23: T.class_of(Funcs)): + $18: Sorbet::Private::Static::Void = Solve<$22, sig> + : T.class_of(Funcs) = $23 + $35: T.class_of(Sorbet::Private::Static) = alias + $37: Sorbet::Private::Static::Void = $35: T.class_of(Sorbet::Private::Static).sig(: T.class_of(Funcs)) + $38: T.class_of(Funcs) = -> bb10 # backedges # - bb6(rubyRegionId=2) -bb9[rubyRegionId=2, firstDead=7](: T.class_of(Funcs), $23: Sorbet::Private::Static::Void, $24: T.class_of(Funcs)): +bb9[rubyRegionId=2, firstDead=7](: T.class_of(Funcs), $22: Sorbet::Private::Static::Void, $23: T.class_of(Funcs)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $29: Symbol(:s) = :s + $27: Symbol(:s) = :s + $29: T.class_of(Symbol) = alias + $25: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($27: Symbol(:s), $29: T.class_of(Symbol)) $31: T.class_of(Symbol) = alias - $27: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($29: Symbol(:s), $31: T.class_of(Symbol)) - $33: T.class_of(Symbol) = alias - $26: T::Private::Methods::DeclBuilder = $27: T::Private::Methods::DeclBuilder.returns($33: T.class_of(Symbol)) - $34: T.noreturn = blockreturn $26: T::Private::Methods::DeclBuilder + $24: T::Private::Methods::DeclBuilder = $25: T::Private::Methods::DeclBuilder.returns($31: T.class_of(Symbol)) + $32: T.noreturn = blockreturn $24: T::Private::Methods::DeclBuilder -> bb6 # backedges # - bb7(rubyRegionId=0) # - bb13(rubyRegionId=3) -bb10[rubyRegionId=3, firstDead=-1](: T.class_of(Funcs), $39: Sorbet::Private::Static::Void, $40: T.class_of(Funcs)): +bb10[rubyRegionId=3, firstDead=-1](: T.class_of(Funcs), $37: Sorbet::Private::Static::Void, $38: T.class_of(Funcs)): # outerLoops: 1 -> (NilClass ? bb13 : bb11) # backedges # - bb10(rubyRegionId=3) -bb11[rubyRegionId=0, firstDead=-1]($39: Sorbet::Private::Static::Void, $40: T.class_of(Funcs)): - $35: Sorbet::Private::Static::Void = Solve<$39, sig> - : T.class_of(Funcs) = $40 - $53: T.class_of(Sorbet::Private::Static) = alias - $55: Sorbet::Private::Static::Void = $53: T.class_of(Sorbet::Private::Static).sig(: T.class_of(Funcs)) - $56: T.class_of(Funcs) = +bb11[rubyRegionId=0, firstDead=-1]($37: Sorbet::Private::Static::Void, $38: T.class_of(Funcs)): + $33: Sorbet::Private::Static::Void = Solve<$37, sig> + : T.class_of(Funcs) = $38 + $50: T.class_of(Sorbet::Private::Static) = alias + $52: Sorbet::Private::Static::Void = $50: T.class_of(Sorbet::Private::Static).sig(: T.class_of(Funcs)) + $53: T.class_of(Funcs) = -> bb14 # backedges # - bb10(rubyRegionId=3) -bb13[rubyRegionId=3, firstDead=7](: T.class_of(Funcs), $39: Sorbet::Private::Static::Void, $40: T.class_of(Funcs)): +bb13[rubyRegionId=3, firstDead=7](: T.class_of(Funcs), $37: Sorbet::Private::Static::Void, $38: T.class_of(Funcs)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $45: Symbol(:s) = :s - $47: T.class_of(Symbol) = alias - $43: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($45: Symbol(:s), $47: T.class_of(Symbol)) - $49: T.class_of(Symbol) = alias - $42: T::Private::Methods::DeclBuilder = $43: T::Private::Methods::DeclBuilder.returns($49: T.class_of(Symbol)) - $50: T.noreturn = blockreturn $42: T::Private::Methods::DeclBuilder + $42: Symbol(:s) = :s + $44: T.class_of(Symbol) = alias + $40: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($42: Symbol(:s), $44: T.class_of(Symbol)) + $46: T.class_of(Symbol) = alias + $39: T::Private::Methods::DeclBuilder = $40: T::Private::Methods::DeclBuilder.returns($46: T.class_of(Symbol)) + $47: T.noreturn = blockreturn $39: T::Private::Methods::DeclBuilder -> bb10 # backedges # - bb11(rubyRegionId=0) # - bb17(rubyRegionId=4) -bb14[rubyRegionId=4, firstDead=-1](: T.class_of(Funcs), $55: Sorbet::Private::Static::Void, $56: T.class_of(Funcs)): +bb14[rubyRegionId=4, firstDead=-1](: T.class_of(Funcs), $52: Sorbet::Private::Static::Void, $53: T.class_of(Funcs)): # outerLoops: 1 -> (NilClass ? bb17 : bb15) # backedges # - bb14(rubyRegionId=4) -bb15[rubyRegionId=0, firstDead=-1]($55: Sorbet::Private::Static::Void, $56: T.class_of(Funcs)): - $51: Sorbet::Private::Static::Void = Solve<$55, sig> - : T.class_of(Funcs) = $56 - $69: T.class_of(Sorbet::Private::Static) = alias - $71: Sorbet::Private::Static::Void = $69: T.class_of(Sorbet::Private::Static).sig(: T.class_of(Funcs)) - $72: T.class_of(Funcs) = +bb15[rubyRegionId=0, firstDead=-1]($52: Sorbet::Private::Static::Void, $53: T.class_of(Funcs)): + $48: Sorbet::Private::Static::Void = Solve<$52, sig> + : T.class_of(Funcs) = $53 + $65: T.class_of(Sorbet::Private::Static) = alias + $67: Sorbet::Private::Static::Void = $65: T.class_of(Sorbet::Private::Static).sig(: T.class_of(Funcs)) + $68: T.class_of(Funcs) = -> bb18 # backedges # - bb14(rubyRegionId=4) -bb17[rubyRegionId=4, firstDead=7](: T.class_of(Funcs), $55: Sorbet::Private::Static::Void, $56: T.class_of(Funcs)): +bb17[rubyRegionId=4, firstDead=7](: T.class_of(Funcs), $52: Sorbet::Private::Static::Void, $53: T.class_of(Funcs)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $61: Symbol(:s) = :s - $63: T.class_of(String) = alias - $59: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($61: Symbol(:s), $63: T.class_of(String)) - $65: T.class_of(String) = alias - $58: T::Private::Methods::DeclBuilder = $59: T::Private::Methods::DeclBuilder.returns($65: T.class_of(String)) - $66: T.noreturn = blockreturn $58: T::Private::Methods::DeclBuilder + $57: Symbol(:s) = :s + $59: T.class_of(String) = alias + $55: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($57: Symbol(:s), $59: T.class_of(String)) + $61: T.class_of(String) = alias + $54: T::Private::Methods::DeclBuilder = $55: T::Private::Methods::DeclBuilder.returns($61: T.class_of(String)) + $62: T.noreturn = blockreturn $54: T::Private::Methods::DeclBuilder -> bb14 # backedges # - bb15(rubyRegionId=0) # - bb21(rubyRegionId=5) -bb18[rubyRegionId=5, firstDead=-1](: T.class_of(Funcs), $71: Sorbet::Private::Static::Void, $72: T.class_of(Funcs)): +bb18[rubyRegionId=5, firstDead=-1](: T.class_of(Funcs), $67: Sorbet::Private::Static::Void, $68: T.class_of(Funcs)): # outerLoops: 1 -> (NilClass ? bb21 : bb19) # backedges # - bb18(rubyRegionId=5) -bb19[rubyRegionId=0, firstDead=12]($71: Sorbet::Private::Static::Void, $72: T.class_of(Funcs)): - $67: Sorbet::Private::Static::Void = Solve<$71, sig> - : T.class_of(Funcs) = $72 - $86: T.class_of(T::Sig) = alias - $88: T.class_of(T) = alias - $83: T.class_of(Funcs) = : T.class_of(Funcs).extend($86: T.class_of(T::Sig)) - $92: Symbol(:f) = :f - $90: T.class_of(Funcs) = : T.class_of(Funcs).private($92: Symbol(:f)) - $96: Symbol(:g) = :g - $94: T.class_of(Funcs) = : T.class_of(Funcs).private($96: Symbol(:g)) - $100: Symbol(:h) = :h - $98: T.class_of(Funcs) = : T.class_of(Funcs).private($100: Symbol(:h)) +bb19[rubyRegionId=0, firstDead=12]($67: Sorbet::Private::Static::Void, $68: T.class_of(Funcs)): + $63: Sorbet::Private::Static::Void = Solve<$67, sig> + : T.class_of(Funcs) = $68 + $81: T.class_of(T::Sig) = alias + $83: T.class_of(T) = alias + $78: T.class_of(Funcs) = : T.class_of(Funcs).extend($81: T.class_of(T::Sig)) + $87: Symbol(:f) = :f + $85: T.class_of(Funcs) = : T.class_of(Funcs).private($87: Symbol(:f)) + $91: Symbol(:g) = :g + $89: T.class_of(Funcs) = : T.class_of(Funcs).private($91: Symbol(:g)) + $95: Symbol(:h) = :h + $93: T.class_of(Funcs) = : T.class_of(Funcs).private($95: Symbol(:h)) : T.noreturn = return $2: NilClass -> bb1 # backedges # - bb18(rubyRegionId=5) -bb21[rubyRegionId=5, firstDead=7](: T.class_of(Funcs), $71: Sorbet::Private::Static::Void, $72: T.class_of(Funcs)): +bb21[rubyRegionId=5, firstDead=7](: T.class_of(Funcs), $67: Sorbet::Private::Static::Void, $68: T.class_of(Funcs)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $77: Symbol(:s) = :s - $79: T.class_of(String) = alias - $75: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($77: Symbol(:s), $79: T.class_of(String)) - $81: T.class_of(String) = alias - $74: T::Private::Methods::DeclBuilder = $75: T::Private::Methods::DeclBuilder.returns($81: T.class_of(String)) - $82: T.noreturn = blockreturn $74: T::Private::Methods::DeclBuilder + $72: Symbol(:s) = :s + $74: T.class_of(String) = alias + $70: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.params($72: Symbol(:s), $74: T.class_of(String)) + $76: T.class_of(String) = alias + $69: T::Private::Methods::DeclBuilder = $70: T::Private::Methods::DeclBuilder.returns($76: T.class_of(String)) + $77: T.noreturn = blockreturn $69: T::Private::Methods::DeclBuilder -> bb18 } diff --git a/test/testdata/namer/module_function.rb.symbol-table-raw.exp b/test/testdata/namer/module_function.rb.symbol-table-raw.exp index 70f2680945..02a9070513 100644 --- a/test/testdata/namer/module_function.rb.symbol-table-raw.exp +++ b/test/testdata/namer/module_function.rb.symbol-table-raw.exp @@ -19,8 +19,7 @@ class >> < > () method ># : private (s, ) -> String @ Loc {file=test/testdata/namer/module_function.rb start=16:3 end=16:11} argument s<> -> String @ Loc {file=test/testdata/namer/module_function.rb start=15:15 end=15:16} argument -> T.untyped @ Loc {file=test/testdata/namer/module_function.rb start=??? end=???} - class > $1>[>>] < > (>) @ Loc {file=test/testdata/namer/module_function.rb start=2:1 end=2:13} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Funcs) @ Loc {file=test/testdata/namer/module_function.rb start=2:1 end=2:13} + class > $1> < > (>) @ Loc {file=test/testdata/namer/module_function.rb start=2:1 end=2:13} method > $1>#> () @ Loc {file=test/testdata/namer/module_function.rb start=2:1 end=17:4} argument @ Loc {file=test/testdata/namer/module_function.rb start=??? end=???} method > $1># (module_function :f, module_function :f) @ Loc {file=test/testdata/namer/module_function.rb start=8:3 end=8:21} diff --git a/test/testdata/namer/redefines_module_as_static_field.rb.symbol-table-raw.exp b/test/testdata/namer/redefines_module_as_static_field.rb.symbol-table-raw.exp index 303aec8eea..0f5d17373b 100644 --- a/test/testdata/namer/redefines_module_as_static_field.rb.symbol-table-raw.exp +++ b/test/testdata/namer/redefines_module_as_static_field.rb.symbol-table-raw.exp @@ -4,34 +4,29 @@ class >> < > () argument @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=??? end=???} module > < >::>::>::> () @ (Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=3:1 end=3:9}, Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/sorbet/t.rbi start=removed end=removed}) module >::> < >::>::>::> () @ (Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=22:1 end=22:24}, Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/sorbet/t.rbi start=removed end=removed}) - module >::>::> < >::>::>::> () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=30:3 end=30:28} - module >::>::>::> < >::>::>::> () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=34:3 end=34:44} + module >::>::> < >::>::>::> () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=38:3 end=38:28} + module >::>::>::> < >::>::>::> () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=38:3 end=38:44} static-field >::>::>::>::> -> AliasType { symbol = >::>::>::> } @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=38:3 end=38:60} static-field >::>::>::>::> -> AliasType { symbol = >::>::>::> } @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=34:3 end=34:51} static-field >::>::>::> $1> -> AliasType { symbol = >::>::>::> } @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=35:3 end=35:44} - class >::>::>::> $1>[>>] < > () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=34:3 end=34:44} - type-member(+) >::>::>::> $1>::>> -> LambdaParam(>::>::>::> $1>::>>, lower=T.noreturn, upper=T::AbstractUtils::Methods::CallValidation) @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=34:3 end=34:44} + class >::>::>::> $1> < > () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=38:3 end=38:44} static-field >::>::>::> -> AliasType { symbol = >::>::>::> } @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=33:3 end=33:35} module >::>::>::> < >::>::>::> () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=30:3 end=30:49} static-field >::>::>::>::> -> AliasType { symbol = >::>::> } @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=30:3 end=30:58} - class >::>::>::> $1>[>>] < > () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=30:3 end=30:49} - type-member(+) >::>::>::> $1>::>> -> LambdaParam(>::>::>::> $1>::>>, lower=T.noreturn, upper=T::AbstractUtils::Methods::SignatureValidation) @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=30:3 end=30:49} + class >::>::>::> $1> < > () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=30:3 end=30:49} static-field >::>::> $1> -> AliasType { symbol = >::>::> } @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=27:3 end=27:28} - class >::>::> $1>[>>] < > () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=30:3 end=30:28} - type-member(+) >::>::> $1>::>> -> LambdaParam(>::>::> $1>::>>, lower=T.noreturn, upper=T::AbstractUtils::Methods) @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=30:3 end=30:28} + class >::>::> $1> < > () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=38:3 end=38:28} module >::> < >::>::>::> () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=7:1 end=7:18} module >::>::> < >::>::>::> () @ (Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/sorbet/tprivate.rbi start=removed end=removed}, Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=10:1 end=10:27}) module >::>::>::> < >::>::>::> () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=16:1 end=16:34} - class >::>::>::> $1>[>>] < > () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=16:1 end=16:34} - type-member(+) >::>::>::> $1>::>> -> LambdaParam(>::>::>::> $1>::>>, lower=T.noreturn, upper=T::Private::Methods::Modes) @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=16:1 end=16:34} + class >::>::>::> $1> < > () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=16:1 end=16:34} method >::>::>::> $1>#> () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=16:1 end=17:4} argument @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=??? end=???} module >::>::>::> < >::>::>::> () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=13:1 end=13:48} - class >::>::>::> $1>[>>] < > () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=13:1 end=13:48} - type-member(+) >::>::>::> $1>::>> -> LambdaParam(>::>::>::> $1>::>>, lower=T.noreturn, upper=T::Private::Methods::SignatureValidation) @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=13:1 end=13:48} + class >::>::>::> $1> < > () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=13:1 end=13:48} method >::>::>::> $1>#> () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=13:1 end=14:4} argument @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=??? end=???} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=7:1 end=7:18} + class >::> $1> < > () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=7:1 end=7:18} method >::> $1>#> () @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=7:1 end=8:4} argument @ Loc {file=test/testdata/namer/redefines_module_as_static_field.rb start=??? end=???} diff --git a/test/testdata/namer/simple.rb.symbol-table-raw.exp b/test/testdata/namer/simple.rb.symbol-table-raw.exp index 6000e1d5d0..f8a83692bc 100644 --- a/test/testdata/namer/simple.rb.symbol-table-raw.exp +++ b/test/testdata/namer/simple.rb.symbol-table-raw.exp @@ -13,8 +13,7 @@ class >> < > () type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=ANamespace::ObviousChild) @ Loc {file=test/testdata/namer/simple.rb start=14:3 end=14:21} method >::> $1>#> () @ Loc {file=test/testdata/namer/simple.rb start=14:3 end=15:6} argument @ Loc {file=test/testdata/namer/simple.rb start=??? end=???} - class > $1>[>>] < > () @ Loc {file=test/testdata/namer/simple.rb start=13:1 end=13:18} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=ANamespace) @ Loc {file=test/testdata/namer/simple.rb start=13:1 end=13:18} + class > $1> < > () @ Loc {file=test/testdata/namer/simple.rb start=13:1 end=13:18} method > $1>#> () @ Loc {file=test/testdata/namer/simple.rb start=13:1 end=16:4} argument @ Loc {file=test/testdata/namer/simple.rb start=??? end=???} class > < > (>) @ Loc {file=test/testdata/namer/simple.rb start=26:1 end=26:21} @@ -23,8 +22,7 @@ class >> < > () method > $1>#> () @ Loc {file=test/testdata/namer/simple.rb start=26:1 end=35:4} argument @ Loc {file=test/testdata/namer/simple.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/namer/simple.rb start=22:1 end=22:13} - class > $1>[>>] < > () @ Loc {file=test/testdata/namer/simple.rb start=22:1 end=22:13} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Mixin) @ Loc {file=test/testdata/namer/simple.rb start=22:1 end=22:13} + class > $1> < > () @ Loc {file=test/testdata/namer/simple.rb start=22:1 end=22:13} method > $1>#> () @ Loc {file=test/testdata/namer/simple.rb start=22:1 end=23:4} argument @ Loc {file=test/testdata/namer/simple.rb start=??? end=???} class > < > () @ Loc {file=test/testdata/namer/simple.rb start=2:1 end=2:18} @@ -34,8 +32,7 @@ class >> < > () method >::> $1>#> () @ Loc {file=test/testdata/namer/simple.rb start=7:3 end=8:6} argument @ Loc {file=test/testdata/namer/simple.rb start=??? end=???} module >::> < >::>::>::> () @ Loc {file=test/testdata/namer/simple.rb start=9:3 end=9:21} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/namer/simple.rb start=9:3 end=9:21} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=NormalClass::InnerModule) @ Loc {file=test/testdata/namer/simple.rb start=9:3 end=9:21} + class >::> $1> < > () @ Loc {file=test/testdata/namer/simple.rb start=9:3 end=9:21} method >::> $1>#> () @ Loc {file=test/testdata/namer/simple.rb start=9:3 end=10:6} argument @ Loc {file=test/testdata/namer/simple.rb start=??? end=???} method ># () @ Loc {file=test/testdata/namer/simple.rb start=3:3 end=3:20} @@ -47,8 +44,7 @@ class >> < > () method > $1># () @ Loc {file=test/testdata/namer/simple.rb start=5:3 end=5:32} argument @ Loc {file=test/testdata/namer/simple.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/namer/simple.rb start=24:1 end=24:18} - class > $1>[>>] < > () @ Loc {file=test/testdata/namer/simple.rb start=24:1 end=24:18} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=OtherMixin) @ Loc {file=test/testdata/namer/simple.rb start=24:1 end=24:18} + class > $1> < > () @ Loc {file=test/testdata/namer/simple.rb start=24:1 end=24:18} method > $1>#> () @ Loc {file=test/testdata/namer/simple.rb start=24:1 end=25:4} argument @ Loc {file=test/testdata/namer/simple.rb start=??? end=???} class > < > () @ Loc {file=test/testdata/namer/simple.rb start=20:1 end=20:13} diff --git a/test/testdata/namer/singleton_class.rb.symbol-table-raw.exp b/test/testdata/namer/singleton_class.rb.symbol-table-raw.exp index 8cf2d38dcd..5397aeac76 100644 --- a/test/testdata/namer/singleton_class.rb.symbol-table-raw.exp +++ b/test/testdata/namer/singleton_class.rb.symbol-table-raw.exp @@ -9,10 +9,8 @@ class >> < > () type-member(+) >::>::> $1>::>> -> LambdaParam(>::>::> $1>::>>, lower=T.noreturn, upper=A::B::C) @ Loc {file=test/testdata/namer/singleton_class.rb start=2:1 end=2:14} method >::>::> $1>#> () @ Loc {file=test/testdata/namer/singleton_class.rb start=2:1 end=3:4} argument @ Loc {file=test/testdata/namer/singleton_class.rb start=??? end=???} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/namer/singleton_class.rb start=2:7 end=2:11} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=A::B) @ Loc {file=test/testdata/namer/singleton_class.rb start=2:7 end=2:11} - class > $1>[>>] < > () @ Loc {file=test/testdata/namer/singleton_class.rb start=2:7 end=2:8} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/namer/singleton_class.rb start=2:7 end=2:8} + class >::> $1> < > () @ Loc {file=test/testdata/namer/singleton_class.rb start=2:7 end=2:11} + class > $1> < > () @ Loc {file=test/testdata/namer/singleton_class.rb start=2:7 end=2:8} class > < > (>) @ Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed} method ># : private () @ Loc {file=test/testdata/namer/singleton_class.rb start=5:1 end=5:9} argument @ Loc {file=test/testdata/namer/singleton_class.rb start=??? end=???} diff --git a/test/testdata/namer/type_alias.rb.symbol-table-raw.exp b/test/testdata/namer/type_alias.rb.symbol-table-raw.exp index 0f5143c203..8fef8cda47 100644 --- a/test/testdata/namer/type_alias.rb.symbol-table-raw.exp +++ b/test/testdata/namer/type_alias.rb.symbol-table-raw.exp @@ -12,8 +12,7 @@ class >> < > () method ># (x, ) -> NilClass @ Loc {file=test/testdata/namer/type_alias.rb start=9:3 end=9:11} argument x<> -> A::C1 @ Loc {file=test/testdata/namer/type_alias.rb start=8:15 end=8:16} argument -> T.untyped @ Loc {file=test/testdata/namer/type_alias.rb start=??? end=???} - class > $1>[>>] < > (>) @ Loc {file=test/testdata/namer/type_alias.rb start=2:1 end=2:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/namer/type_alias.rb start=2:1 end=2:9} + class > $1> < > (>) @ Loc {file=test/testdata/namer/type_alias.rb start=2:1 end=2:9} method > $1>#> () @ Loc {file=test/testdata/namer/type_alias.rb start=2:1 end=11:4} argument @ Loc {file=test/testdata/namer/type_alias.rb start=??? end=???} diff --git a/test/testdata/namer/type_alias_inside_method.rb.symbol-table-raw.exp b/test/testdata/namer/type_alias_inside_method.rb.symbol-table-raw.exp index f845ea206f..88c06d7144 100644 --- a/test/testdata/namer/type_alias_inside_method.rb.symbol-table-raw.exp +++ b/test/testdata/namer/type_alias_inside_method.rb.symbol-table-raw.exp @@ -11,8 +11,7 @@ class >> < > () method ># (x, ) -> NilClass @ Loc {file=test/testdata/namer/type_alias_inside_method.rb start=8:3 end=8:11} argument x<> -> A::C1 @ Loc {file=test/testdata/namer/type_alias_inside_method.rb start=7:15 end=7:16} argument -> T.untyped @ Loc {file=test/testdata/namer/type_alias_inside_method.rb start=??? end=???} - class > $1>[>>] < > (>) @ Loc {file=test/testdata/namer/type_alias_inside_method.rb start=2:1 end=2:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/namer/type_alias_inside_method.rb start=2:1 end=2:9} + class > $1> < > (>) @ Loc {file=test/testdata/namer/type_alias_inside_method.rb start=2:1 end=2:9} method > $1>#> () @ Loc {file=test/testdata/namer/type_alias_inside_method.rb start=2:1 end=12:4} argument @ Loc {file=test/testdata/namer/type_alias_inside_method.rb start=??? end=???} diff --git a/test/testdata/namer/type_member_inside_method.rb.symbol-table-raw.exp b/test/testdata/namer/type_member_inside_method.rb.symbol-table-raw.exp index 5aa0296c39..75f3807c38 100644 --- a/test/testdata/namer/type_member_inside_method.rb.symbol-table-raw.exp +++ b/test/testdata/namer/type_member_inside_method.rb.symbol-table-raw.exp @@ -11,8 +11,7 @@ class >> < > () method ># (x, ) -> NilClass @ Loc {file=test/testdata/namer/type_member_inside_method.rb start=8:3 end=8:11} argument x<> -> A::C1 @ Loc {file=test/testdata/namer/type_member_inside_method.rb start=7:15 end=7:16} argument -> T.untyped @ Loc {file=test/testdata/namer/type_member_inside_method.rb start=??? end=???} - class > $1>[>>] < > (>) @ Loc {file=test/testdata/namer/type_member_inside_method.rb start=2:1 end=2:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/namer/type_member_inside_method.rb start=2:1 end=2:9} + class > $1> < > (>) @ Loc {file=test/testdata/namer/type_member_inside_method.rb start=2:1 end=2:9} method > $1>#> () @ Loc {file=test/testdata/namer/type_member_inside_method.rb start=2:1 end=13:4} argument @ Loc {file=test/testdata/namer/type_member_inside_method.rb start=??? end=???} diff --git a/test/testdata/namer/type_template_inside_method.rb.symbol-table-raw.exp b/test/testdata/namer/type_template_inside_method.rb.symbol-table-raw.exp index c50241424f..aa1d716934 100644 --- a/test/testdata/namer/type_template_inside_method.rb.symbol-table-raw.exp +++ b/test/testdata/namer/type_template_inside_method.rb.symbol-table-raw.exp @@ -11,8 +11,7 @@ class >> < > () method ># (x, ) -> NilClass @ Loc {file=test/testdata/namer/type_template_inside_method.rb start=8:3 end=8:11} argument x<> -> A::C1 @ Loc {file=test/testdata/namer/type_template_inside_method.rb start=7:15 end=7:16} argument -> T.untyped @ Loc {file=test/testdata/namer/type_template_inside_method.rb start=??? end=???} - class > $1>[>>] < > (>) @ Loc {file=test/testdata/namer/type_template_inside_method.rb start=2:1 end=2:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/namer/type_template_inside_method.rb start=2:1 end=2:9} + class > $1> < > (>) @ Loc {file=test/testdata/namer/type_template_inside_method.rb start=2:1 end=2:9} method > $1>#> () @ Loc {file=test/testdata/namer/type_template_inside_method.rb start=2:1 end=13:4} argument @ Loc {file=test/testdata/namer/type_template_inside_method.rb start=??? end=???} diff --git a/test/testdata/namer/visibility.rb.symbol-table-raw.exp b/test/testdata/namer/visibility.rb.symbol-table-raw.exp index 7fb52a60f4..dc7e0a5859 100644 --- a/test/testdata/namer/visibility.rb.symbol-table-raw.exp +++ b/test/testdata/namer/visibility.rb.symbol-table-raw.exp @@ -60,7 +60,7 @@ class >> < > () argument @ Loc {file=test/testdata/namer/visibility.rb start=??? end=???} method > $1># : private () @ Loc {file=test/testdata/namer/visibility.rb start=42:3 end=42:15} argument @ Loc {file=test/testdata/namer/visibility.rb start=??? end=???} - class > $1> $1>[>>] < > $1> $1> () @ Loc {file=test/testdata/namer/visibility.rb start=44:3 end=44:8} + class > $1> $1>[>>] < > () @ Loc {file=test/testdata/namer/visibility.rb start=44:3 end=44:8} type-member(+) > $1> $1>::>> -> LambdaParam(> $1> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > $1> targs = [ >> = Foo3 ] }) @ Loc {file=test/testdata/namer/visibility.rb start=44:3 end=44:8} method > $1> $1>#> () @ Loc {file=test/testdata/namer/visibility.rb start=44:3 end=46:6} argument @ Loc {file=test/testdata/namer/visibility.rb start=??? end=???} @@ -71,7 +71,7 @@ class >> < > () argument @ Loc {file=test/testdata/namer/visibility.rb start=??? end=???} method > $1># : private () @ Loc {file=test/testdata/namer/visibility.rb start=51:5 end=51:12} argument @ Loc {file=test/testdata/namer/visibility.rb start=??? end=???} - class > $1> $1>[>>] < > $1> $1> () @ Loc {file=test/testdata/namer/visibility.rb start=50:3 end=50:8} + class > $1> $1>[>>] < > () @ Loc {file=test/testdata/namer/visibility.rb start=50:3 end=50:8} type-member(+) > $1> $1>::>> -> LambdaParam(> $1> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > $1> targs = [ >> = Foo4 ] }) @ Loc {file=test/testdata/namer/visibility.rb start=50:3 end=50:8} method > $1> $1>#> () @ Loc {file=test/testdata/namer/visibility.rb start=50:3 end=54:6} argument @ Loc {file=test/testdata/namer/visibility.rb start=??? end=???} diff --git a/test/testdata/packager/export_all/__package.rb b/test/testdata/packager/export_all/__package.rb new file mode 100644 index 0000000000..fe0b61cace --- /dev/null +++ b/test/testdata/packager/export_all/__package.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true +# typed: strict +# enable-packager: true + +class Foo::Bar < PackageSpec + export_all! + + export_all!(:with_argument) + # ^^^^^^^^^^^^^^ error: Too many arguments + + export Foo::Bar::Thing + # ^^^^^^^^^^^^^^^ error: Package `Foo::Bar` declares `export_all!` and therefore should not use explicit exports + +end diff --git a/test/testdata/packager/export_all/foo_bar.rb b/test/testdata/packager/export_all/foo_bar.rb new file mode 100644 index 0000000000..d3cc29947b --- /dev/null +++ b/test/testdata/packager/export_all/foo_bar.rb @@ -0,0 +1,13 @@ +# typed: strict + +module Foo::Bar + class Thing + extend T::Sig + + sig {void} + def self.hello; end + end + + class OtherThing + end +end diff --git a/test/testdata/packager/export_all/foo_bar_baz/__package.rb b/test/testdata/packager/export_all/foo_bar_baz/__package.rb new file mode 100644 index 0000000000..4f6758c622 --- /dev/null +++ b/test/testdata/packager/export_all/foo_bar_baz/__package.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true +# typed: strict +# enable-packager: true + +class Foo::Bar::Baz < PackageSpec +end diff --git a/test/testdata/packager/export_all/foo_bar_baz/foo_bar_baz.rb b/test/testdata/packager/export_all/foo_bar_baz/foo_bar_baz.rb new file mode 100644 index 0000000000..dbf5fe0541 --- /dev/null +++ b/test/testdata/packager/export_all/foo_bar_baz/foo_bar_baz.rb @@ -0,0 +1,10 @@ +# typed: strict + +module Foo::Bar::Baz + class Quux + extend T::Sig + + sig { void } + def example; end + end +end diff --git a/test/testdata/packager/export_all/other/__package.rb b/test/testdata/packager/export_all/other/__package.rb new file mode 100644 index 0000000000..ada6f1de48 --- /dev/null +++ b/test/testdata/packager/export_all/other/__package.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true +# typed: strict + +class Other < PackageSpec + import Foo::Bar + import Foo::Bar::Baz + import Typical +end diff --git a/test/testdata/packager/export_all/other/other.rb b/test/testdata/packager/export_all/other/other.rb new file mode 100644 index 0000000000..24958b19a3 --- /dev/null +++ b/test/testdata/packager/export_all/other/other.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true +# typed: strict + +class Other::OtherClass + Foo::Bar::Thing.hello # This ref still works + Foo::Bar::OtherThing # anything from the package should be fine + + # because `Foo::Bar::Baz` is a subpackage, the export walk should stop + # and not export the stuff under that namespace + Foo::Bar::Baz::Quux +# ^^^^^^^^^^^^^^^^^^^ error: `Foo::Bar::Baz::Quux` resolves but is not exported + + Typical::Example # packages that don't use `export_all` are not affected + Typical::NonExported # packages that don't use `export_all` are not affected +# ^^^^^^^^^^^^^^^^^^^^ error: `Typical::NonExported` resolves but is not exported +end diff --git a/test/testdata/packager/export_all/typical/__package.rb b/test/testdata/packager/export_all/typical/__package.rb new file mode 100644 index 0000000000..a671f97ab0 --- /dev/null +++ b/test/testdata/packager/export_all/typical/__package.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true +# typed: strict + +class Typical < PackageSpec + export Typical::Example +end diff --git a/test/testdata/packager/export_all/typical/typical.rb b/test/testdata/packager/export_all/typical/typical.rb new file mode 100644 index 0000000000..f6abd129c7 --- /dev/null +++ b/test/testdata/packager/export_all/typical/typical.rb @@ -0,0 +1,6 @@ +# typed: strict + +module Typical + class Example; end + class NonExported; end +end diff --git a/test/testdata/packager/export_all_in_test/__package.rb b/test/testdata/packager/export_all_in_test/__package.rb new file mode 100644 index 0000000000..488fa73c49 --- /dev/null +++ b/test/testdata/packager/export_all_in_test/__package.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true +# typed: strict +# enable-packager: true + +class Foo::Bar < PackageSpec + export_all! + + export Test::Foo::Bar::Thing + # ^^^^^^^^^^^^^^^^^^^^^ error: Package `Foo::Bar` declares `export_all!` and therefore should not use explicit exports + +end diff --git a/test/testdata/packager/export_all_in_test/other/__package.rb b/test/testdata/packager/export_all_in_test/other/__package.rb new file mode 100644 index 0000000000..3447b6e5c4 --- /dev/null +++ b/test/testdata/packager/export_all_in_test/other/__package.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true +# typed: strict + +class Other < PackageSpec + test_import Foo::Bar + test_import Typical +end diff --git a/test/testdata/packager/export_all_in_test/other/test/other.test.rb b/test/testdata/packager/export_all_in_test/other/test/other.test.rb new file mode 100644 index 0000000000..6d070facea --- /dev/null +++ b/test/testdata/packager/export_all_in_test/other/test/other.test.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true +# typed: strict + +class Test::Other::OtherClass + Test::Foo::Bar::Thing.hello # This ref still works + Test::Foo::Bar::OtherThing # anything from the package should be fine + + Test::Typical::Example # packages that don't use `export_all` are not affected + Test::Typical::NonExported # packages that don't use `export_all` are not affected +# ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `Test::Typical::NonExported` resolves but is not exported +end diff --git a/test/testdata/packager/export_all_in_test/test/foo_bar.test.rb b/test/testdata/packager/export_all_in_test/test/foo_bar.test.rb new file mode 100644 index 0000000000..12788d731b --- /dev/null +++ b/test/testdata/packager/export_all_in_test/test/foo_bar.test.rb @@ -0,0 +1,13 @@ +# typed: strict + +module Test::Foo::Bar + class Thing + extend T::Sig + + sig {void} + def self.hello; end + end + + class OtherThing + end +end diff --git a/test/testdata/packager/export_all_in_test/typical/__package.rb b/test/testdata/packager/export_all_in_test/typical/__package.rb new file mode 100644 index 0000000000..c653232765 --- /dev/null +++ b/test/testdata/packager/export_all_in_test/typical/__package.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true +# typed: strict + +class Typical < PackageSpec + export Test::Typical::Example +end diff --git a/test/testdata/packager/export_all_in_test/typical/test/typical.test.rb b/test/testdata/packager/export_all_in_test/typical/test/typical.test.rb new file mode 100644 index 0000000000..82e9b9c82c --- /dev/null +++ b/test/testdata/packager/export_all_in_test/typical/test/typical.test.rb @@ -0,0 +1,6 @@ +# typed: strict + +module Test::Typical + class Example; end + class NonExported; end +end diff --git a/test/testdata/packager/export_for_test/foo/foo.rb b/test/testdata/packager/export_for_test/foo/foo.rb index dc4ef8c8c0..9a5db67891 100644 --- a/test/testdata/packager/export_for_test/foo/foo.rb +++ b/test/testdata/packager/export_for_test/foo/foo.rb @@ -29,6 +29,7 @@ def self.stub_stuff!; end # util/__package.rb exposed via export_for_test, cannot access from here: Opus::Util::Nesting.nesting_method +# ^^^^^^^^^^^^^^^^^^^ error: `Opus::Util::Nesting` resolves but is not exported from `Opus::Util` # via test_import Opus::TestImported diff --git a/test/testdata/packager/export_for_test/foo/foo.test.rb b/test/testdata/packager/export_for_test/foo/foo.test.rb index c65e3c3877..a47f6e58e5 100644 --- a/test/testdata/packager/export_for_test/foo/foo.test.rb +++ b/test/testdata/packager/export_for_test/foo/foo.test.rb @@ -13,6 +13,7 @@ class Test::Opus::Foo::FooTest # util/__package.rb exposed via export_for_test, cannot access from here: Opus::Util::Nesting.nesting_method +# ^^^^^^^^^^^^^^^^^^^ error: `Opus::Util::Nesting` resolves but is not exported from `Opus::Util` # via test_import Opus::TestImported Opus::TestImported::TIClass diff --git a/test/testdata/packager/lsp_update_packaged_file_static_field/__package.rb b/test/testdata/packager/lsp_update_packaged_file_static_field/__package.rb new file mode 100644 index 0000000000..f35451e9dc --- /dev/null +++ b/test/testdata/packager/lsp_update_packaged_file_static_field/__package.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true +# typed: strict +# enable-packager: true + +class Package < PackageSpec + import Dep + + export Package::A +end diff --git a/test/testdata/packager/lsp_update_packaged_file_static_field/a.1.rbupdate b/test/testdata/packager/lsp_update_packaged_file_static_field/a.1.rbupdate new file mode 100644 index 0000000000..153f6e9cae --- /dev/null +++ b/test/testdata/packager/lsp_update_packaged_file_static_field/a.1.rbupdate @@ -0,0 +1,18 @@ +# frozen_string_literal: true +# typed: strict +# assert-fast-path: a.rb,__package.rb,dep/exported.rb,dep/__package.rb + +class Package::A + extend T::Sig + + sig {returns(String)} + def self.a + Dep::Exports::ExportedClass::Val + "asdf" + end + + sig {returns(Integer)} + def self.b + 2 + end +end diff --git a/test/testdata/packager/lsp_update_packaged_file_static_field/a.rb b/test/testdata/packager/lsp_update_packaged_file_static_field/a.rb new file mode 100644 index 0000000000..68ceb4d65b --- /dev/null +++ b/test/testdata/packager/lsp_update_packaged_file_static_field/a.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true +# typed: strict + +class Package::A + extend T::Sig + + sig {returns(String)} + def self.a + Dep::Exports::ExportedClass::Val + "asdf" + end + + sig {returns(String)} + def self.b + "foo" + end +end diff --git a/test/testdata/packager/lsp_update_packaged_file_static_field/dep/__package.rb b/test/testdata/packager/lsp_update_packaged_file_static_field/dep/__package.rb new file mode 100644 index 0000000000..b742252f89 --- /dev/null +++ b/test/testdata/packager/lsp_update_packaged_file_static_field/dep/__package.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true +# typed: strict + +class Dep < PackageSpec + import Package + + export Dep::Exports::ExportedClass +end diff --git a/test/testdata/packager/lsp_update_packaged_file_static_field/dep/exported.rb b/test/testdata/packager/lsp_update_packaged_file_static_field/dep/exported.rb new file mode 100644 index 0000000000..d8250fff54 --- /dev/null +++ b/test/testdata/packager/lsp_update_packaged_file_static_field/dep/exported.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true +# typed: strict + +module Dep::Exports + class ExportedClass + Val = 1 + end + + Package::A.b +end diff --git a/test/testdata/packager/nested_package_enforcement/inner/inner.rb b/test/testdata/packager/nested_package_enforcement/inner/inner.rb index 830e092284..79dc7c15d9 100644 --- a/test/testdata/packager/nested_package_enforcement/inner/inner.rb +++ b/test/testdata/packager/nested_package_enforcement/inner/inner.rb @@ -3,7 +3,7 @@ module Outer def self.bad -# ^^^^^^^^^^^^ error: Class or method behavior may not be defined outside of the enclosing package namespace `Outer::Inner` +# ^^^^^^^^^^^^ error: This file must only define behavior in enclosing package `Outer::Inner` end @@ -13,5 +13,5 @@ module Mixin; end end include Inner::Mixin -# ^^^^^^^^^^^^^^^^^^^^ error: Class or method behavior may not be defined outside of the enclosing package namespace `Outer::Inner` +# ^^^^^^^^^^^^^^^^^^^^ error: This file must only define behavior in enclosing package `Outer::Inner` end diff --git a/test/testdata/packager/package-prefix-enforcement/nested/nested.rb b/test/testdata/packager/package-prefix-enforcement/nested/nested.rb index 6476b2efb2..980852c625 100644 --- a/test/testdata/packager/package-prefix-enforcement/nested/nested.rb +++ b/test/testdata/packager/package-prefix-enforcement/nested/nested.rb @@ -41,7 +41,7 @@ class Deep2; end end class Root::Stringy < String -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Class or method behavior may not be defined outside of the enclosing package namespace `Root::Nested` +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: This file must only define behavior in enclosing package `Root::Nested` end class Root::Nested::Stringy < String @@ -49,14 +49,14 @@ class Root::Nested::Stringy < String module Root extend T::Sig -# ^^^^^^^^^^^^^ error: Class or method behavior may not be defined outside of the enclosing package namespace `Root::Nested` +# ^^^^^^^^^^^^^ error: This file must only define behavior in enclosing package `Root::Nested` NOT_IN_PACKAGE = T.let(1, Integer) # ^^^^^^^^^^^^^^ error: File belongs to package `Root::Nested` but defines a constant that does not match this namespace sig {returns(NilClass)} -# ^^^^^^^^^^^^^^^^^^^^^^^ error: Class or method behavior may not be defined outside of the enclosing package namespace `Root::Nested` +# ^^^^^^^^^^^^^^^^^^^^^^^ error: This file must only define behavior in enclosing package `Root::Nested` def self.method -# ^^^^^^^^^^^^^^^ error: Class or method behavior may not be defined outside of the enclosing package namespace `Root::Nested` +# ^^^^^^^^^^^^^^^ error: This file must only define behavior in enclosing package `Root::Nested` nil end end diff --git a/test/testdata/packager/visibility/bar/__package.rb b/test/testdata/packager/visibility/bar/__package.rb new file mode 100644 index 0000000000..5a638e2d4c --- /dev/null +++ b/test/testdata/packager/visibility/bar/__package.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true +# typed: strict +# enable-packager: true + +class Bar < PackageSpec + import Foo +end diff --git a/test/testdata/packager/visibility/baz/__package.rb b/test/testdata/packager/visibility/baz/__package.rb new file mode 100644 index 0000000000..7952eb7346 --- /dev/null +++ b/test/testdata/packager/visibility/baz/__package.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true +# typed: strict +# enable-packager: true + +class Baz < PackageSpec + import Foo # error: Package `Foo` includes explicit visibility modifiers and cannot be imported from `Baz` +end diff --git a/test/testdata/packager/visibility/foo/__package.rb b/test/testdata/packager/visibility/foo/__package.rb new file mode 100644 index 0000000000..a5f2516fa2 --- /dev/null +++ b/test/testdata/packager/visibility/foo/__package.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true +# typed: strict +# enable-packager: true +# skip-package-import-visibility-check-for: SkipCheck::For + +class Foo < PackageSpec + visible_to Bar + visible_to Quux + # ^^^^ error: Unable to resolve constant `Quux` +end diff --git a/test/testdata/packager/visibility/skip_check/__package.rb b/test/testdata/packager/visibility/skip_check/__package.rb new file mode 100644 index 0000000000..e3e9729848 --- /dev/null +++ b/test/testdata/packager/visibility/skip_check/__package.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true +# typed: strict +# enable-packager: true + +class SkipCheck::For < PackageSpec + import Foo +end diff --git a/test/testdata/parser/error_recovery/case_1.rb b/test/testdata/parser/error_recovery/case_1.rb index 07281fccc8..16388c809c 100644 --- a/test/testdata/parser/error_recovery/case_1.rb +++ b/test/testdata/parser/error_recovery/case_1.rb @@ -20,8 +20,6 @@ def test2 puts 'after' end - # TODO(jez) cool autocompletion opportunity here would be to automatically - # fill in all the when cases if `x` is an enum type. def test3 puts 'before' case x # error: Hint: this "case" token might not be properly closed diff --git a/test/testdata/rbi/argf.rb b/test/testdata/rbi/argf.rb index 5af8b1b279..46fba0cbcb 100644 --- a/test/testdata/rbi/argf.rb +++ b/test/testdata/rbi/argf.rb @@ -20,10 +20,6 @@ ARGF.each_byte ARGF.each_char ARGF.each_codepoint -ARGF.lines -ARGF.bytes -ARGF.chars -ARGF.codepoints ARGF.read ARGF.readpartial ARGF.read_nonblock diff --git a/test/testdata/rbi/array.rb b/test/testdata/rbi/array.rb index 3b2cd53de9..afbd0031d2 100644 --- a/test/testdata/rbi/array.rb +++ b/test/testdata/rbi/array.rb @@ -59,3 +59,14 @@ T.assert_type!([1, 2].to_set { |x| x + 1 }, T::Set[T.untyped]) T.assert_type!([1, 2].to_set, T::Set[T.untyped]) + +# intersecting +arr = [1, 2, 3] +T.assert_type!(arr.intersection([3, 5]), T::Array[Integer]) +T.assert_type!(arr.intersect?([2, 7]), T::Boolean) + +T.reveal_type(arr.fetch(0, -1)) # error: Revealed type: `Integer` +T.reveal_type(arr.fetch(0) { 1 }) # error: Revealed type: `Integer` + +T.reveal_type(arr.fetch(0, 'error')) # error: Revealed type: `T.any(Integer, String)` +T.reveal_type(arr.fetch(0) { 'error' }) # error: Revealed type: `T.any(Integer, String)` diff --git a/test/testdata/rbi/class.rb b/test/testdata/rbi/class.rb index 8f22f0ac80..cb16b11aea 100644 --- a/test/testdata/rbi/class.rb +++ b/test/testdata/rbi/class.rb @@ -1,23 +1,40 @@ - # typed: true + class Parent def self.foo; end end -Class.new -Class.new(Parent) +Parent.singleton_class.attached_object +Parent.attached_object +Parent.new.singleton_class.attached_object + +c1 = Class.new +T.reveal_type(c1) # error: Revealed type: `T::Class[Object]` +T.reveal_type(c1.new) # error: Revealed type: `Object` -Class.new {|cls| cls.superclass} -Class.new(Parent) {|cls| cls.superclass} +c2 = Class.new(Parent) +T.reveal_type(c2) # error: Revealed type: `T.class_of(Parent)` +T.reveal_type(c2.new) # error: Revealed type: `Parent` + +c3 = Class.new { |cls| cls.superclass } +T.reveal_type(c3) # error: Revealed type: `T::Class[Object]` +T.reveal_type(c3.new) # error: Revealed type: `Object` + +c4 = Class.new(Parent) { |cls| cls.superclass } +T.reveal_type(c4) # error: Revealed type: `T.class_of(Parent)` +T.reveal_type(c4.new) # error: Revealed type: `Parent` # Our ClassNew Rewriter pass can only re-write Class.new where the lefthand # side is assigned to a constant -Class.new(Parent).foo # error: Method `foo` does not exist on `Class` +Class.new(Parent).foo c = Class.new(Parent) -c.foo # error: Method `foo` does not exist on `Class` +c.foo +c.new.foo # error: Method `foo` does not exist on `Parent` C = Class.new(Parent) do |cls| cls.foo foo end C.foo + +Class.new('Foo') # error: Expected `T.all(T::Class[T.anything], T.type_parameter(:Parent))` but found `String("Foo")` for argument `super_class` diff --git a/test/testdata/rbi/data.rb b/test/testdata/rbi/data.rb new file mode 100644 index 0000000000..e91c3101fb --- /dev/null +++ b/test/testdata/rbi/data.rb @@ -0,0 +1,79 @@ +# typed: true + +class Define + Data.define # error: Not enough arguments provided for method `Data.define`. Expected: `1+`, got: `0` + Data.define(:x, :y) + Data.define("x", "y") + + Data.define(:x) do + # Takes a block + end +end + +Point = Data.define(:x, :y) + +class ValidInitializers + T.assert_type!(Point, T.class_of(Point)) + + point = Point.new(1, 2) + point.x + point.y + T.assert_type!(point, Point) + + point = Point.new(x: 1, y: 2) + point.x + point.y + T.assert_type!(point, Point) + + point = Point[1, 2] + point = Point[x: 1, y: 2] +end + +class InstanceMethods + point = Point.new(1, 2) + other_point = Point.new(3, 4) + + # == + T.assert_type!(point == other_point, T::Boolean) + T.assert_type!(point == 'random', T::Boolean) + + # eql? + T.assert_type!(point.eql?(other_point), T::Boolean) + T.assert_type!(point.eql?('random'), T::Boolean) + + # deconstruct + T.assert_type!(point.deconstruct, T::Array[T.untyped]) + + # deconstruct_keys + T.assert_type!( + point.deconstruct_keys(nil), + T::Hash[Symbol, T.untyped] + ) + T.assert_type!( + point.deconstruct_keys([:x]), + T::Hash[Symbol, T.untyped] + ) + T.assert_type!( + point.deconstruct_keys(["x", "y"]), + T::Hash[Symbol, T.untyped] + ) + + # hash + T.assert_type!(point.hash, Integer) + + # inspect + T.assert_type!(point.inspect, String) + + # members + T.assert_type!(point.members, T::Array[Symbol]) + + # to_h + T.assert_type!(point.to_h, T::Hash[T.untyped, T.untyped]) + point.to_h do |name, value| + T.assert_type!(name, Symbol) + end + + # with + new_point = point.with(x: 5) + T.assert_type!(new_point, Point) +end diff --git a/test/testdata/rbi/enumerable.rb b/test/testdata/rbi/enumerable.rb index af3717af08..91bd02a057 100644 --- a/test/testdata/rbi/enumerable.rb +++ b/test/testdata/rbi/enumerable.rb @@ -21,7 +21,10 @@ T.assert_type!([1].lazy, T::Enumerator::Lazy[Integer]) T.assert_type!([1, 2].filter_map { |x| x.odd? ? x.to_f : x.to_s }, T::Array[T.any(Float, String)]) + T.assert_type!([1, 2, "3", nil].tally, T::Hash[T.nilable(T.any(Integer, String)), Integer]) +T.assert_type!([1, 2, "3", nil].tally(T::Hash[T.nilable(T.any(Integer, String)), Integer].new), + T::Hash[T.nilable(T.any(Integer, String)), Integer]) # There are 3 different ways to call all?, any? and none? a = [1, 3, 20] @@ -54,6 +57,15 @@ T.reveal_type([1,2].detect(p) {|x| false}) # error: Revealed type: `Integer` T.reveal_type([1,2].detect(p)) # error: Revealed type: `T::Enumerator[Integer]` +# find +p = T.let(->{ 1 }, T.proc.returns(Integer)) +T.reveal_type([1,2].find) # error: Revealed type: `T::Enumerator[Integer]` +T.reveal_type([1,2].find {|x| false}) # error: Revealed type: `T.nilable(Integer)` +T.reveal_type([1,2].find(-> {}) {|x| false}) # error: Revealed type: `T.untyped` +T.reveal_type([1,2].find(-> {})) # error: Revealed type: `T::Enumerator[T.untyped]` +T.reveal_type([1,2].find(p) {|x| false}) # error: Revealed type: `Integer` +T.reveal_type([1,2].find(p)) # error: Revealed type: `T::Enumerator[Integer]` + sig {params(xs: T::Array[Integer]).void} def example(xs) res = xs.reduce('') do |acc, x| diff --git a/test/testdata/rbi/enumerator_chain.rb b/test/testdata/rbi/enumerator_chain.rb new file mode 100644 index 0000000000..21b6f4a009 --- /dev/null +++ b/test/testdata/rbi/enumerator_chain.rb @@ -0,0 +1,11 @@ +# typed: true + +chain1 = Enumerator::Chain.new([1,2], [:foo,:bar]) + +T.assert_type!(chain1, T::Enumerator::Chain[T.any(Integer, Symbol)]) +T.assert_type!(chain1, T::Enumerable[T.any(Integer, Symbol)]) + +chain2 = [1,2].chain([:foo,:bar]) + +T.assert_type!(chain2, T::Enumerator::Chain[T.any(Integer, Symbol)]) +T.assert_type!(chain2, T::Enumerable[T.any(Integer, Symbol)]) diff --git a/test/testdata/rbi/fileutils.rb b/test/testdata/rbi/fileutils.rb new file mode 100644 index 0000000000..ef63a13b70 --- /dev/null +++ b/test/testdata/rbi/fileutils.rb @@ -0,0 +1,3 @@ +# typed: true + +FileUtils.cp_r('src0.txt', 'dest0.txt', preserve: true) diff --git a/test/testdata/rbi/gc.rbi b/test/testdata/rbi/gc.rbi new file mode 100644 index 0000000000..325da938de --- /dev/null +++ b/test/testdata/rbi/gc.rbi @@ -0,0 +1,30 @@ +# typed: true + +T.assert_type!( + GC.stat_heap, + T::Hash[Integer, T::Hash[Symbol, Integer]] +) + +T.assert_type!( + GC.stat_heap(0), + T::Hash[Symbol, Integer] +) + +T.assert_type!( + GC.stat_heap(0, :slot_size), + Integer +) + +all_stats = T.let({}, T::Hash[T.untyped, T.untyped]) + +T.assert_type!( + GC.stat_heap(nil, all_stats), + T::Hash[T.untyped, T.untyped] +) + +stats = T.let({}, T::Hash[T.untyped, T.untyped]) + +T.assert_type!( + GC.stat_heap(0, stats), + T::Hash[T.untyped, T.untyped] +) diff --git a/test/testdata/rbi/hash.rb b/test/testdata/rbi/hash.rb index d9a0641a88..f62f451f9a 100644 --- a/test/testdata/rbi/hash.rb +++ b/test/testdata/rbi/hash.rb @@ -97,3 +97,6 @@ T.reveal_type(key) # error: Revealed type: `Symbol` T.reveal_type(value) # error: Revealed type: `Integer` end) + +T.assert_type!({a: 1}.shift, T.nilable(T::Array[T.untyped])) +T.assert_type!({}.shift, T.nilable(T::Array[T.untyped])) diff --git a/test/testdata/rbi/kernel.rb b/test/testdata/rbi/kernel.rb index 15bdf81e22..93d68f34f9 100644 --- a/test/testdata/rbi/kernel.rb +++ b/test/testdata/rbi/kernel.rb @@ -1,5 +1,7 @@ # typed: true +extend T::Sig + T.assert_type!(caller, T::Array[String]) T.assert_type!(caller(10), T.nilable(T::Array[String])) @@ -33,6 +35,11 @@ def raise_test raise ArgumentError, 'bad argument', nil end +sig { params(obj_or_str: T.any(Exception, String)).void } +def raise_obj_or_string(obj_or_str) + raise obj_or_str +end + # make sure we don't regress and mark these as errors env = {'VAR' => 'VAL'} system('echo') @@ -65,9 +72,20 @@ def raise_test obj = T.let("foo", String) T.assert_type!(obj.itself, String) -y = loop do -end -puts y # error: This code is unreachable + +# These types are deliberately wrong, because `Kernel#p` is difficult to type +# in an RBI. See the comments in kernel.rbi. +p_result = Kernel.p 1 +# This should be `Integer`. +T.reveal_type(p_result) # error: Revealed type: `NilClass` + +p_result = p "string" +# This should be `String`. +T.reveal_type(p_result) # error: Revealed type: `NilClass` + +p_result = p 1, 2 +# This should be `[1, 2]` or `T::Array[T.untyped]` +T.reveal_type(p_result) # error: Revealed type: `NilClass` class CustomError < StandardError def initialize(cause, team) @@ -87,3 +105,34 @@ def raises_raise def fail_class_message fail StandardError, "message" end + +y = loop do +end +puts y # error: This code is unreachable + +class Test + def test + a = 1 + b = 2 + end +end + +set_trace_func proc { |event, file, line, id, binding, classname| + printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname +} +t = Test.new +t.test + +set_trace_func(nil) + +require "continuation" +callcc {|cont| + for i in 0..4 + print "\n#{i}: " + for j in i*5...(i+1)*5 + cont.call() if j == 17 + printf "%3d", j + end + end +} +puts diff --git a/test/testdata/rbi/matrix.rb b/test/testdata/rbi/matrix.rb new file mode 100644 index 0000000000..930381f58d --- /dev/null +++ b/test/testdata/rbi/matrix.rb @@ -0,0 +1,11 @@ +# typed: true +require "matrix" + +class Foo + extend T::Sig + + sig { params(matrix: Matrix).void } + def initialize(matrix:) + @matrix = matrix + end +end diff --git a/test/testdata/rbi/objspace.rb b/test/testdata/rbi/objspace.rb new file mode 100644 index 0000000000..b024e1f172 --- /dev/null +++ b/test/testdata/rbi/objspace.rb @@ -0,0 +1,4 @@ +# typed: true + +ObjectSpace::WeakMap.new +X = ObjectSpace::WeakMap.new diff --git a/test/testdata/rbi/pathname.rb b/test/testdata/rbi/pathname.rb index 973fb3785f..b5e6f2bbe4 100644 --- a/test/testdata/rbi/pathname.rb +++ b/test/testdata/rbi/pathname.rb @@ -7,3 +7,12 @@ break pn if pn.to_s.end_with?('.md') end T.reveal_type(md_file) # error: `T.nilable(Pathname)` + +T.reveal_type(Pathname('/')) # error: `Pathname` +T.reveal_type(Pathname(Pathname('/'))) # error: `Pathname` + +T.reveal_type(Pathname.glob(['*.rb', Pathname('/')])) # error: `T::Array[Pathname]` +T.reveal_type(Pathname.glob(['*.rb', Pathname('/')]) { puts _1 }) # error: `NilClass` + +T.reveal_type(Pathname('/usr/bin').glob(['*.d', Pathname('/')])) # error: `T::Array[Pathname]` +T.reveal_type(Pathname('/usr/bin').glob(['*.d', Pathname('/')]) { puts _1 }) # error: `NilClass` diff --git a/test/testdata/rbi/regexp.rb b/test/testdata/rbi/regexp.rb index b8b5794233..86d5068095 100644 --- a/test/testdata/rbi/regexp.rb +++ b/test/testdata/rbi/regexp.rb @@ -3,6 +3,25 @@ maybe_match = /foo/.match('foo') T.reveal_type(maybe_match) # error: type: `T.nilable(MatchData)` +if maybe_match + b1 = maybe_match.begin(0) + T.reveal_type(b1) # error: type: `Integer` + e1 = maybe_match.end(0) + T.reveal_type(e1) # error: type: `Integer` + + # These are nonsensical because there are no capture groups in + # the original regexp, but Sorbet doesn't know that. + b2 = maybe_match.begin(:nope) + T.reveal_type(b2) # error: type: `Integer` + e2 = maybe_match.end(:nope) + T.reveal_type(e2) # error: type: `Integer` + + b3 = maybe_match.begin("nope") + T.reveal_type(b3) # error: type: `Integer` + e3 = maybe_match.end("nope") + T.reveal_type(e3) # error: type: `Integer` +end + /foo/.match('foo') do |m| T.reveal_type(m) # error: type: `MatchData` end @@ -13,3 +32,8 @@ T.reveal_type(Regexp.compile('foo')) # error: type: `Regexp` T.reveal_type(Regexp.compile('foo', Regexp::EXTENDED | Regexp::IGNORECASE)) # error: type: `Regexp` T.reveal_type(Regexp.compile(/foo/)) # error: type: `Regexp` + +T.reveal_type(Regexp.timeout) # error: type: `T.nilable(Float)` +T.reveal_type(Regexp.timeout = 3.0) # error: type: `Float(3.000000)` +T.reveal_type(Regexp.timeout) # error: type: `T.nilable(Float)` +T.reveal_type(Regexp.timeout = nil) # error: type: `NilClass` diff --git a/test/testdata/rbi/uri.rb b/test/testdata/rbi/uri.rb index 9ea16d48da..5f943573d8 100644 --- a/test/testdata/rbi/uri.rb +++ b/test/testdata/rbi/uri.rb @@ -9,7 +9,7 @@ def validate_http(uri_string) uri end -sig {returns(Class)} +sig {returns(T::Class[T.anything])} def uri_parser URI::Parser end diff --git a/test/testdata/resolver/abstract_abstract.rb b/test/testdata/resolver/abstract_abstract.rb new file mode 100644 index 0000000000..33e0d61865 --- /dev/null +++ b/test/testdata/resolver/abstract_abstract.rb @@ -0,0 +1,23 @@ +# typed: true + +class Parent + extend T::Sig + extend T::Helpers + abstract! + + sig {abstract.returns(T.nilable(Integer))} + def example1; end + + sig {abstract.returns(Integer)} + def example2; end +end + +class Child < Parent + abstract! + + sig {abstract.returns(Integer)} + def example1; end + + sig {abstract.returns(T.nilable(Integer))} + def example2; end # error: Return type `T.nilable(Integer)` does not match return type of abstract method `Parent#example2` +end diff --git a/test/testdata/resolver/abstract_initialization.rb b/test/testdata/resolver/abstract_initialization.rb new file mode 100644 index 0000000000..5d2135a5c0 --- /dev/null +++ b/test/testdata/resolver/abstract_initialization.rb @@ -0,0 +1,71 @@ +# typed: true + +class Abstract + extend T::Sig + extend T::Helpers + abstract! + + sig {abstract.void} + def foo; end +end + +Abstract.new # error: Attempt to instantiate abstract class `Abstract` + +class SubclassOfAbstract < Abstract + def foo; end +end + +SubclassOfAbstract.new + +class AbstractWithSingletonNew + extend T::Helpers + abstract! + + def self.new; end +end + +AbstractWithSingletonNew.new + +class SingletonNew + def self.new; end +end + +class AbstractInheritedFromSingletonNew < SingletonNew + extend T::Helpers + abstract! +end + +AbstractInheritedFromSingletonNew.new + +module ModuleNew + def new; end +end + +class Bar + extend ModuleNew + extend T::Helpers + abstract! +end + +Bar.new + +class CommonRubyPattern + extend T::Sig + + class A + extend T::Helpers + abstract! + end + + class B < A; end + + sig{ params(a: T.class_of(A)).void } + def takesA(a) + a.new + end + + def assignsA + a = T.let(B, T.class_of(A)) + a.new + end +end diff --git a/test/testdata/resolver/abstract_override_kwargs.rb b/test/testdata/resolver/abstract_override_kwargs.rb new file mode 100644 index 0000000000..941f9d221e --- /dev/null +++ b/test/testdata/resolver/abstract_override_kwargs.rb @@ -0,0 +1,85 @@ +# typed: strict + +module I + extend T::Sig + extend T::Helpers + interface! + + sig do + abstract.params( + a: Integer, + b: Integer, + c: Integer, + d: Integer, + e: Integer, + f: Integer, + g: Integer, + h: Integer, + i: Integer, + j: Integer, + k: Integer, + l: Integer, + m: Integer, + n: Integer + ).returns(Integer) + end + def foo( + a:, + b:, + c:, + d:, + e:, + f:, + g: 10, + h: 10, + i: 10, + j: 10, + k: 10, + l: 10, + m: 10, + n: 10 + ) + end +end + +class C + extend T::Sig + include I + + sig do + override.params( + a: Integer, + b: Integer, + c: Integer, + d: Integer, + e: Integer, + f: Integer, + g: Integer, + h: Integer, + i: Integer, + j: Integer, + k: Integer, + l: Integer, + m: Integer, + n: Integer + ).returns(Integer) + end + def foo( + a:, + b:, + c:, + d:, + e:, + f:, + g: 10, + h: 10, + i: 10, + j: 10, + k: 10, + l: 10, + m: 10, + n: 10 + ) + 10 + end +end diff --git a/test/testdata/resolver/abstract_validation.rb b/test/testdata/resolver/abstract_validation.rb index 0cc3a406da..c11c6f8381 100644 --- a/test/testdata/resolver/abstract_validation.rb +++ b/test/testdata/resolver/abstract_validation.rb @@ -137,7 +137,7 @@ module PrivateMethodInInterface interface! sig {abstract.returns(Object)} - private def bad; end # error: Interface method `PrivateMethodInInterface#bad` cannot be private + private def ok; end end module ProtectedMethodInInterface diff --git a/test/testdata/resolver/attached_class_bound.rb b/test/testdata/resolver/attached_class_bound.rb new file mode 100644 index 0000000000..365d84d895 --- /dev/null +++ b/test/testdata/resolver/attached_class_bound.rb @@ -0,0 +1,68 @@ +# typed: strict + +class Wrapper + extend T::Sig + extend T::Generic + + X = type_member(:out) { {upper: ParentThing} } + + sig {params(x: X).void} + def initialize(x) + @x = x + end +end + +class ParentThing + extend T::Sig + extend T::Generic + + # ParentThing:: <: ParentThing + # + # so + # + # Wrapper[ParentThing::] <: Wrapper[ParentThing] + + sig {void} + def self.example + x = self.new + T.reveal_type(x) # error: `T.attached_class (of ParentThing)` + y = T.let(x, ParentThing) + + ex = Wrapper[T.attached_class].new(x) + T.reveal_type(ex) # error: `Wrapper[T.attached_class (of ParentThing)]` + ex2 = T.let(ex, Wrapper[ParentThing]) + end + + sig {returns(Wrapper[T.attached_class])} + def self.make_thing_and_wrap + thing = self.new + T.reveal_type(thing) # error: `T.attached_class (of ParentThing)` + x = Wrapper[T.attached_class].new(thing) + p(x) + x + end +end + +class ChildThing < ParentThing +end + +parent_thing = ParentThing.make_thing_and_wrap +T.reveal_type(parent_thing) # error: `Wrapper[ParentThing]` + +child_thing = ChildThing.make_thing_and_wrap +T.reveal_type(child_thing) # error: `Wrapper[ChildThing]` + +class Unrelated + extend T::Sig + extend T::Generic + + sig {returns(Wrapper[T.attached_class])} + # ^^^^^^^^^^^^^^^^ error: `T.attached_class (of Unrelated)` is not a subtype of upper bound of type member `::Wrapper::X + def self.foo + unrelated = self.new + x = Wrapper[T.attached_class].new(unrelated) + # ^^^^^^^^^^^^^^^^ error: `T.attached_class (of Unrelated)` is not a subtype of upper bound of type member `::Wrapper::X + p(x) + x + end +end diff --git a/test/testdata/resolver/basic_constant_out_of_order__1.rb b/test/testdata/resolver/basic_constant_out_of_order__1.rb new file mode 100644 index 0000000000..894ac91799 --- /dev/null +++ b/test/testdata/resolver/basic_constant_out_of_order__1.rb @@ -0,0 +1,53 @@ +# check-out-of-order-constant-references: true +# typed: false + +module Foo + # This reports an error despite Foo::X also having a definition in the RBI file. + A = X + # ^ error: `Foo::X` referenced before it is defined + + def self.foo(arg:) + X # this is ok + end + + def self.bar(&blk) + X # this is ok + end + + foo arg: ->{ X } # this is ok + + bar do + X # this is ok + end + + class X; end + + B = X # this is ok + + class Bar + p(Foo::Y) + # ^^^^^^ error: `Foo::Y` referenced before it is defined + end + + class X; end + + class Bar + Foo.bar do + p(Foo::Y) # this is ok + end + end + + Y = 1 + + Y = 2 + + class Bar + p(Foo::Y) # this is ok + end + + Y = 3 + + p(Foo::Z) # this is ok since Foo::Z is also defined in another file + class Z + end +end diff --git a/test/testdata/resolver/basic_constant_out_of_order__2.rbi b/test/testdata/resolver/basic_constant_out_of_order__2.rbi new file mode 100644 index 0000000000..d9d9db2b56 --- /dev/null +++ b/test/testdata/resolver/basic_constant_out_of_order__2.rbi @@ -0,0 +1,6 @@ +# check-out-of-order-constant-references: true +# typed: false + +module Foo + class X; end +end diff --git a/test/testdata/resolver/basic_constant_out_of_order__3.rb b/test/testdata/resolver/basic_constant_out_of_order__3.rb new file mode 100644 index 0000000000..a32a53e0df --- /dev/null +++ b/test/testdata/resolver/basic_constant_out_of_order__3.rb @@ -0,0 +1,8 @@ +# check-out-of-order-constant-references: true +# typed: strict + +module Foo + class Z + end +end + diff --git a/test/testdata/resolver/cbase.rb.symbol-table-raw.exp b/test/testdata/resolver/cbase.rb.symbol-table-raw.exp index 3f40e4a830..7c2feccab9 100644 --- a/test/testdata/resolver/cbase.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/cbase.rb.symbol-table-raw.exp @@ -3,13 +3,11 @@ class >> < > () method >> $1>#> $CENSORED> () @ Loc {file=test/testdata/resolver/cbase.rb start=2:1 end=8:2} argument @ Loc {file=test/testdata/resolver/cbase.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/cbase.rb start=2:1 end=2:9} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/cbase.rb start=2:1 end=2:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/resolver/cbase.rb start=2:1 end=2:9} + class > $1> < > () @ Loc {file=test/testdata/resolver/cbase.rb start=2:1 end=2:9} method > $1>#> () @ Loc {file=test/testdata/resolver/cbase.rb start=2:1 end=5:4} argument @ Loc {file=test/testdata/resolver/cbase.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/cbase.rb start=3:3 end=3:13} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/cbase.rb start=3:3 end=3:13} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=B) @ Loc {file=test/testdata/resolver/cbase.rb start=3:3 end=3:13} + class > $1> < > () @ Loc {file=test/testdata/resolver/cbase.rb start=3:3 end=3:13} method > $1>#> () @ Loc {file=test/testdata/resolver/cbase.rb start=3:3 end=4:6} argument @ Loc {file=test/testdata/resolver/cbase.rb start=??? end=???} diff --git a/test/testdata/resolver/class_instance_vars.rb.symbol-table-raw.exp b/test/testdata/resolver/class_instance_vars.rb.symbol-table-raw.exp index ccdc610a84..f0e21b8bb0 100644 --- a/test/testdata/resolver/class_instance_vars.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/class_instance_vars.rb.symbol-table-raw.exp @@ -17,8 +17,7 @@ class >> < > () argument @ Loc {file=test/testdata/resolver/class_instance_vars.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/class_instance_vars.rb start=15:1 end=15:13} static-field >:: -> Integer @ Loc {file=test/testdata/resolver/class_instance_vars.rb start=16:3 end=16:15} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/class_instance_vars.rb start=15:1 end=15:13} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Mixin) @ Loc {file=test/testdata/resolver/class_instance_vars.rb start=15:1 end=15:13} + class > $1> < > () @ Loc {file=test/testdata/resolver/class_instance_vars.rb start=15:1 end=15:13} method > $1>#> () @ Loc {file=test/testdata/resolver/class_instance_vars.rb start=15:1 end=18:4} argument @ Loc {file=test/testdata/resolver/class_instance_vars.rb start=??? end=???} class > < > () @ Loc {file=test/testdata/resolver/class_instance_vars.rb start=2:1 end=2:13} @@ -27,7 +26,7 @@ class >> < > () field ># -> Integer @ Loc {file=test/testdata/resolver/class_instance_vars.rb start=5:5 end=5:11} method ># () @ Loc {file=test/testdata/resolver/class_instance_vars.rb start=9:3 end=9:9} argument @ Loc {file=test/testdata/resolver/class_instance_vars.rb start=??? end=???} - method ># () @ Loc {file=test/testdata/resolver/class_instance_vars.rb start=3:3 end=3:17} + method ># : private () @ Loc {file=test/testdata/resolver/class_instance_vars.rb start=3:3 end=3:17} argument @ Loc {file=test/testdata/resolver/class_instance_vars.rb start=??? end=???} class > $1>[>>] < > $1> () @ Loc {file=test/testdata/resolver/class_instance_vars.rb start=2:1 end=2:13} type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Parent) @ Loc {file=test/testdata/resolver/class_instance_vars.rb start=2:1 end=2:13} diff --git a/test/testdata/resolver/constant_out_of_order_filler.rb b/test/testdata/resolver/constant_out_of_order_filler.rb new file mode 100644 index 0000000000..0852e767f5 --- /dev/null +++ b/test/testdata/resolver/constant_out_of_order_filler.rb @@ -0,0 +1,7 @@ +# check-out-of-order-constant-references: true +# typed: true + +X = A # No error. We do not report undeclared (implicit definition) symbols. + +class A::Foo +end diff --git a/test/testdata/resolver/field_nonexistent_type.rb.symbol-table.exp b/test/testdata/resolver/field_nonexistent_type.rb.symbol-table.exp index cc1682a62f..692a063e28 100644 --- a/test/testdata/resolver/field_nonexistent_type.rb.symbol-table.exp +++ b/test/testdata/resolver/field_nonexistent_type.rb.symbol-table.exp @@ -11,7 +11,7 @@ class :: < ::Object () field ::A#@baz -> A::Baz1 (unresolved) @ test/testdata/resolver/field_nonexistent_type.rb:25 field ::A#@biz -> T.untyped @ test/testdata/resolver/field_nonexistent_type.rb:29 field ::A#@foo -> A::Foo (unresolved) @ test/testdata/resolver/field_nonexistent_type.rb:20 - method ::A#initialize () -> Sorbet::Private::Static::Void @ test/testdata/resolver/field_nonexistent_type.rb:19 + method ::A#initialize : private () -> Sorbet::Private::Static::Void @ test/testdata/resolver/field_nonexistent_type.rb:19 argument -> T.untyped @ Loc {file=test/testdata/resolver/field_nonexistent_type.rb start=??? end=???} class ::[] < :: (Sig) @ test/testdata/resolver/field_nonexistent_type.rb:3 type-member(+) :::: -> T.attached_class (of A) @ test/testdata/resolver/field_nonexistent_type.rb:3 diff --git a/test/testdata/resolver/final_method.rb.symbol-table.exp b/test/testdata/resolver/final_method.rb.symbol-table.exp index 69c423e23b..eaa8883a02 100644 --- a/test/testdata/resolver/final_method.rb.symbol-table.exp +++ b/test/testdata/resolver/final_method.rb.symbol-table.exp @@ -12,27 +12,23 @@ class :: < ::Object () method ::#bar : final () -> Sorbet::Private::Static::Void @ test/testdata/resolver/final_method.rb:38 argument -> T.untyped @ Loc {file=test/testdata/resolver/final_method.rb start=??? end=???} module ::ExtendAgain < ::Sorbet::Private::Static::ImplicitModuleSuperclass () @ test/testdata/resolver/final_method.rb:91 - class ::[] < ::Module (M1) @ test/testdata/resolver/final_method.rb:91 - type-member(+) :::: -> T.attached_class (of ExtendAgain) @ test/testdata/resolver/final_method.rb:91 + class :: < ::Module (M1) @ test/testdata/resolver/final_method.rb:91 method ::# () @ test/testdata/resolver/final_method.rb:91 argument @ Loc {file=test/testdata/resolver/final_method.rb start=??? end=???} module ::IncludeAgain < ::Sorbet::Private::Static::ImplicitModuleSuperclass (M1) @ test/testdata/resolver/final_method.rb:86 - class ::[] < ::Module () @ test/testdata/resolver/final_method.rb:86 - type-member(+) :::: -> T.attached_class (of IncludeAgain) @ test/testdata/resolver/final_method.rb:86 + class :: < ::Module () @ test/testdata/resolver/final_method.rb:86 method ::# () @ test/testdata/resolver/final_method.rb:86 argument @ Loc {file=test/testdata/resolver/final_method.rb start=??? end=???} module ::M1 < ::Sorbet::Private::Static::ImplicitModuleSuperclass () @ test/testdata/resolver/final_method.rb:46 method ::M1#foo : final () -> Sorbet::Private::Static::Void @ test/testdata/resolver/final_method.rb:49 argument -> T.untyped @ Loc {file=test/testdata/resolver/final_method.rb start=??? end=???} - class ::[] < ::Module (Sig) @ test/testdata/resolver/final_method.rb:46 - type-member(+) :::: -> T.attached_class (of M1) @ test/testdata/resolver/final_method.rb:46 + class :: < ::Module (Sig) @ test/testdata/resolver/final_method.rb:46 method ::# () @ test/testdata/resolver/final_method.rb:46 argument @ Loc {file=test/testdata/resolver/final_method.rb start=??? end=???} module ::M2 < ::Sorbet::Private::Static::ImplicitModuleSuperclass () @ test/testdata/resolver/final_method.rb:52 method ::M2#foo : final () -> Sorbet::Private::Static::Void @ test/testdata/resolver/final_method.rb:55 argument -> T.untyped @ Loc {file=test/testdata/resolver/final_method.rb start=??? end=???} - class ::[] < ::Module (Sig) @ test/testdata/resolver/final_method.rb:52 - type-member(+) :::: -> T.attached_class (of M2) @ test/testdata/resolver/final_method.rb:52 + class :: < ::Module (Sig) @ test/testdata/resolver/final_method.rb:52 method ::# () @ test/testdata/resolver/final_method.rb:52 argument @ Loc {file=test/testdata/resolver/final_method.rb start=??? end=???} class ::OverrideDoubleExtend < ::Object () @ test/testdata/resolver/final_method.rb:73 @@ -71,8 +67,7 @@ class :: < ::Object () module ::OverrideManySteps < ::Sorbet::Private::Static::ImplicitModuleSuperclass (Step3, Step2, Step1, M1) @ test/testdata/resolver/final_method.rb:81 method ::OverrideManySteps#foo () @ test/testdata/resolver/final_method.rb:83 argument @ Loc {file=test/testdata/resolver/final_method.rb start=??? end=???} - class ::[] < ::Module () @ test/testdata/resolver/final_method.rb:81 - type-member(+) :::: -> T.attached_class (of OverrideManySteps) @ test/testdata/resolver/final_method.rb:81 + class :: < ::Module () @ test/testdata/resolver/final_method.rb:81 method ::# () @ test/testdata/resolver/final_method.rb:81 argument @ Loc {file=test/testdata/resolver/final_method.rb start=??? end=???} class ::Redefine < ::Object () @ test/testdata/resolver/final_method.rb:3 @@ -85,18 +80,15 @@ class :: < ::Object () method ::#bar : final () -> Sorbet::Private::Static::Void @ test/testdata/resolver/final_method.rb:30 argument -> T.untyped @ Loc {file=test/testdata/resolver/final_method.rb start=??? end=???} module ::Step1 < ::Sorbet::Private::Static::ImplicitModuleSuperclass (M1) @ test/testdata/resolver/final_method.rb:78 - class ::[] < ::Module () @ test/testdata/resolver/final_method.rb:78 - type-member(+) :::: -> T.attached_class (of Step1) @ test/testdata/resolver/final_method.rb:78 + class :: < ::Module () @ test/testdata/resolver/final_method.rb:78 method ::# () @ test/testdata/resolver/final_method.rb:78 argument @ Loc {file=test/testdata/resolver/final_method.rb start=??? end=???} module ::Step2 < ::Sorbet::Private::Static::ImplicitModuleSuperclass (Step1, M1) @ test/testdata/resolver/final_method.rb:79 - class ::[] < ::Module () @ test/testdata/resolver/final_method.rb:79 - type-member(+) :::: -> T.attached_class (of Step2) @ test/testdata/resolver/final_method.rb:79 + class :: < ::Module () @ test/testdata/resolver/final_method.rb:79 method ::# () @ test/testdata/resolver/final_method.rb:79 argument @ Loc {file=test/testdata/resolver/final_method.rb start=??? end=???} module ::Step3 < ::Sorbet::Private::Static::ImplicitModuleSuperclass (Step2, Step1, M1) @ test/testdata/resolver/final_method.rb:80 - class ::[] < ::Module () @ test/testdata/resolver/final_method.rb:80 - type-member(+) :::: -> T.attached_class (of Step3) @ test/testdata/resolver/final_method.rb:80 + class :: < ::Module () @ test/testdata/resolver/final_method.rb:80 method ::# () @ test/testdata/resolver/final_method.rb:80 argument @ Loc {file=test/testdata/resolver/final_method.rb start=??? end=???} diff --git a/test/testdata/resolver/fuzz_type_member_forget.rb b/test/testdata/resolver/fuzz_type_member_forget.rb index 800db96b76..be2c2f1a0f 100644 --- a/test/testdata/resolver/fuzz_type_member_forget.rb +++ b/test/testdata/resolver/fuzz_type_member_forget.rb @@ -9,4 +9,4 @@ class DifferentArityChild < Parent # error: Type `TParent` declared by parent `P TChild = type_member {{fixed: String}} end -T.cast(1, DifferentArityChild[Integer, String, Symbol]) # error-with-dupes: Wrong number of type parameters for `DifferentArityChild`. Expected: `0`, got: `3` +T.cast(1, DifferentArityChild[Integer, String, Symbol]) # error-with-dupes: All type parameters for `DifferentArityChild` have already been fixed diff --git a/test/testdata/resolver/generics_type_syntax_autocorrect.rb b/test/testdata/resolver/generics_type_syntax_autocorrect.rb new file mode 100644 index 0000000000..bd71791eda --- /dev/null +++ b/test/testdata/resolver/generics_type_syntax_autocorrect.rb @@ -0,0 +1,50 @@ +# typed: true +extend T::Sig + +class Example + extend T::Generic + + Elem = type_member +end + +class Opus::Another + extend T::Generic + + Elem = type_member +end + +module T + # There's a method called `Kernel#Hash` that Sorbet thinks is being called, + # which has one argument. When given two, Sorbet thinks it's smart enough to + # delete the second argument for you. Technically that conflicts with the + # autocorrect below. + # + # But in practice, you would either use IDE code actions to pick the one you + # want, or use the --isolate-error-code flag to apply from the command line, + # so let's define this to make it easier to test via autocorrect snapshots. + def self.Hash(x, y); end +end + + +sig { params(x: T::Array(String)).void } +# ^^^^^^^^^^^^^^^^ error: Did you mean to use square brackets: `T::Array[String]` +def test1(x) +end + +sig { params(x: Example(String)).void } +# ^^^^^^^^^^^^^^^ error: Did you mean to use square brackets: `Example[String]` +# ^^^^^^^ error: does not exist +def test2(x) +end + +sig { params(x: Opus::Another(String)).void } +# ^^^^^^^^^^^^^^^^^^^^^ error: Did you mean to use square brackets: `Opus::Another[String]` +# ^^^^^^^ error: does not exist +def test3(x) +end + +sig { params(x: T::Hash(Symbol, Integer)).void } +# ^^^^^^^^^^^^^^^^^^^^^^^^ error: Did you mean to use square brackets: `T::Hash[Symbol, Integer]` +def test4(x) +end + diff --git a/test/testdata/resolver/generics_type_syntax_autocorrect.rb.autocorrects.exp b/test/testdata/resolver/generics_type_syntax_autocorrect.rb.autocorrects.exp new file mode 100644 index 0000000000..4be04d1aa3 --- /dev/null +++ b/test/testdata/resolver/generics_type_syntax_autocorrect.rb.autocorrects.exp @@ -0,0 +1,52 @@ +# -- test/testdata/resolver/generics_type_syntax_autocorrect.rb -- +# typed: true +extend T::Sig + +class Example + extend T::Generic + + Elem = type_member +end + +class Opus::Another + extend T::Generic + + Elem = type_member +end + +module T + # There's a method called `Kernel#Hash` that Sorbet thinks is being called, + # which has one argument. When given two, Sorbet thinks it's smart enough to + # delete the second argument for you. Technically that conflicts with the + # autocorrect below. + # + # But in practice, you would either use IDE code actions to pick the one you + # want, or use the --isolate-error-code flag to apply from the command line, + # so let's define this to make it easier to test via autocorrect snapshots. + def self.Hash(x, y); end +end + + +sig { params(x: T::Array[String]).void } +# ^^^^^^^^^^^^^^^^ error: Did you mean to use square brackets: `T::Array[String]` +def test1(x) +end + +sig { params(x: Example[String]).void } +# ^^^^^^^^^^^^^^^ error: Did you mean to use square brackets: `Example[String]` +# ^^^^^^^ error: does not exist +def test2(x) +end + +sig { params(x: Opus::Another[String]).void } +# ^^^^^^^^^^^^^^^^^^^^^ error: Did you mean to use square brackets: `Opus::Another[String]` +# ^^^^^^^ error: does not exist +def test3(x) +end + +sig { params(x: T::Hash[Symbol, Integer]).void } +# ^^^^^^^^^^^^^^^^^^^^^^^^ error: Did you mean to use square brackets: `T::Hash[Symbol, Integer]` +def test4(x) +end + +# ------------------------------ diff --git a/test/testdata/resolver/inherit_alias.rb.symbol-table-raw.exp b/test/testdata/resolver/inherit_alias.rb.symbol-table-raw.exp index bd93f2f9bf..ed124dd3c3 100644 --- a/test/testdata/resolver/inherit_alias.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/inherit_alias.rb.symbol-table-raw.exp @@ -14,8 +14,7 @@ class >> < > () type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=NS::Dest) @ Loc {file=test/testdata/resolver/inherit_alias.rb start=6:3 end=6:13} method >::> $1>#> () @ Loc {file=test/testdata/resolver/inherit_alias.rb start=6:3 end=7:6} argument @ Loc {file=test/testdata/resolver/inherit_alias.rb start=??? end=???} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/inherit_alias.rb start=2:1 end=2:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=NS) @ Loc {file=test/testdata/resolver/inherit_alias.rb start=2:1 end=2:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/inherit_alias.rb start=2:1 end=2:10} method > $1>#> () @ Loc {file=test/testdata/resolver/inherit_alias.rb start=2:1 end=10:4} argument @ Loc {file=test/testdata/resolver/inherit_alias.rb start=??? end=???} diff --git a/test/testdata/resolver/interface_initialize.rb b/test/testdata/resolver/interface_initialize.rb new file mode 100644 index 0000000000..9e0fd42cd7 --- /dev/null +++ b/test/testdata/resolver/interface_initialize.rb @@ -0,0 +1,18 @@ +# typed: true +module MyInterface + extend T::Sig + extend T::Helpers + interface! + + sig {abstract.params(foo: T.nilable(String)).void} + def initialize(foo); end +end + +class A + extend T::Sig + include MyInterface + + sig {override.void} + def initialize # error: must accept at least `1` positional + end +end diff --git a/test/testdata/resolver/interface_private.rb b/test/testdata/resolver/interface_private.rb new file mode 100644 index 0000000000..e4a807bc41 --- /dev/null +++ b/test/testdata/resolver/interface_private.rb @@ -0,0 +1,54 @@ +# typed: strict +extend T::Sig + +module IFoo + extend T::Sig + extend T::Helpers + interface! + + sig {abstract.returns(String)} + private def foo_impl; end + + sig {abstract.returns(String)} + def foo; end +end + +module IFooDefault + extend T::Sig + extend T::Helpers + include IFoo + abstract! + + sig {override.returns(String)} + def foo; foo_impl; end # good +end + + +class FooWithDefault + extend T::Sig + include IFooDefault + + sig {override.returns(String)} + def foo_impl; 'FooWithDefault'; end +end + +class FooCustom + extend T::Sig + include IFoo + + sig {override.returns(String)} + def foo_impl; 'FooCustom'; end + + sig {override.returns(String)} + def foo + res = foo_impl # good + puts("Custom foo: #{res}") + res + end +end + +sig {params(x: IFoo).void} +def example(x) + x.foo # good + x.foo_impl # error: Non-private call to private method `foo_impl` on `IFoo` +end diff --git a/test/testdata/resolver/invalid_alias.rb.symbol-table-raw.exp b/test/testdata/resolver/invalid_alias.rb.symbol-table-raw.exp index da9beb1297..f07425c379 100644 --- a/test/testdata/resolver/invalid_alias.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/invalid_alias.rb.symbol-table-raw.exp @@ -9,7 +9,7 @@ class >> < > () argument @ Loc {file=test/testdata/resolver/invalid_alias.rb start=??? end=???} method > $1># () @ Loc {file=test/testdata/resolver/invalid_alias.rb start=5:5 end=5:14} argument @ Loc {file=test/testdata/resolver/invalid_alias.rb start=??? end=???} - class > $1> $1>[>>] < > $1> $1> () @ Loc {file=test/testdata/resolver/invalid_alias.rb start=4:3 end=4:8} + class > $1> $1>[>>] < > () @ Loc {file=test/testdata/resolver/invalid_alias.rb start=4:3 end=4:8} type-member(+) > $1> $1>::>> -> LambdaParam(> $1> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > $1> targs = [ >> = BadAliasingClassMethod1 ] }) @ Loc {file=test/testdata/resolver/invalid_alias.rb start=4:3 end=4:8} method > $1> $1>#> () @ Loc {file=test/testdata/resolver/invalid_alias.rb start=4:3 end=10:6} argument @ Loc {file=test/testdata/resolver/invalid_alias.rb start=??? end=???} diff --git a/test/testdata/resolver/let_errors.rb.symbol-table-raw.exp b/test/testdata/resolver/let_errors.rb.symbol-table-raw.exp index fcfe00ff64..89678f69c5 100644 --- a/test/testdata/resolver/let_errors.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/let_errors.rb.symbol-table-raw.exp @@ -9,7 +9,7 @@ class >> < > () static-field >:: -> Integer @ Loc {file=test/testdata/resolver/let_errors.rb start=3:3 end=3:11} field ># -> Integer @ Loc {file=test/testdata/resolver/let_errors.rb start=11:5 end=11:7} field ># -> Integer @ Loc {file=test/testdata/resolver/let_errors.rb start=7:5 end=7:10} - method ># () @ Loc {file=test/testdata/resolver/let_errors.rb start=5:3 end=5:17} + method ># : private () @ Loc {file=test/testdata/resolver/let_errors.rb start=5:3 end=5:17} argument @ Loc {file=test/testdata/resolver/let_errors.rb start=??? end=???} method ># () @ Loc {file=test/testdata/resolver/let_errors.rb start=10:3 end=10:21} argument @ Loc {file=test/testdata/resolver/let_errors.rb start=??? end=???} diff --git a/test/testdata/resolver/let_var.rb.symbol-table-raw.exp b/test/testdata/resolver/let_var.rb.symbol-table-raw.exp index a820be17d1..2c80e109e2 100644 --- a/test/testdata/resolver/let_var.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/let_var.rb.symbol-table-raw.exp @@ -17,8 +17,7 @@ class >> < > () argument @ Loc {file=test/testdata/resolver/let_var.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/let_var.rb start=18:1 end=18:13} static-field >:: -> Integer @ Loc {file=test/testdata/resolver/let_var.rb start=19:3 end=19:15} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/let_var.rb start=18:1 end=18:13} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Mixin) @ Loc {file=test/testdata/resolver/let_var.rb start=18:1 end=18:13} + class > $1> < > () @ Loc {file=test/testdata/resolver/let_var.rb start=18:1 end=18:13} method > $1>#> () @ Loc {file=test/testdata/resolver/let_var.rb start=18:1 end=21:4} argument @ Loc {file=test/testdata/resolver/let_var.rb start=??? end=???} class > < > () @ Loc {file=test/testdata/resolver/let_var.rb start=2:1 end=2:13} @@ -27,7 +26,7 @@ class >> < > () field ># -> Integer @ Loc {file=test/testdata/resolver/let_var.rb start=5:5 end=5:11} method ># () @ Loc {file=test/testdata/resolver/let_var.rb start=12:3 end=12:9} argument @ Loc {file=test/testdata/resolver/let_var.rb start=??? end=???} - method ># () @ Loc {file=test/testdata/resolver/let_var.rb start=3:3 end=3:17} + method ># : private () @ Loc {file=test/testdata/resolver/let_var.rb start=3:3 end=3:17} argument @ Loc {file=test/testdata/resolver/let_var.rb start=??? end=???} class > $1>[>>] < > $1> () @ Loc {file=test/testdata/resolver/let_var.rb start=2:1 end=2:13} type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Parent) @ Loc {file=test/testdata/resolver/let_var.rb start=2:1 end=2:13} diff --git a/test/testdata/resolver/linearization/includes_class.rb.symbol-table-raw.exp b/test/testdata/resolver/linearization/includes_class.rb.symbol-table-raw.exp index 24ed1e4833..4a0941a212 100644 --- a/test/testdata/resolver/linearization/includes_class.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/linearization/includes_class.rb.symbol-table-raw.exp @@ -15,13 +15,11 @@ class >> < > () module > < >::>::>::> (>, >, >, >, >) @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=13:1 end=13:9} method ># () @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=16:3 end=16:10} argument @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=??? end=???} - class > $1>[>>] < > (>, >, >, >, >) @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=13:1 end=13:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=C) @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=13:1 end=13:9} + class > $1> < > (>, >, >, >, >) @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=13:1 end=13:9} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=13:1 end=19:4} argument @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=??? end=???} module > < >::>::>::> (>) @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=21:1 end=21:18} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=21:1 end=21:18} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=IncludesBO) @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=21:1 end=21:18} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=21:1 end=21:18} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=21:1 end=23:4} argument @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=??? end=???} class > < > () @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=3:1 end=3:10} diff --git a/test/testdata/resolver/linearization/linearization1.rb.symbol-table-raw.exp b/test/testdata/resolver/linearization/linearization1.rb.symbol-table-raw.exp index e049b66263..91b1ce1b43 100644 --- a/test/testdata/resolver/linearization/linearization1.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/linearization/linearization1.rb.symbol-table-raw.exp @@ -15,20 +15,17 @@ class >> < > () module > < >::>::>::> () @ Loc {file=test/testdata/resolver/linearization/linearization1.rb start=2:1 end=2:10} method ># () @ Loc {file=test/testdata/resolver/linearization/linearization1.rb start=3:3 end=3:10} argument @ Loc {file=test/testdata/resolver/linearization/linearization1.rb start=??? end=???} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization1.rb start=2:1 end=2:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=M1) @ Loc {file=test/testdata/resolver/linearization/linearization1.rb start=2:1 end=2:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization1.rb start=2:1 end=2:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization1.rb start=2:1 end=4:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization1.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/linearization/linearization1.rb start=6:1 end=6:10} method ># () @ Loc {file=test/testdata/resolver/linearization/linearization1.rb start=7:3 end=7:10} argument @ Loc {file=test/testdata/resolver/linearization/linearization1.rb start=??? end=???} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization1.rb start=6:1 end=6:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=M2) @ Loc {file=test/testdata/resolver/linearization/linearization1.rb start=6:1 end=6:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization1.rb start=6:1 end=6:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization1.rb start=6:1 end=8:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization1.rb start=??? end=???} module > < >::>::>::> (>, >) @ Loc {file=test/testdata/resolver/linearization/linearization1.rb start=10:1 end=10:10} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization1.rb start=10:1 end=10:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=M3) @ Loc {file=test/testdata/resolver/linearization/linearization1.rb start=10:1 end=10:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization1.rb start=10:1 end=10:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization1.rb start=10:1 end=13:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization1.rb start=??? end=???} diff --git a/test/testdata/resolver/linearization/linearization2.rb.symbol-table-raw.exp b/test/testdata/resolver/linearization/linearization2.rb.symbol-table-raw.exp index f3922f878a..2784ffd78d 100644 --- a/test/testdata/resolver/linearization/linearization2.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/linearization/linearization2.rb.symbol-table-raw.exp @@ -6,8 +6,7 @@ class >> < > () method ># (x, ) @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=3:3 end=3:11} argument x<> @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=3:9 end=3:10} argument @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=??? end=???} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=2:1 end=2:12} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Base) @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=2:1 end=2:12} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=2:1 end=2:12} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=2:1 end=4:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=??? end=???} class > < > (>, >, >) @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=13:1 end=13:8} @@ -20,13 +19,11 @@ class >> < > () argument x<> @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=7:9 end=7:10} argument y<> @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=7:12 end=7:13} argument @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=??? end=???} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=5:1 end=5:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=M1) @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=5:1 end=5:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=5:1 end=5:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=5:1 end=8:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=??? end=???} module > < >::>::>::> (>) @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=9:1 end=9:10} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=9:1 end=9:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=M2) @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=9:1 end=9:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=9:1 end=9:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=9:1 end=11:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization2.rb start=??? end=???} diff --git a/test/testdata/resolver/linearization/linearization3.rb.symbol-table-raw.exp b/test/testdata/resolver/linearization/linearization3.rb.symbol-table-raw.exp index 6852e28917..6d0162bfa0 100644 --- a/test/testdata/resolver/linearization/linearization3.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/linearization/linearization3.rb.symbol-table-raw.exp @@ -3,23 +3,19 @@ class >> < > () method >> $1>#> $CENSORED> () @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=2:1 end=12:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=2:1 end=2:10} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=2:1 end=2:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A3) @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=2:1 end=2:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=2:1 end=2:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=2:1 end=2:15} argument @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=??? end=???} module > < >::>::>::> (>) @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=3:1 end=3:10} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=3:1 end=3:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=C3) @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=3:1 end=3:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=3:1 end=3:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=3:1 end=5:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=??? end=???} module > < >::>::>::> (>) @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=6:1 end=6:10} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=6:1 end=6:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=D3) @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=6:1 end=6:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=6:1 end=6:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=6:1 end=8:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=??? end=???} module > < >::>::>::> (>, >, >) @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=9:1 end=9:10} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=9:1 end=9:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=E3) @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=9:1 end=9:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=9:1 end=9:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=9:1 end=12:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization3.rb start=??? end=???} diff --git a/test/testdata/resolver/linearization/linearization4.rb.symbol-table-raw.exp b/test/testdata/resolver/linearization/linearization4.rb.symbol-table-raw.exp index 083908e64d..356d1390d8 100644 --- a/test/testdata/resolver/linearization/linearization4.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/linearization/linearization4.rb.symbol-table-raw.exp @@ -3,27 +3,23 @@ class >> < > () method >> $1>#> $CENSORED> () @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=2:1 end=19:11} argument @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=2:1 end=2:10} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=2:1 end=2:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A4) @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=2:1 end=2:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=2:1 end=2:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=2:1 end=3:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=4:1 end=4:10} method ># () @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=5:3 end=5:10} argument @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=??? end=???} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=4:1 end=4:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=B4) @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=4:1 end=4:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=4:1 end=4:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=4:1 end=6:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=??? end=???} module > < >::>::>::> (>) @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=7:1 end=7:10} method ># () @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=8:3 end=8:10} argument @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=??? end=???} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=7:1 end=7:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=C4) @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=7:1 end=7:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=7:1 end=7:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=7:1 end=10:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=??? end=???} module > < >::>::>::> (>, >) @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=11:1 end=11:10} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=11:1 end=11:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=D4) @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=11:1 end=11:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=11:1 end=11:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=11:1 end=14:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=??? end=???} class > < > (>, >, >, >) @ Loc {file=test/testdata/resolver/linearization/linearization4.rb start=15:1 end=15:9} diff --git a/test/testdata/resolver/linearization/linearization4a.rb.symbol-table-raw.exp b/test/testdata/resolver/linearization/linearization4a.rb.symbol-table-raw.exp index 6d3949fc88..8344ab21fc 100644 --- a/test/testdata/resolver/linearization/linearization4a.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/linearization/linearization4a.rb.symbol-table-raw.exp @@ -3,27 +3,23 @@ class >> < > () method >> $1>#> $CENSORED> () @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=2:1 end=19:11} argument @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=2:1 end=2:10} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=2:1 end=2:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A4) @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=2:1 end=2:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=2:1 end=2:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=2:1 end=3:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=4:1 end=4:10} method ># () @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=5:3 end=5:10} argument @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=??? end=???} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=4:1 end=4:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=B4) @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=4:1 end=4:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=4:1 end=4:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=4:1 end=6:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=7:1 end=7:10} method ># () @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=8:3 end=8:10} argument @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=??? end=???} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=7:1 end=7:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=C4) @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=7:1 end=7:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=7:1 end=7:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=7:1 end=10:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=??? end=???} module > < >::>::>::> (>, >) @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=11:1 end=11:10} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=11:1 end=11:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=D4) @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=11:1 end=11:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=11:1 end=11:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=11:1 end=14:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=??? end=???} class > < > (>, >, >, >) @ Loc {file=test/testdata/resolver/linearization/linearization4a.rb start=15:1 end=15:9} diff --git a/test/testdata/resolver/linearization/linearization5.rb.symbol-table-raw.exp b/test/testdata/resolver/linearization/linearization5.rb.symbol-table-raw.exp index 75fd00c6bb..586ba9fb4a 100644 --- a/test/testdata/resolver/linearization/linearization5.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/linearization/linearization5.rb.symbol-table-raw.exp @@ -3,15 +3,13 @@ class >> < > () method >> $1>#> $CENSORED> () @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=2:1 end=18:11} argument @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=2:1 end=2:10} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=2:1 end=2:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A5) @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=2:1 end=2:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=2:1 end=2:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=2:1 end=3:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=4:1 end=4:10} method ># () @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=5:3 end=5:10} argument @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=??? end=???} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=4:1 end=4:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=B5) @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=4:1 end=4:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=4:1 end=4:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=4:1 end=6:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=??? end=???} class > < > (>) @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=7:1 end=7:9} @@ -22,8 +20,7 @@ class >> < > () method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=7:1 end=10:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=??? end=???} module > < >::>::>::> (>, >) @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=11:1 end=11:10} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=11:1 end=11:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=D5) @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=11:1 end=11:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=11:1 end=11:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=11:1 end=14:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=??? end=???} class > < > (>, >) @ Loc {file=test/testdata/resolver/linearization/linearization5.rb start=15:1 end=15:14} diff --git a/test/testdata/resolver/linearization/linearization6.rb.symbol-table-raw.exp b/test/testdata/resolver/linearization/linearization6.rb.symbol-table-raw.exp index 9569891667..ca43b758c4 100644 --- a/test/testdata/resolver/linearization/linearization6.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/linearization/linearization6.rb.symbol-table-raw.exp @@ -3,28 +3,23 @@ class >> < > () method >> $1>#> $CENSORED> () @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=2:1 end=15:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=2:1 end=2:10} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=2:1 end=2:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A6) @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=2:1 end=2:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=2:1 end=2:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=2:1 end=2:15} argument @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=3:1 end=3:10} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=3:1 end=3:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=B6) @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=3:1 end=3:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=3:1 end=3:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=3:1 end=3:15} argument @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=??? end=???} module > < >::>::>::> (>, >) @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=4:1 end=4:10} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=4:1 end=4:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=C6) @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=4:1 end=4:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=4:1 end=4:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=4:1 end=7:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=??? end=???} module > < >::>::>::> (>, >) @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=8:1 end=8:10} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=8:1 end=8:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=D6) @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=8:1 end=8:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=8:1 end=8:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=8:1 end=11:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=??? end=???} module > < >::>::>::> (>, >, >, >) @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=12:1 end=12:10} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=12:1 end=12:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=E6) @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=12:1 end=12:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=12:1 end=12:10} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=12:1 end=15:4} argument @ Loc {file=test/testdata/resolver/linearization/linearization6.rb start=??? end=???} diff --git a/test/testdata/resolver/missing_alias_target.rb b/test/testdata/resolver/missing_alias_target.rb new file mode 100644 index 0000000000..3024efd5c5 --- /dev/null +++ b/test/testdata/resolver/missing_alias_target.rb @@ -0,0 +1,5 @@ +# typed: strict +A = 1 + +K = B +# ^ error: Unable to resolve constant `B` diff --git a/test/testdata/resolver/missing_alias_target.rb.symbol-table.exp b/test/testdata/resolver/missing_alias_target.rb.symbol-table.exp new file mode 100644 index 0000000000..79aa2020a2 --- /dev/null +++ b/test/testdata/resolver/missing_alias_target.rb.symbol-table.exp @@ -0,0 +1,7 @@ +class :: < ::Object () + class ::>[] < :: () + method ::># () @ test/testdata/resolver/missing_alias_target.rb:2 + argument @ Loc {file=test/testdata/resolver/missing_alias_target.rb start=??? end=???} + static-field ::A -> Integer @ test/testdata/resolver/missing_alias_target.rb:2 + static-field ::K -> @ test/testdata/resolver/missing_alias_target.rb:4 + diff --git a/test/testdata/resolver/mixes_in_class_methods.rb.symbol-table-raw.exp b/test/testdata/resolver/mixes_in_class_methods.rb.symbol-table-raw.exp index b22914a8ce..c5708d084d 100644 --- a/test/testdata/resolver/mixes_in_class_methods.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/mixes_in_class_methods.rb.symbol-table-raw.exp @@ -3,14 +3,12 @@ class >> < > () method >> $1>#> $CENSORED> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=2:1 end=67:4} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=25:1 end=25:12} - class > $1>[>>] < > (>, >) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=25:1 end=25:12} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Bad1) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=25:1 end=25:12} + class > $1> < > (>, >) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=25:1 end=25:12} method > $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=25:1 end=29:4} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=??? end=???} class > < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=31:1 end=31:11} module >::> < >::>::>::> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=34:3 end=34:22} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=34:3 end=34:22} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Bad2::ClassMethods) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=34:3 end=34:22} + class >::> $1> < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=34:3 end=34:22} method >::> $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=34:3 end=34:27} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=??? end=???} class > $1>[>>] < > $1> (>, >) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=31:1 end=31:11} @@ -23,38 +21,32 @@ class >> < > () type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Bad3::ClassMethods) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=42:3 end=42:21} method >::> $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=42:3 end=42:26} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=??? end=???} - class > $1>[>>] < > (>, >) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=38:1 end=38:12} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Bad3) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=38:1 end=38:12} + class > $1> < > (>, >) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=38:1 end=38:12} method > $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=38:1 end=44:4} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=46:1 end=46:12} - class > $1>[>>] < > (>, >) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=46:1 end=46:12} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Bad4) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=46:1 end=46:12} + class > $1> < > (>, >) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=46:1 end=46:12} method > $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=46:1 end=52:4} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=54:1 end=54:12} static-field >::> -> Integer @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=58:3 end=58:16} - class > $1>[>>] < > (>, >) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=54:1 end=54:12} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Bad5) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=54:1 end=54:12} + class > $1> < > (>, >) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=54:1 end=54:12} method > $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=54:1 end=61:4} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=63:1 end=63:12} - class > $1>[>>] < > (>, >) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=63:1 end=63:12} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Bad6) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=63:1 end=63:12} + class > $1> < > (>, >) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=63:1 end=63:12} method > $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=63:1 end=67:4} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=2:1 end=2:13} module >::> < >::>::>::> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=6:3 end=6:22} method >::># () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=7:5 end=7:27} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=??? end=???} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=6:3 end=6:22} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Mixin::ClassMethods) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=6:3 end=6:22} + class >::> $1> < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=6:3 end=6:22} method >::> $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=6:3 end=9:6} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=??? end=???} method ># () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=13:3 end=13:19} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=??? end=???} - class > $1>[>>] < > (>, >) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=2:1 end=2:13} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Mixin) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=2:1 end=2:13} + class > $1> < > (>, >) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=2:1 end=2:13} method > $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=2:1 end=15:4} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=??? end=???} class > < > (>) @ Loc {file=test/testdata/resolver/mixes_in_class_methods.rb start=17:1 end=17:11} diff --git a/test/testdata/resolver/mixes_in_class_methods_multiple.rb.symbol-table-raw.exp b/test/testdata/resolver/mixes_in_class_methods_multiple.rb.symbol-table-raw.exp index 09b65b0d54..450aa13b42 100644 --- a/test/testdata/resolver/mixes_in_class_methods_multiple.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/mixes_in_class_methods_multiple.rb.symbol-table-raw.exp @@ -4,8 +4,7 @@ class >> < > () argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=71:1 end=71:12} module >::> < >::>::>::> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=75:3 end=75:22} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=75:3 end=75:22} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Bad1::ClassMethods) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=75:3 end=75:22} + class >::> $1> < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=75:3 end=75:22} method >::> $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=75:3 end=75:27} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} class >::> < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=76:3 end=76:22} @@ -13,72 +12,61 @@ class >> < > () type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Bad1::ClassMethods2) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=76:3 end=76:22} method >::> $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=76:3 end=76:27} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} - class > $1>[>>] < > (>, >) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=71:1 end=71:12} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Bad1) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=71:1 end=71:12} + class > $1> < > (>, >) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=71:1 end=71:12} method > $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=71:1 end=78:4} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=80:1 end=80:12} module >::> < >::>::>::> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=84:3 end=84:22} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=84:3 end=84:22} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Bad2::ClassMethods) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=84:3 end=84:22} + class >::> $1> < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=84:3 end=84:22} method >::> $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=84:3 end=84:27} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} - class > $1>[>>] < > (>, >) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=80:1 end=80:12} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Bad2) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=80:1 end=80:12} + class > $1> < > (>, >) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=80:1 end=80:12} method > $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=80:1 end=86:4} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=88:1 end=88:12} module >::> < >::>::>::> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=92:3 end=92:22} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=92:3 end=92:22} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Bad3::ClassMethods) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=92:3 end=92:22} + class >::> $1> < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=92:3 end=92:22} method >::> $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=92:3 end=92:27} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} - class > $1>[>>] < > (>, >) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=88:1 end=88:12} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Bad3) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=88:1 end=88:12} + class > $1> < > (>, >) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=88:1 end=88:12} method > $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=88:1 end=95:4} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=2:1 end=2:13} module >::> < >::>::>::> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=5:3 end=5:23} method >::># () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=6:5 end=6:29} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=5:3 end=5:23} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Mixin::ClassMethods1) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=5:3 end=5:23} + class >::> $1> < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=5:3 end=5:23} method >::> $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=5:3 end=8:6} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} module >::> < >::>::>::> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=10:3 end=10:23} method >::># () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=11:5 end=11:29} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=10:3 end=10:23} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Mixin::ClassMethods2) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=10:3 end=10:23} + class >::> $1> < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=10:3 end=10:23} method >::> $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=10:3 end=13:6} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} module >::> < >::>::>::> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=15:3 end=15:23} method >::># () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=16:5 end=16:29} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=15:3 end=15:23} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Mixin::ClassMethods3) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=15:3 end=15:23} + class >::> $1> < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=15:3 end=15:23} method >::> $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=15:3 end=18:6} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} method ># () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=22:3 end=22:19} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} - class > $1>[>>] < > (>) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=2:1 end=2:13} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Mixin) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=2:1 end=2:13} + class > $1> < > (>) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=2:1 end=2:13} method > $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=2:1 end=24:4} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=37:1 end=37:14} module >::> < >::>::>::> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=40:3 end=40:23} method >::># () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=41:5 end=41:12} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=40:3 end=40:23} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Mixin2::ClassMethods1) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=40:3 end=40:23} + class >::> $1> < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=40:3 end=40:23} method >::> $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=40:3 end=43:6} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} module >::> < >::>::>::> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=45:3 end=45:23} method >::># (a, ) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=46:5 end=46:15} argument a<> @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=46:13 end=46:14} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=45:3 end=45:23} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Mixin2::ClassMethods2) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=45:3 end=45:23} + class >::> $1> < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=45:3 end=45:23} method >::> $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=45:3 end=48:6} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} module >::> < >::>::>::> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=52:3 end=52:23} @@ -87,8 +75,7 @@ class >> < > () argument b<> @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=53:16 end=53:17} argument c<> @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=53:19 end=53:20} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=52:3 end=52:23} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Mixin2::ClassMethods3) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=52:3 end=52:23} + class >::> $1> < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=52:3 end=52:23} method >::> $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=52:3 end=55:6} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} module >::> < >::>::>::> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=57:3 end=57:23} @@ -98,12 +85,10 @@ class >> < > () argument c<> @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=58:19 end=58:20} argument d<> @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=58:22 end=58:23} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=57:3 end=57:23} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Mixin2::ClassMethods4) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=57:3 end=57:23} + class >::> $1> < > () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=57:3 end=57:23} method >::> $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=57:3 end=60:6} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} - class > $1>[>>] < > (>) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=37:1 end=37:14} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Mixin2) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=37:1 end=37:14} + class > $1> < > (>) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=37:1 end=37:14} method > $1>#> () @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=37:1 end=63:4} argument @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=??? end=???} class > < > (>) @ Loc {file=test/testdata/resolver/mixes_in_class_methods_multiple.rb start=26:1 end=26:11} diff --git a/test/testdata/resolver/module_superclass.rb b/test/testdata/resolver/module_superclass.rb index ed42237267..370d10046c 100644 --- a/test/testdata/resolver/module_superclass.rb +++ b/test/testdata/resolver/module_superclass.rb @@ -1,4 +1,5 @@ # typed: true +# disable-fast-path: true # It's okay to subclass BasicObject class SubclassBasicObject < BasicObject; end @@ -9,8 +10,8 @@ class SubclassObject < Object; end # It's okay to subclass Module (the class) class SubclassModule2 < Module; end -# It's okay to subclass Class -class SubclassClass < Class; end +# It's not okay to subclass Class +class SubclassClass < Class; end # error: `SubclassClass` is a subclass of `Class` which is not allowed module M; end diff --git a/test/testdata/resolver/optional_keyword_param_override.rb b/test/testdata/resolver/optional_keyword_param_override.rb new file mode 100644 index 0000000000..7146e62624 --- /dev/null +++ b/test/testdata/resolver/optional_keyword_param_override.rb @@ -0,0 +1,48 @@ +# typed: strict +extend T::Sig + +class Left + extend T::Sig + + sig { overridable.params(x: T::Boolean).returns(T.nilable(String)) } + def foo(x: false) + end +end + +class Right1 < Left + sig { override.returns(T.nilable(String)) } + def foo # error: Implementation of overridable method `Left#foo` must accept optional keyword parameter `x` + end +end +class Right2 < Left + sig { override.params(x: T::Boolean).returns(T.nilable(String)) } + def foo(x: false) + end +end +class Right3 < Left + sig { override.params(x: T::Boolean).returns(T.nilable(String)) } + def foo(x:) # error: Implementation of overridable method `Left#foo` must redeclare keyword parameter `x` as optional + end +end + +class Parent + extend T::Sig + extend T::Helpers + abstract! + + sig {abstract.params(no_check: T::Boolean).void} + def example(no_check: false) + end +end + +class Child1 < Parent + sig {override.params(opts: T::Hash[T.untyped, T.untyped]).void} + def example(opts={}) # error: Implementation of abstract method `Parent#example` must accept optional keyword parameter `no_check` + end +end +class Child2 < Parent + # The runtime doesn't allow this + sig {override.params(opts: T.untyped).void} + def example(**opts) + end +end diff --git a/test/testdata/resolver/positional_bind.rb b/test/testdata/resolver/positional_bind.rb new file mode 100644 index 0000000000..9feff6d4e6 --- /dev/null +++ b/test/testdata/resolver/positional_bind.rb @@ -0,0 +1,12 @@ +# typed: true + +class A + extend T::Sig + + # it doesn't make sense to use `.bind` for + # anything other than a `&blk` argument + sig {params(f: T.proc.bind(A).void).void} + # ^ error: Using `bind` is not permitted here + def self.takes_fn(f) + end +end diff --git a/test/testdata/resolver/redeclare_type_member_template.rb b/test/testdata/resolver/redeclare_type_member_template.rb new file mode 100644 index 0000000000..f64711d983 --- /dev/null +++ b/test/testdata/resolver/redeclare_type_member_template.rb @@ -0,0 +1,30 @@ +# typed: true +# disable-fast-path: true +extend T::Sig + +class Parent + extend T::Generic + extend T::Sig + + X = type_member + sig {returns(X)} + def foo; raise "unimplemented"; end +end + +class Child1 < Parent + extend T::Generic + + X = type_template { {fixed: Integer} } # error: `X` must be declared as a type_member (not a type_template) to match the parent + + def main + x = foo + end +end + +class Child2 < Parent # error: Type `X` declared by parent `Parent` must be re-declared in `Child2` + extend T::Generic + + def main + x = foo + end +end diff --git a/test/testdata/resolver/redefinition_of_subclass_type_member.rb b/test/testdata/resolver/redefinition_of_subclass_type_member.rb index a3fc31cec8..499e517376 100644 --- a/test/testdata/resolver/redefinition_of_subclass_type_member.rb +++ b/test/testdata/resolver/redefinition_of_subclass_type_member.rb @@ -26,6 +26,6 @@ def foo(k, v) class Bar < Foo # error: Type `V` declared by parent `Foo` must be re-declared in `Bar` K = Bar[String,String].new.foo('a', 2) -# ^ error: Type variable `K` needs to be declared as `= type_member(SOMETHING)` - # ^^^^^^^^^^^^^ error: Wrong number of type parameters +# ^ error: Type variable `K` needs to be declared as a type_member or type_template, not a static-field + # ^^^^^^^^^^^^^ error: All type parameters for `Bar` have already been fixed end diff --git a/test/testdata/resolver/requires_ancestor_calls_types.rb b/test/testdata/resolver/requires_ancestor_calls_types.rb index 551e737f3f..0f1de1182a 100644 --- a/test/testdata/resolver/requires_ancestor_calls_types.rb +++ b/test/testdata/resolver/requires_ancestor_calls_types.rb @@ -226,7 +226,7 @@ def self.m2 def m2 T.attached_class.foo - # ^^^^^^^^^^^^^^^^ error: `T.attached_class` may only be used in a singleton class method context + # ^^^^^^^^^^^^^^^^ error: `Test10::M2` must declare `has_attached_class!` before module instance methods can use `T.attached_class T.class_of(M2).foo # ^^^ error: Call to method `foo` on `T.class_of(Test10::M2)` end diff --git a/test/testdata/resolver/resolution_order.rb b/test/testdata/resolver/resolution_order.rb index 54712758c6..232b3e2289 100644 --- a/test/testdata/resolver/resolution_order.rb +++ b/test/testdata/resolver/resolution_order.rb @@ -1,4 +1,4 @@ -# typed: strong +# typed: strict # This file is not valid Ruby since we reference A before # definition. However, it would work with our autoloader, and also diff --git a/test/testdata/resolver/resolution_order.rb.symbol-table-raw.exp b/test/testdata/resolver/resolution_order.rb.symbol-table-raw.exp index 363bc96983..d6f78ee1d4 100644 --- a/test/testdata/resolver/resolution_order.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/resolution_order.rb.symbol-table-raw.exp @@ -5,8 +5,7 @@ class >> < > () module > < >::>::>::> () @ Loc {file=test/testdata/resolver/resolution_order.rb start=34:1 end=34:9} static-field >::> -> AliasType { symbol = > } @ Loc {file=test/testdata/resolver/resolution_order.rb start=35:3 end=35:5} static-field >::> -> Integer @ Loc {file=test/testdata/resolver/resolution_order.rb start=37:3 end=37:5} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/resolution_order.rb start=34:1 end=34:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/resolver/resolution_order.rb start=34:1 end=34:9} + class > $1> < > () @ Loc {file=test/testdata/resolver/resolution_order.rb start=34:1 end=34:9} method > $1>#> () @ Loc {file=test/testdata/resolver/resolution_order.rb start=34:1 end=38:4} argument @ Loc {file=test/testdata/resolver/resolution_order.rb start=??? end=???} class > < > () @ Loc {file=test/testdata/resolver/resolution_order.rb start=40:1 end=40:8} @@ -22,20 +21,17 @@ class >> < > () argument @ Loc {file=test/testdata/resolver/resolution_order.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/resolution_order.rb start=62:1 end=62:9} static-field >::> -> AliasType { symbol = > } @ Loc {file=test/testdata/resolver/resolution_order.rb start=63:3 end=63:5} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/resolution_order.rb start=62:1 end=62:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=D) @ Loc {file=test/testdata/resolver/resolution_order.rb start=62:1 end=62:9} + class > $1> < > () @ Loc {file=test/testdata/resolver/resolution_order.rb start=62:1 end=62:9} method > $1>#> () @ Loc {file=test/testdata/resolver/resolution_order.rb start=62:1 end=64:4} argument @ Loc {file=test/testdata/resolver/resolution_order.rb start=??? end=???} module > < >::>::>::> (>) @ Loc {file=test/testdata/resolver/resolution_order.rb start=56:1 end=56:9} static-field >::> -> AliasType { symbol = >::> } @ Loc {file=test/testdata/resolver/resolution_order.rb start=58:3 end=58:5} static-field >::> -> AliasType { symbol = >::> } @ Loc {file=test/testdata/resolver/resolution_order.rb start=59:3 end=59:5} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/resolution_order.rb start=56:1 end=56:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=E) @ Loc {file=test/testdata/resolver/resolution_order.rb start=56:1 end=56:9} + class > $1> < > () @ Loc {file=test/testdata/resolver/resolution_order.rb start=56:1 end=56:9} method > $1>#> () @ Loc {file=test/testdata/resolver/resolution_order.rb start=56:1 end=60:4} argument @ Loc {file=test/testdata/resolver/resolution_order.rb start=??? end=???} module > < >::>::>::> (>, >) @ Loc {file=test/testdata/resolver/resolution_order.rb start=47:1 end=47:9} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/resolution_order.rb start=47:1 end=47:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=F) @ Loc {file=test/testdata/resolver/resolution_order.rb start=47:1 end=47:9} + class > $1> < > () @ Loc {file=test/testdata/resolver/resolution_order.rb start=47:1 end=47:9} method > $1>#> () @ Loc {file=test/testdata/resolver/resolution_order.rb start=47:1 end=54:4} argument @ Loc {file=test/testdata/resolver/resolution_order.rb start=??? end=???} class > < > (>) @ Loc {file=test/testdata/resolver/resolution_order.rb start=10:1 end=10:15} diff --git a/test/testdata/resolver/resolution_scoping.rb.symbol-table-raw.exp b/test/testdata/resolver/resolution_scoping.rb.symbol-table-raw.exp index 97378e1570..fbd8d98718 100644 --- a/test/testdata/resolver/resolution_scoping.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/resolution_scoping.rb.symbol-table-raw.exp @@ -4,13 +4,11 @@ class >> < > () argument @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=??? end=???} class > < >::>::>::> (>, >) @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=2:1 end=2:12} module >::> < >::>::>::> () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=5:3 end=5:11} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=5:3 end=5:11} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=A::B) @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=5:3 end=5:11} + class >::> $1> < > () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=5:3 end=5:11} method >::> $1>#> () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=5:3 end=6:6} argument @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=??? end=???} module >::> < >::>::>::> () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=3:3 end=3:11} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=3:3 end=3:11} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=A::C) @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=3:3 end=3:11} + class >::> $1> < > () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=3:3 end=3:11} method >::> $1>#> () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=3:3 end=4:6} argument @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=??? end=???} class > $1>[>>] < >::>::>::> $1> () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=2:1 end=2:12} @@ -19,12 +17,10 @@ class >> < > () argument @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=??? end=???} module > < >::>::>::> (>) @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=11:1 end=11:9} module >::> < >::>::>::> () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=13:3 end=13:11} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=13:3 end=13:11} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=D::E) @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=13:3 end=13:11} + class >::> $1> < > () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=13:3 end=13:11} method >::> $1>#> () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=13:3 end=14:6} argument @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=??? end=???} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=11:1 end=11:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=D) @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=11:1 end=11:9} + class > $1> < > () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=11:1 end=11:9} method > $1>#> () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=11:1 end=15:4} argument @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=??? end=???} diff --git a/test/testdata/resolver/resolve_through_alias.rb.symbol-table-raw.exp b/test/testdata/resolver/resolve_through_alias.rb.symbol-table-raw.exp index 91046bb8ec..67b0bd6368 100644 --- a/test/testdata/resolver/resolve_through_alias.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/resolve_through_alias.rb.symbol-table-raw.exp @@ -10,12 +10,10 @@ class >> < > () argument @ Loc {file=test/testdata/resolver/resolve_through_alias.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/resolve_through_alias.rb start=3:1 end=3:10} module >::> < >::>::>::> () @ Loc {file=test/testdata/resolver/resolve_through_alias.rb start=4:3 end=4:15} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/resolver/resolve_through_alias.rb start=4:3 end=4:15} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=NS::Inner) @ Loc {file=test/testdata/resolver/resolve_through_alias.rb start=4:3 end=4:15} + class >::> $1> < > () @ Loc {file=test/testdata/resolver/resolve_through_alias.rb start=4:3 end=4:15} method >::> $1>#> () @ Loc {file=test/testdata/resolver/resolve_through_alias.rb start=4:3 end=5:6} argument @ Loc {file=test/testdata/resolver/resolve_through_alias.rb start=??? end=???} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/resolve_through_alias.rb start=3:1 end=3:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=NS) @ Loc {file=test/testdata/resolver/resolve_through_alias.rb start=3:1 end=3:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/resolve_through_alias.rb start=3:1 end=3:10} method > $1>#> () @ Loc {file=test/testdata/resolver/resolve_through_alias.rb start=3:1 end=6:4} argument @ Loc {file=test/testdata/resolver/resolve_through_alias.rb start=??? end=???} diff --git a/test/testdata/resolver/sealed_subclasses.rb b/test/testdata/resolver/sealed_subclasses.rb new file mode 100644 index 0000000000..cb11b9452a --- /dev/null +++ b/test/testdata/resolver/sealed_subclasses.rb @@ -0,0 +1,40 @@ +# typed: true +extend T::Sig + +# A previous version of Sorbet's support for sealed_subclasses manually created +# `ClassType`'s for the elements of the sealed subclasses, instead of +# AppliedTypes, like will normally be created if someone writes a +# `T.class_of(...)` type directly. That meant that once upon a time, the final +# `T.let` below failed to check. +# +# This was particularly pernicious to diagnose, because we print ClassTypes and +# AppliedTypes the same way for singleton classes, so it wasn't obvious why the +# two cases were different. This also means that it's important to use `T.let` +# in this test, because using `T.reveal_type` would show the same type in both +# cases. + +class Parent + extend T::Helpers + abstract! + sealed! +end + +class Child1 < Parent +end + +class Child2 < Parent +end + +sig {params(x: T.any(T.class_of(Child1), T.class_of(Child2))).void} +def foo(x) + T.let(x, T.class_of(Parent)) +end + +y = T.must(Parent.sealed_subclasses.first) +T.let(y, T.class_of(Parent)) + +Parent.sealed_subclasses.each do |klass| + T.reveal_type(klass) # error: `T.any(T.class_of(Child1), T.class_of(Child2))` + instance = klass.new + T.reveal_type(instance) # error: `T.any(Child1, Child2)` +end diff --git a/test/testdata/resolver/self.rb.symbol-table-raw.exp b/test/testdata/resolver/self.rb.symbol-table-raw.exp index 9126bd5e27..b59953e1c2 100644 --- a/test/testdata/resolver/self.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/self.rb.symbol-table-raw.exp @@ -13,11 +13,11 @@ class >> < > () method > $1>#> () @ Loc {file=test/testdata/resolver/self.rb start=3:1 end=20:4} argument @ Loc {file=test/testdata/resolver/self.rb start=??? end=???} class >[>] < > () @ Loc {file=test/testdata/resolver/self.rb start=23:1 end=23:22} - type-member(=) >::> -> LambdaParam(>::>, lower=T.noreturn, upper=) @ Loc {file=test/testdata/resolver/self.rb start=27:3 end=27:21} - method ># (s, ) -> AppliedType { klass = > targs = [ > = LambdaParam(>::>, lower=T.noreturn, upper=) ] } @ Loc {file=test/testdata/resolver/self.rb start=32:3 end=32:15} + type-member(=) >::> -> LambdaParam(>::>, lower=T.noreturn, upper=T.anything) @ Loc {file=test/testdata/resolver/self.rb start=27:3 end=27:21} + method ># (s, ) -> AppliedType { klass = > targs = [ > = LambdaParam(>::>, lower=T.noreturn, upper=T.anything) ] } @ Loc {file=test/testdata/resolver/self.rb start=32:3 end=32:15} argument s<> -> TestSelfGeneric[TestSelfGeneric::Elem] @ Loc {file=test/testdata/resolver/self.rb start=29:12 end=29:13} argument -> T.untyped @ Loc {file=test/testdata/resolver/self.rb start=??? end=???} - method ># () -> AppliedType { klass = > targs = [ > = LambdaParam(>::>, lower=T.noreturn, upper=) ] } @ Loc {file=test/testdata/resolver/self.rb start=39:3 end=39:13} + method ># () -> AppliedType { klass = > targs = [ > = LambdaParam(>::>, lower=T.noreturn, upper=T.anything) ] } @ Loc {file=test/testdata/resolver/self.rb start=39:3 end=39:13} argument -> T.untyped @ Loc {file=test/testdata/resolver/self.rb start=??? end=???} class > $1>[>>] < > $1> (>, >, >) @ Loc {file=test/testdata/resolver/self.rb start=23:1 end=23:22} type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/resolver/self.rb start=23:1 end=23:22} diff --git a/test/testdata/resolver/sig_generated.rb b/test/testdata/resolver/sig_generated.rb index 2fa22cd0b5..ca1a57e556 100644 --- a/test/testdata/resolver/sig_generated.rb +++ b/test/testdata/resolver/sig_generated.rb @@ -4,6 +4,6 @@ sig {returns(NilClass).generated} # ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Malformed `sig`: `generated` is invalid in this context -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Non-private call to private method `generated` on `T::Private::Methods::DeclBuilder` +# ^^^^^^^^^ error: Non-private call to private method `generated` on `T::Private::Methods::DeclBuilder` def generated end diff --git a/test/testdata/resolver/sig_good.rb.symbol-table-raw.exp b/test/testdata/resolver/sig_good.rb.symbol-table-raw.exp index 843b28f690..23b0213f74 100644 --- a/test/testdata/resolver/sig_good.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/sig_good.rb.symbol-table-raw.exp @@ -26,8 +26,7 @@ class >> < > () argument @ Loc {file=test/testdata/resolver/sig_good.rb start=??? end=???} static-field > -> AliasType { symbol = > } @ Loc {file=test/testdata/resolver/sig_good.rb start=5:1 end=5:3} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/sig_good.rb start=4:1 end=4:10} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/sig_good.rb start=4:1 end=4:10} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=M1) @ Loc {file=test/testdata/resolver/sig_good.rb start=4:1 end=4:10} + class > $1> < > () @ Loc {file=test/testdata/resolver/sig_good.rb start=4:1 end=4:10} method > $1>#> () @ Loc {file=test/testdata/resolver/sig_good.rb start=4:1 end=4:15} argument @ Loc {file=test/testdata/resolver/sig_good.rb start=??? end=???} class > < > () @ Loc {file=test/testdata/resolver/sig_good.rb start=2:1 end=2:9} diff --git a/test/testdata/resolver/strict.rb b/test/testdata/resolver/strict.rb index 3229dc34ff..f2d971a998 100644 --- a/test/testdata/resolver/strict.rb +++ b/test/testdata/resolver/strict.rb @@ -1,8 +1,9 @@ # typed: strict -A = String.new # error: Constants must have type annotations with `T.let` when specifying `# typed: strict` +A = String.new B = T.let(T.unsafe(nil), T.untyped) C = T.let(1, Integer) D = T.type_alias {Integer} +E = '' + '' # error: Constants must have type annotations with `T.let` when specifying `# typed: strict` diff --git a/test/testdata/resolver/stub_missing_class_alias.rb.symbol-table-raw.exp b/test/testdata/resolver/stub_missing_class_alias.rb.symbol-table-raw.exp index d15c105a67..6a1f5dafe4 100644 --- a/test/testdata/resolver/stub_missing_class_alias.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/stub_missing_class_alias.rb.symbol-table-raw.exp @@ -5,7 +5,7 @@ class >> < > () module > < >::>::>::> () @ Loc {file=test/testdata/resolver/stub_missing_class_alias.rb start=4:7 end=4:8} class >::> < > () @ Loc {file=test/testdata/resolver/stub_missing_class_alias.rb start=4:1 end=4:11} static-field >::>::> -> Integer @ Loc {file=test/testdata/resolver/stub_missing_class_alias.rb start=9:3 end=9:7} - static-field >::>::> @ Loc {file=test/testdata/resolver/stub_missing_class_alias.rb start=8:3 end=8:11} + static-field >::>::> -> AliasType { symbol = >::>::>::> } @ Loc {file=test/testdata/resolver/stub_missing_class_alias.rb start=8:3 end=8:11} class >::>::> < > () @ Loc {file=test/testdata/resolver/stub_missing_class_alias.rb start=5:3 end=5:10} class >::>::> $1>[>>] < > $1> () @ Loc {file=test/testdata/resolver/stub_missing_class_alias.rb start=5:3 end=5:10} type-member(+) >::>::> $1>::>> -> LambdaParam(>::>::> $1>::>>, lower=T.noreturn, upper=O::B::J) @ Loc {file=test/testdata/resolver/stub_missing_class_alias.rb start=5:3 end=5:10} @@ -19,6 +19,5 @@ class >> < > () argument arg0<> -> O::B::Document::J (unresolved) @ Loc {file=test/testdata/resolver/stub_missing_class_alias.rb start=16:7 end=16:11} argument arg1<> -> O::B::Doc1::J (unresolved) @ Loc {file=test/testdata/resolver/stub_missing_class_alias.rb start=17:7 end=17:11} argument -> T.untyped @ Loc {file=test/testdata/resolver/stub_missing_class_alias.rb start=??? end=???} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/stub_missing_class_alias.rb start=4:7 end=4:8} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=O) @ Loc {file=test/testdata/resolver/stub_missing_class_alias.rb start=4:7 end=4:8} + class > $1> < > () @ Loc {file=test/testdata/resolver/stub_missing_class_alias.rb start=4:7 end=4:8} diff --git a/test/testdata/resolver/t_class.rb b/test/testdata/resolver/t_class.rb new file mode 100644 index 0000000000..d0461f4419 --- /dev/null +++ b/test/testdata/resolver/t_class.rb @@ -0,0 +1,8 @@ +# typed: strict +extend T::Sig + +sig {returns(Class)} +# ^^^^^ error: Generic class without type arguments `Class` +def example + Integer +end diff --git a/test/testdata/resolver/t_class.rb.autocorrects.exp b/test/testdata/resolver/t_class.rb.autocorrects.exp new file mode 100644 index 0000000000..a4e4c46cdd --- /dev/null +++ b/test/testdata/resolver/t_class.rb.autocorrects.exp @@ -0,0 +1,10 @@ +# -- test/testdata/resolver/t_class.rb -- +# typed: strict +extend T::Sig + +sig {returns(T::Class[T.anything])} +# ^^^^^ error: Generic class without type arguments `Class` +def example + Integer +end +# ------------------------------ diff --git a/test/testdata/resolver/type_member_constant_assignment.rb.symbol-table-raw.exp b/test/testdata/resolver/type_member_constant_assignment.rb.symbol-table-raw.exp index 83b61234a0..7fd4a0cc45 100644 --- a/test/testdata/resolver/type_member_constant_assignment.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/type_member_constant_assignment.rb.symbol-table-raw.exp @@ -4,25 +4,21 @@ class >> < > () argument @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=3:1 end=3:9} static-field-type-alias >::> -> AppliedType { klass = >::> targs = [ > = Integer ] } @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=4:3 end=4:9} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=3:1 end=3:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=3:1 end=3:9} + class > $1> < > () @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=3:1 end=3:9} method > $1>#> () @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=3:1 end=5:4} argument @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=7:1 end=7:9} static-field >::> -> AliasType { symbol = >::> } @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=8:3 end=8:9} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=7:1 end=7:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=B) @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=7:1 end=7:9} + class > $1> < > () @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=7:1 end=7:9} method > $1>#> () @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=7:1 end=9:4} argument @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=11:1 end=11:9} module >::>[>] < >::>::>::> () @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=12:3 end=12:16} - type-member(+) >::>::> -> LambdaParam(>::>::>, lower=T.noreturn, upper=) @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=15:5 end=15:26} - class >::> $1>[>>] < > (>, >) @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=12:3 end=12:16} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = >::> targs = [ > = ] }) @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=12:3 end=12:16} + type-member(+) >::>::> -> LambdaParam(>::>::>, lower=T.noreturn, upper=T.anything) @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=15:5 end=15:26} + class >::> $1> < > (>, >) @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=12:3 end=12:16} method >::> $1>#> () @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=12:3 end=16:6} argument @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=??? end=???} - class > $1>[>>] < > () @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=11:1 end=11:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=C) @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=11:1 end=11:9} + class > $1> < > () @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=11:1 end=11:9} method > $1>#> () @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=11:1 end=17:4} argument @ Loc {file=test/testdata/resolver/type_member_constant_assignment.rb start=??? end=???} diff --git a/test/testdata/resolver/type_member_cycle.rb b/test/testdata/resolver/type_member_cycle.rb index 59711211bc..74ed397fc2 100644 --- a/test/testdata/resolver/type_member_cycle.rb +++ b/test/testdata/resolver/type_member_cycle.rb @@ -17,12 +17,24 @@ class B # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Type member `B::X` is involved in a cycle sig {returns(X)} - def test + def test_method 10 end end -T.reveal_type(B.new.test) # error: Revealed type: `T.untyped` +# This error is not great. This happens because the type member cycle on `B` +# prevents us from resolving `` on `T.class_of(B)`. We should +# probably recover from this problem better, making it so that +# `` is not bounded by `` even when this error +# happens. +# +# Before, we got around this because both `Foo.new` and `self.new` had +# intrinsics that forcibly set the result type based on the type of the +# receiver, but there was always this lurking problem. +b = B.new +T.reveal_type(b) # error: `` +res = B.new.test_method # error: Method `test_method` does not exist on `` +T.reveal_type(res) # error: `T.untyped` class C extend T::Generic diff --git a/test/testdata/resolver/type_member_missing.rb.symbol-table-raw.exp b/test/testdata/resolver/type_member_missing.rb.symbol-table-raw.exp index 50f35b1d74..e5ca4e2a5d 100644 --- a/test/testdata/resolver/type_member_missing.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/type_member_missing.rb.symbol-table-raw.exp @@ -3,7 +3,7 @@ class >> < > () method >> $1>#> $CENSORED> () @ Loc {file=test/testdata/resolver/type_member_missing.rb start=3:1 end=12:4} argument @ Loc {file=test/testdata/resolver/type_member_missing.rb start=??? end=???} class >[>] < > () @ Loc {file=test/testdata/resolver/type_member_missing.rb start=3:1 end=3:11} - type-member(=) >::> -> LambdaParam(>::>, lower=T.noreturn, upper=) @ Loc {file=test/testdata/resolver/type_member_missing.rb start=6:3 end=6:21} + type-member(=) >::> -> LambdaParam(>::>, lower=T.noreturn, upper=T.anything) @ Loc {file=test/testdata/resolver/type_member_missing.rb start=6:3 end=6:21} class > $1>[>>] < > $1> (>, >) @ Loc {file=test/testdata/resolver/type_member_missing.rb start=3:1 end=3:11} type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/resolver/type_member_missing.rb start=3:1 end=3:11} method > $1>#> () @ Loc {file=test/testdata/resolver/type_member_missing.rb start=3:1 end=7:4} diff --git a/test/testdata/resolver/type_member_parent_missing.rb b/test/testdata/resolver/type_member_parent_missing.rb index 09fa53a329..fb53bccf44 100644 --- a/test/testdata/resolver/type_member_parent_missing.rb +++ b/test/testdata/resolver/type_member_parent_missing.rb @@ -10,7 +10,6 @@ class Foo1 class Bar1 < Foo1 # ^^^^^^^^^^^^^^^^^ error: Missing definition for abstract method `Enumerable#each` Elem = type_member(:out) - # ^^^^^^^^^^^^^^^^^^^^^^^^ error: Classes can only have invariant type members # ^^^^^^^^^^^^^^^^^^^^^^^^ error: Type variance mismatch for `Elem` with parent `Foo1`. Child `Bar1` should be `invariant`, but it is `:out` # ^^^^^^^^^^^ error: Method `type_member` does not exist on `T.class_of(Bar1)` end @@ -19,14 +18,13 @@ class Foo2 # ^^^^^^^^^^ error: Missing definition for abstract method `Enumerable#each` include Enumerable Elem = T.let(0, Integer) - # ^^^^ error: Type variable `Elem` needs to be declared as `= type_member(SOMETHING)` + # ^^^^ error: Type variable `Elem` needs to be declared as a type_member or type_template, not a static-field end class Bar2 < Foo2 # ^^^^^^^^^^^^^^^^^ error: Type `Elem` declared by parent `Foo2` must be re-declared in `Bar2` # ^^^^^^^^^^^^^^^^^ error: Missing definition for abstract method `Enumerable#each` Elem = type_member(:out) - # ^^^^^^^^^^^^^^^^^^^^^^^^ error: Classes can only have invariant type members # ^^^^^^^^^^^^^^^^^ error: `Bar2::Elem` is a type member but `Foo2::Elem` is not a type member # ^^^^^^^^^^^ error: Method `type_member` does not exist on `T.class_of(Bar2)` end diff --git a/test/testdata/resolver/type_member_singleton_members.rb.symbol-table-raw.exp b/test/testdata/resolver/type_member_singleton_members.rb.symbol-table-raw.exp index 9f2b95fb41..440e14e29a 100644 --- a/test/testdata/resolver/type_member_singleton_members.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/type_member_singleton_members.rb.symbol-table-raw.exp @@ -8,12 +8,12 @@ class >> < > () method > $1>#> () @ Loc {file=test/testdata/resolver/type_member_singleton_members.rb start=3:1 end=13:4} argument @ Loc {file=test/testdata/resolver/type_member_singleton_members.rb start=??? end=???} type-member(+) > $1>::> -> LambdaParam(> $1>::>, lower=T.noreturn, upper=String) @ Loc {file=test/testdata/resolver/type_member_singleton_members.rb start=6:5 end=6:44} - class > $1> $1>[>>, >] < > $1> $1> (>, >) @ Loc {file=test/testdata/resolver/type_member_singleton_members.rb start=8:5 end=8:10} + class > $1> $1>[>>, >] < > (>, >) @ Loc {file=test/testdata/resolver/type_member_singleton_members.rb start=8:5 end=8:10} type-member(+) > $1> $1>::>> -> LambdaParam(> $1> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > $1> targs = [ >> = A > = String ] }) @ Loc {file=test/testdata/resolver/type_member_singleton_members.rb start=4:3 end=4:8} method > $1> $1>#> () @ Loc {file=test/testdata/resolver/type_member_singleton_members.rb start=4:3 end=12:6} argument @ Loc {file=test/testdata/resolver/type_member_singleton_members.rb start=??? end=???} type-member(+) > $1> $1>::> -> LambdaParam(> $1> $1>::>, lower=T.noreturn, upper=Integer) @ Loc {file=test/testdata/resolver/type_member_singleton_members.rb start=10:7 end=10:47} - class > $1> $1> $1>[>>] < > $1> $1> $1> (>, >) @ Loc {file=test/testdata/resolver/type_member_singleton_members.rb start=8:5 end=8:10} + class > $1> $1> $1>[>>] < > $1> (>, >) @ Loc {file=test/testdata/resolver/type_member_singleton_members.rb start=8:5 end=8:10} type-member(+) > $1> $1> $1>::>> -> LambdaParam(> $1> $1> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > $1> $1> targs = [ >> = AppliedType { klass = > $1> targs = [ >> = A > = String ] } > = Integer ] }) @ Loc {file=test/testdata/resolver/type_member_singleton_members.rb start=8:5 end=8:10} method > $1> $1> $1>#> () @ Loc {file=test/testdata/resolver/type_member_singleton_members.rb start=8:5 end=11:8} argument @ Loc {file=test/testdata/resolver/type_member_singleton_members.rb start=??? end=???} diff --git a/test/testdata/resolver/type_member_static_field.rb b/test/testdata/resolver/type_member_static_field.rb new file mode 100644 index 0000000000..bc8346cf70 --- /dev/null +++ b/test/testdata/resolver/type_member_static_field.rb @@ -0,0 +1,23 @@ +# typed: true +# disable-fast-path: true + +class Parent1 + extend T::Generic + + X = type_member +end +class Child1 < Parent1 + Elem = type_template + X = Elem # error: Type variable `X` needs to be declared as a type_member or type_template, not a static-field +end + +class Parent2 + extend T::Generic + + X = type_member +end + +class Child2 < Parent2 + Elem = type_member + X = Elem # error: Type variable `X` needs to be declared as a type_member or type_template, not a static-field +end diff --git a/test/testdata/resolver/type_member_type_template_mismatch.rb b/test/testdata/resolver/type_member_type_template_mismatch.rb new file mode 100644 index 0000000000..ee9511b547 --- /dev/null +++ b/test/testdata/resolver/type_member_type_template_mismatch.rb @@ -0,0 +1,12 @@ +# typed: strict +# disable-fast-path: true + +class AbstractRPCMethod + extend T::Generic + + RPCInput = type_member +end + +class TextDocumentHoverMethod < AbstractRPCMethod + RPCInput = type_template # error: `RPCInput` must be declared as a type_member (not a type_template) to match the parent +end diff --git a/test/testdata/resolver/type_members.rb b/test/testdata/resolver/type_members.rb index 842c731bde..ce3235525f 100644 --- a/test/testdata/resolver/type_members.rb +++ b/test/testdata/resolver/type_members.rb @@ -1,8 +1,8 @@ # typed: true # disable-fast-path: true -class CovariantNotAllowed +class ClassCanBeContravariantNow extend T::Generic - Elem = type_member(:in) # error: can only have invariant type members + Elem = type_member(:in) end class Invalids @@ -56,5 +56,5 @@ class BadChild2 < Parent # error: must be re-declared end class BadChild3 < Parent - Elem = 3 # error: Type variable `Elem` needs to be declared as `= type_member(SOMETHING)` + Elem = 3 # error: Type variable `Elem` needs to be declared as a type_member or type_template, not a static-field end diff --git a/test/testdata/resolver/weird_self_asgn.rb b/test/testdata/resolver/weird_self_asgn.rb new file mode 100644 index 0000000000..ef07cd6b23 --- /dev/null +++ b/test/testdata/resolver/weird_self_asgn.rb @@ -0,0 +1,11 @@ +# typed: false + +# Code like this comes up from time to time when when running Sorbet over a +# random Ruby gem, e.g. where Sorbet can't see that DoesNotExist is a class +# that exists in some _other_ gem, not the one we're currently running over. +# +# The only behavior we care about here is that Sorbet doesn't crash + +class A < DoesNotExist # error: The super class `DoesNotExist` of `A` does not derive from `Class` + ::DoesNotExist::X = self +end diff --git a/test/testdata/rewriter/attr.rb.symbol-table-raw.exp b/test/testdata/rewriter/attr.rb.symbol-table-raw.exp index acf9f8cbf1..c3cd1a59e7 100644 --- a/test/testdata/rewriter/attr.rb.symbol-table-raw.exp +++ b/test/testdata/rewriter/attr.rb.symbol-table-raw.exp @@ -9,7 +9,7 @@ class >> < > () field ># -> Integer @ Loc {file=test/testdata/rewriter/attr.rb start=7:5 end=7:8} field ># -> String @ Loc {file=test/testdata/rewriter/attr.rb start=8:5 end=8:8} field ># -> String @ Loc {file=test/testdata/rewriter/attr.rb start=9:5 end=9:8} - method ># () -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/attr.rb start=6:3 end=6:17} + method ># : private () -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/attr.rb start=6:3 end=6:17} argument -> T.untyped @ Loc {file=test/testdata/rewriter/attr.rb start=??? end=???} method ># () -> Float @ Loc {file=test/testdata/rewriter/attr.rb start=33:3 end=33:22} argument -> T.untyped @ Loc {file=test/testdata/rewriter/attr.rb start=??? end=???} diff --git a/test/testdata/rewriter/chalk_odm_document.rb.rewrite-tree.exp b/test/testdata/rewriter/chalk_odm_document.rb.rewrite-tree.exp index 1d0cdb71b0..b057513d68 100644 --- a/test/testdata/rewriter/chalk_odm_document.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/chalk_odm_document.rb.rewrite-tree.exp @@ -17,7 +17,7 @@ class <>> < (::) end def my_parent_method<>(&) - .instance_variable_get(:@my_parent_method) + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -25,7 +25,7 @@ class <>> < (::) end def my_parent_method=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end .prop(:my_parent_method, ::, :without_accessors, true) @@ -41,7 +41,7 @@ class <>> < (::) end def my_child_method<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -49,7 +49,7 @@ class <>> < (::) end def my_child_method=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end .prop(:my_child_method, ::, :without_accessors, true) diff --git a/test/testdata/rewriter/chalk_odm_document_compiled.rb.rewrite-tree.exp b/test/testdata/rewriter/chalk_odm_document_compiled.rb.rewrite-tree.exp index fa9f6100be..2985adb3b9 100644 --- a/test/testdata/rewriter/chalk_odm_document_compiled.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/chalk_odm_document_compiled.rb.rewrite-tree.exp @@ -17,7 +17,10 @@ class <>> < (::) end def my_parent_method<>(&) - .instance_variable_get(:@my_parent_method) + begin + arg2 = .instance_variable_get(:@my_parent_method) + .class().decorator().prop_get_logic(, :my_parent_method, arg2) + end end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -33,7 +36,7 @@ class <>> < (::) .prop(:my_parent_method, ::, :without_accessors, true) - ::Sorbet::Private::Static.keep_def(, :my_parent_method, :attr_reader) + ::Sorbet::Private::Static.keep_def(, :my_parent_method, :genericPropGetter) ::Sorbet::Private::Static.keep_def(, :my_parent_method=, :normal) end diff --git a/test/testdata/rewriter/class_new.rb b/test/testdata/rewriter/class_new.rb index 67fa23ae43..9c0e4c7ff5 100644 --- a/test/testdata/rewriter/class_new.rb +++ b/test/testdata/rewriter/class_new.rb @@ -56,7 +56,7 @@ def self.foo; end end c1 = Class.new do - T.reveal_type self # error: Revealed type: `Class` + T.reveal_type self # error: Revealed type: `T::Class[Object]` end c2 = Class.new(Foo) do @@ -65,7 +65,7 @@ def self.foo; end end Class.new do - T.reveal_type(self) # error: Revealed type: `Class` + T.reveal_type(self) # error: Revealed type: `T::Class[Object]` end Class.new(Foo) do diff --git a/test/testdata/rewriter/class_new.rb.rewrite-tree.exp b/test/testdata/rewriter/class_new.rb.rewrite-tree.exp index f18a3bc268..f66cf742dc 100644 --- a/test/testdata/rewriter/class_new.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/class_new.rb.rewrite-tree.exp @@ -80,7 +80,7 @@ class <>> < (::) c1 = ::.new() do || begin - >(, , ::Class) + >(, , ::T::Class.[](::Object)) ::.reveal_type() end end @@ -95,7 +95,7 @@ class <>> < (::) ::.new() do || begin - >(, , ::Class) + >(, , ::T::Class.[](::Object)) ::.reveal_type() end end diff --git a/test/testdata/rewriter/class_new_strict.rb b/test/testdata/rewriter/class_new_strict.rb index d539f8b249..2db6a78697 100644 --- a/test/testdata/rewriter/class_new_strict.rb +++ b/test/testdata/rewriter/class_new_strict.rb @@ -3,7 +3,10 @@ class A extend T::Sig sig {void} def self.make - cls = Class.new(A) do + _cls = Class.new do + end + + _cls = Class.new(A) do end end end diff --git a/test/testdata/rewriter/class_new_strict.rb.cfg-text.exp b/test/testdata/rewriter/class_new_strict.rb.cfg-text.exp index 12d12b226f..cf8e882c30 100644 --- a/test/testdata/rewriter/class_new_strict.rb.cfg-text.exp +++ b/test/testdata/rewriter/class_new_strict.rb.cfg-text.exp @@ -19,46 +19,77 @@ method ::#make { bb0[rubyRegionId=0, firstDead=-1](): : T.class_of(A) = cast(: NilClass, T.class_of(A)); - $4: T.class_of(Class) = alias - $6: T.class_of(A) = alias - $7: Sorbet::Private::Static::Void = $4: T.class_of(Class).new($6: T.class_of(A)) - $8: T.class_of(A) = + $5: T.class_of(Class)[T::Class[T.anything]] = alias + $6: Sorbet::Private::Static::Void = $5: T.class_of(Class)[T::Class[T.anything]].new() + $7: T.class_of(A) = -> bb2 # backedges -# - bb3(rubyRegionId=0) +# - bb7(rubyRegionId=0) bb1[rubyRegionId=0, firstDead=-1](): -> bb1 # backedges # - bb0(rubyRegionId=0) # - bb5(rubyRegionId=1) -bb2[rubyRegionId=1, firstDead=-1](: T.class_of(A), $7: Sorbet::Private::Static::Void, $8: T.class_of(A)): +bb2[rubyRegionId=1, firstDead=-1](: T.class_of(A), $6: Sorbet::Private::Static::Void, $7: T.class_of(A)): # outerLoops: 1 -> (NilClass ? bb5 : bb3) # backedges # - bb2(rubyRegionId=1) -bb3[rubyRegionId=0, firstDead=3]($7: Sorbet::Private::Static::Void, $8: T.class_of(A)): - cls: Class = Solve<$7, new> - $2: Class = cls - : T.noreturn = return $2: Class - -> bb1 +bb3[rubyRegionId=0, firstDead=-1]($6: Sorbet::Private::Static::Void, $7: T.class_of(A)): + _cls: T::Class[Object] = Solve<$6, new> + : T.class_of(A) = $7 + $18: T.class_of(Class)[T::Class[T.anything]] = alias + $20: T.class_of(A) = alias + $21: Sorbet::Private::Static::Void = $18: T.class_of(Class)[T::Class[T.anything]].new($20: T.class_of(A)) + $22: T.class_of(A) = + -> bb6 # backedges # - bb2(rubyRegionId=1) -bb5[rubyRegionId=1, firstDead=8](: T.class_of(A), $7: Sorbet::Private::Static::Void, $8: T.class_of(A)): +bb5[rubyRegionId=1, firstDead=8](: T.class_of(A), $6: Sorbet::Private::Static::Void, $7: T.class_of(A)): # outerLoops: 1 : T.class_of(A) = loadSelf(new) - $14: T.class_of(T) = alias - $16: T.class_of(A) = alias - keep_for_ide$12: Runtime object representing type: T.class_of(A) = $14: T.class_of(T).class_of($16: T.class_of(A)) - keep_for_ide$12: T.untyped = keep_for_ide$12 - $17: T.class_of(A) = - : T.class_of(A) = cast($17: T.class_of(A), T.class_of(A)); - $18: T.noreturn = blockreturn $10: NilClass + $12: T.class_of(T::Class) = alias + $14: T.class_of(Object) = alias + keep_for_ide$10: Runtime object representing type: T::Class[Object] = $12: T.class_of(T::Class).[]($14: T.class_of(Object)) + keep_for_ide$10: T.untyped = keep_for_ide$10 + $15: T.class_of(A) = + : T::Class[Object] = cast($15: T.class_of(A), T::Class[Object]); + $16: T.noreturn = blockreturn $8: NilClass -> bb2 +# backedges +# - bb3(rubyRegionId=0) +# - bb9(rubyRegionId=2) +bb6[rubyRegionId=2, firstDead=-1](: T.class_of(A), $21: Sorbet::Private::Static::Void, $22: T.class_of(A)): + # outerLoops: 1 + -> (NilClass ? bb9 : bb7) + +# backedges +# - bb6(rubyRegionId=2) +bb7[rubyRegionId=0, firstDead=3]($21: Sorbet::Private::Static::Void, $22: T.class_of(A)): + _cls: T.class_of(A) = Solve<$21, new> + $2: T.class_of(A) = _cls + : T.noreturn = return $2: T.class_of(A) + -> bb1 + +# backedges +# - bb6(rubyRegionId=2) +bb9[rubyRegionId=2, firstDead=8](: T.class_of(A), $21: Sorbet::Private::Static::Void, $22: T.class_of(A)): + # outerLoops: 1 + : T.class_of(A) = loadSelf(new) + $27: T.class_of(T) = alias + $29: T.class_of(A) = alias + keep_for_ide$25: Runtime object representing type: T.class_of(A) = $27: T.class_of(T).class_of($29: T.class_of(A)) + keep_for_ide$25: T.untyped = keep_for_ide$25 + $30: T.class_of(A) = + : T.class_of(A) = cast($30: T.class_of(A), T.class_of(A)); + $31: T.noreturn = blockreturn $23: NilClass + -> bb6 + } method ::# { @@ -87,9 +118,9 @@ bb2[rubyRegionId=1, firstDead=-1](: T.class_of(A), $7 bb3[rubyRegionId=0, firstDead=6]($7: Sorbet::Private::Static::Void, $8: T.class_of(A)): $3: Sorbet::Private::Static::Void = Solve<$7, sig> : T.class_of(A) = $8 - $16: T.class_of(T::Sig) = alias - $18: T.class_of(T) = alias - $13: T.class_of(A) = : T.class_of(A).extend($16: T.class_of(T::Sig)) + $15: T.class_of(T::Sig) = alias + $17: T.class_of(T) = alias + $12: T.class_of(A) = : T.class_of(A).extend($15: T.class_of(T::Sig)) : T.noreturn = return $2: NilClass -> bb1 @@ -98,8 +129,8 @@ bb3[rubyRegionId=0, firstDead=6]($7: Sorbet::Private::Stati bb5[rubyRegionId=1, firstDead=3](: T.class_of(A), $7: Sorbet::Private::Static::Void, $8: T.class_of(A)): # outerLoops: 1 : T::Private::Methods::DeclBuilder = loadSelf(sig) - $10: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.void() - $12: T.noreturn = blockreturn $10: T::Private::Methods::DeclBuilder + $9: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.void() + $11: T.noreturn = blockreturn $9: T::Private::Methods::DeclBuilder -> bb2 } diff --git a/test/testdata/rewriter/class_new_strict.rb.flatten-tree.exp b/test/testdata/rewriter/class_new_strict.rb.flatten-tree.exp index 1c1d986d75..bd4de53938 100644 --- a/test/testdata/rewriter/class_new_strict.rb.flatten-tree.exp +++ b/test/testdata/rewriter/class_new_strict.rb.flatten-tree.exp @@ -11,15 +11,28 @@ begin end class ::A<> < (::) def self.make() - cls = ::Class.new(::A) do || - begin - >(, AppliedType { - klass = > $1> - targs = [ - >> = A - ] - }, ::T.class_of(::A)) - + begin + _cls = ::Class.new() do || + begin + >(, AppliedType { + klass = > + targs = [ + >> = Object + ] + }, ::T::Class.[](::Object)) + + end + end + _cls = ::Class.new(::A) do || + begin + >(, AppliedType { + klass = > $1> + targs = [ + >> = A + ] + }, ::T.class_of(::A)) + + end end end end diff --git a/test/testdata/rewriter/class_new_strict.rb.rewrite-tree.exp b/test/testdata/rewriter/class_new_strict.rb.rewrite-tree.exp index 6fd2a1505a..3b6bf4e3a8 100644 --- a/test/testdata/rewriter/class_new_strict.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/class_new_strict.rb.rewrite-tree.exp @@ -5,10 +5,18 @@ class <>> < (::) end def self.make<>(&) - cls = ::.new(::) do || - begin - >(, , ::T.class_of(::)) - + begin + _cls = ::.new() do || + begin + >(, , ::T::Class.[](::Object)) + + end + end + _cls = ::.new(::) do || + begin + >(, , ::T.class_of(::)) + + end end end end diff --git a/test/testdata/rewriter/command.rb.rewrite-tree.exp b/test/testdata/rewriter/command.rb.rewrite-tree.exp index 00f1061b3c..78cfa3c3d8 100644 --- a/test/testdata/rewriter/command.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/command.rb.rewrite-tree.exp @@ -17,7 +17,7 @@ class <>> < (::) end def self.call<>(x, &) - ::T.unsafe(nil) + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end @@ -39,7 +39,7 @@ class <>> < (::) end def self.call<>(x, &) - ::T.unsafe(nil) + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end diff --git a/test/testdata/rewriter/constant_assume_type.rb b/test/testdata/rewriter/constant_assume_type.rb new file mode 100644 index 0000000000..6a96f96609 --- /dev/null +++ b/test/testdata/rewriter/constant_assume_type.rb @@ -0,0 +1,94 @@ +# typed: true + +class NormalClass +end + +class NewIsNilable + extend T::Sig + + sig {returns(T.nilable(T.attached_class))} + def self.new + return if [true, false].sample + super + end +end + +class NewIsSpecific + extend T::Sig + + sig {returns(NewIsSpecificChild)} + def self.new + NewIsSpecificChild.new + end +end +class NewIsSpecificChild < NewIsSpecific +end + +class GenericClass + extend T::Generic + + Elem = type_member +end + +class GenericClassWithFixed + extend T::Generic + + Elem = type_member {{fixed: Integer}} +end + +module ModuleWithCustomNew + extend T::Sig + + sig {returns(Integer)} + def self.new + 0 + end +end + +A = NormalClass.new +T.reveal_type(A) # error: `NormalClass` + +B = NewIsNilable.new # error: Assumed expression had type `NewIsNilable` but found `T.nilable(NewIsNilable)` +T.reveal_type(B) # error: `NewIsNilable` + +C1 = NewIsSpecific.new +T.reveal_type(C1) # error: `NewIsSpecific` +C2 = T.let(NewIsSpecific.new, NewIsSpecific) +T.reveal_type(C2) # error: `NewIsSpecific` +C3 = NewIsSpecificChild.new +T.reveal_type(C3) # error: `NewIsSpecificChild` + +D1 = Integer.new # error: Expected `String` but found `Integer` for field +D1 = String.new + +D2 = T.let(0, Integer) +D2 = T.let('', String) + +E1 = Integer.new +E2 = '' + '' + +F1 = 0 + 0 +F2 = String.new + +G1 = T.let(0, Integer) +G2 = String.new + +H1 = 0 + 0 +H2 = T.let('', String) + +class SomethingThatHasNew + def new; end +end +NotAClass = SomethingThatHasNew.new + +I = NotAClass.new +T.reveal_type(I) # error: `T.untyped` + +J = GenericClass.new +T.reveal_type(J) # error: `T.untyped` + +K = GenericClassWithFixed.new +T.reveal_type(K) # error: `GenericClassWithFixed` + +L = ModuleWithCustomNew.new +T.reveal_type(L) # error: `T.untyped` diff --git a/test/testdata/rewriter/constant_assume_type.rb.autocorrects.exp b/test/testdata/rewriter/constant_assume_type.rb.autocorrects.exp new file mode 100644 index 0000000000..8f5517dece --- /dev/null +++ b/test/testdata/rewriter/constant_assume_type.rb.autocorrects.exp @@ -0,0 +1,96 @@ +# -- test/testdata/rewriter/constant_assume_type.rb -- +# typed: true + +class NormalClass +end + +class NewIsNilable + extend T::Sig + + sig {returns(T.nilable(T.attached_class))} + def self.new + return if [true, false].sample + super + end +end + +class NewIsSpecific + extend T::Sig + + sig {returns(NewIsSpecificChild)} + def self.new + NewIsSpecificChild.new + end +end +class NewIsSpecificChild < NewIsSpecific +end + +class GenericClass + extend T::Generic + + Elem = type_member +end + +class GenericClassWithFixed + extend T::Generic + + Elem = type_member {{fixed: Integer}} +end + +module ModuleWithCustomNew + extend T::Sig + + sig {returns(Integer)} + def self.new + 0 + end +end + +A = NormalClass.new +T.reveal_type(A) # error: `NormalClass` + +B = T.let(NewIsNilable.new, T.nilable(NewIsNilable)) # error: Assumed expression had type `NewIsNilable` but found `T.nilable(NewIsNilable)` +T.reveal_type(B) # error: `NewIsNilable` + +C1 = NewIsSpecific.new +T.reveal_type(C1) # error: `NewIsSpecific` +C2 = T.let(NewIsSpecific.new, NewIsSpecific) +T.reveal_type(C2) # error: `NewIsSpecific` +C3 = NewIsSpecificChild.new +T.reveal_type(C3) # error: `NewIsSpecificChild` + +D1 = Integer.new # error: Expected `String` but found `Integer` for field +D1 = String.new + +D2 = T.let(0, Integer) +D2 = T.let('', String) + +E1 = Integer.new +E2 = '' + '' + +F1 = 0 + 0 +F2 = String.new + +G1 = T.let(0, Integer) +G2 = String.new + +H1 = 0 + 0 +H2 = T.let('', String) + +class SomethingThatHasNew + def new; end +end +NotAClass = SomethingThatHasNew.new + +I = NotAClass.new +T.reveal_type(I) # error: `T.untyped` + +J = GenericClass.new +T.reveal_type(J) # error: `T.untyped` + +K = GenericClassWithFixed.new +T.reveal_type(K) # error: `GenericClassWithFixed` + +L = ModuleWithCustomNew.new +T.reveal_type(L) # error: `T.untyped` +# ------------------------------ diff --git a/test/testdata/rewriter/constant_assume_type_false__false.rb b/test/testdata/rewriter/constant_assume_type_false__false.rb new file mode 100644 index 0000000000..01097ac68f --- /dev/null +++ b/test/testdata/rewriter/constant_assume_type_false__false.rb @@ -0,0 +1,31 @@ +# typed: false + +class NormalClass +end + +class NewIsNilable + extend T::Sig + + sig {returns(T.nilable(T.attached_class))} + def self.new + return if [true, false].sample + super + end +end + +class NewIsSpecific + extend T::Sig + + sig {returns(NewIsSpecificChild)} + def self.new + NewIsSpecificChild.new + end +end +class NewIsSpecificChild < NewIsSpecific +end + +A = NormalClass.new +B = NewIsNilable.new +C1 = NewIsSpecific.new +C2 = T.let(NewIsSpecific.new, NewIsSpecific) +C3 = NewIsSpecificChild.new diff --git a/test/testdata/rewriter/constant_assume_type_false__true.rb b/test/testdata/rewriter/constant_assume_type_false__true.rb new file mode 100644 index 0000000000..a4d78f262f --- /dev/null +++ b/test/testdata/rewriter/constant_assume_type_false__true.rb @@ -0,0 +1,10 @@ +# typed: true + +T.reveal_type(A) # error: `T.untyped` + +T.reveal_type(B) # error: `T.untyped` + +T.reveal_type(C1) # error: `T.untyped` +T.reveal_type(C2) # error: `NewIsSpecific` +T.reveal_type(C3) # error: `T.untyped` + diff --git a/test/testdata/rewriter/data.rb b/test/testdata/rewriter/data.rb new file mode 100644 index 0000000000..cbef1da821 --- /dev/null +++ b/test/testdata/rewriter/data.rb @@ -0,0 +1,102 @@ +# typed: true +require_relative "../../t" + +module Foo + class Data + end +end + +class NotData + B = T.let(Foo::Data.new, Foo::Data) + var = Data.define(:foo) +end + +class RealData + A = Data.define(:foo, :bar) +end + +class RealDataDesugar + class A < Data + extend T::Sig + def foo; end + def bar; end + sig {params(foo: BasicObject, bar: BasicObject).returns(A)} + def self.new(foo=nil, bar=nil) + T.cast(nil, A) + end + end +end + +class TwoDatas + A = Data.define(:foo) + B = Data.define(:foo) +end + +class AccidentallyData + class Data + def self.define; end + end + + # We do this in the Rewriter pass before we've typeAlias the constants + A = Data.define(:foo, :bar) +end + +class InvalidMember + A = Data.define(:foo=) # error: Data member `foo=` cannot end with an equal +end + +class MixinData + module MyMixin + def foo; end + end + + MyData = Data.define(:x) do + include MyMixin + self.new(1).x + self.new(1).foo + end + + MyData.new(1).x + MyData.new(1).foo +end + +class BadUsages + A = Data.define # error: Not enough arguments provided for method `Data.define`. Expected: `1+`, got: `0` + B = Data.define(giberish: 1) + # ^^^^^^^^^^^ error: Expected `T.any(Symbol, String)` but found `{giberish: Integer(1)}` for argument `arg0` + + C = Data.define(:c) + c_data = C.new(1) + c_data.c = 6 # error: Method `c=` does not exist on `BadUsages::C` +end + +class Main + def main + a = Data.define(:foo) + # a.is_a?(Data) is actually false, because `Data.define` dynamically + # allocates and returns a class object for this struct, but we don't + # have a great way to model that statically in the case where the + # result is assigned to a local variable, not a constant. + T.assert_type!(a, Data) + T.assert_type!(a.new(2), Data) + + # This should raise a "Not enough arguments" error, but it doesn't because the rewriter + # currently doesn't know how to typecheck when LHS is an ident instead of a constant. + # Is this okay? + a.new + + T.assert_type!(RealData::A.new(2, 3), RealData::A) + + T.assert_type!(RealDataDesugar::A.new(2, 3), RealDataDesugar::A) + end +end +puts Main.new.main + +class FullyQualifiedDataUsages + Foo = Data.define(:a) + Bar = ::Data.define(:a) + Baz = ::Foo::Data.new + + Foo.new(1).a + Bar.new(1).a +end diff --git a/test/testdata/rewriter/flatten_module_private_class_method.rb.symbol-table-raw.exp b/test/testdata/rewriter/flatten_module_private_class_method.rb.symbol-table-raw.exp index 1a6298253e..0a68f9ec9c 100644 --- a/test/testdata/rewriter/flatten_module_private_class_method.rb.symbol-table-raw.exp +++ b/test/testdata/rewriter/flatten_module_private_class_method.rb.symbol-table-raw.exp @@ -3,8 +3,7 @@ class >> < > () method >> $1>#> $CENSORED> () @ Loc {file=test/testdata/rewriter/flatten_module_private_class_method.rb start=3:1 end=15:4} argument @ Loc {file=test/testdata/rewriter/flatten_module_private_class_method.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/rewriter/flatten_module_private_class_method.rb start=3:1 end=3:9} - class > $1>[>>] < > () @ Loc {file=test/testdata/rewriter/flatten_module_private_class_method.rb start=3:1 end=3:9} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/rewriter/flatten_module_private_class_method.rb start=3:1 end=3:9} + class > $1> < > () @ Loc {file=test/testdata/rewriter/flatten_module_private_class_method.rb start=3:1 end=3:9} method > $1>#> () @ Loc {file=test/testdata/rewriter/flatten_module_private_class_method.rb start=3:1 end=8:4} argument @ Loc {file=test/testdata/rewriter/flatten_module_private_class_method.rb start=??? end=???} method > $1># () @ Loc {file=test/testdata/rewriter/flatten_module_private_class_method.rb start=5:26 end=5:38} diff --git a/test/testdata/rewriter/flatten_nested_sclass.rb.symbol-table-raw.exp b/test/testdata/rewriter/flatten_nested_sclass.rb.symbol-table-raw.exp index 394e409769..097b61a45d 100644 --- a/test/testdata/rewriter/flatten_nested_sclass.rb.symbol-table-raw.exp +++ b/test/testdata/rewriter/flatten_nested_sclass.rb.symbol-table-raw.exp @@ -11,7 +11,7 @@ class >> < > () argument @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=??? end=???} method > $1># () @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=4:3 end=4:15} argument @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=??? end=???} - class > $1> $1>[>>] < > $1> $1> () @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=5:5 end=5:10} + class > $1> $1>[>>] < > () @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=5:5 end=5:10} type-member(+) > $1> $1>::>> -> LambdaParam(> $1> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > $1> targs = [ >> = A ] }) @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=5:5 end=5:10} method > $1> $1>#> () @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=5:5 end=7:8} argument @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=??? end=???} @@ -24,7 +24,7 @@ class >> < > () argument @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=??? end=???} method > $1># () @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=17:7 end=17:14} argument @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=??? end=???} - class > $1> $1>[>>] < > $1> $1> () @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=16:5 end=16:10} + class > $1> $1>[>>] < > () @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=16:5 end=16:10} type-member(+) > $1> $1>::>> -> LambdaParam(> $1> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > $1> targs = [ >> = B ] }) @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=16:5 end=16:10} method > $1> $1>#> () @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=16:5 end=18:8} argument @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=??? end=???} @@ -35,13 +35,13 @@ class >> < > () argument @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=??? end=???} method > $1># () @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=27:3 end=27:15} argument @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=??? end=???} - class > $1> $1>[>>] < > $1> $1> () @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=29:7 end=29:12} + class > $1> $1>[>>] < > () @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=29:7 end=29:12} type-member(+) > $1> $1>::>> -> LambdaParam(> $1> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > $1> targs = [ >> = C ] }) @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=28:5 end=28:10} method > $1> $1>#> () @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=28:5 end=32:8} argument @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=??? end=???} method > $1> $1># () @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=30:9 end=30:16} argument @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=??? end=???} - class > $1> $1> $1>[>>] < > $1> $1> $1> () @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=29:7 end=29:12} + class > $1> $1> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=29:7 end=29:12} type-member(+) > $1> $1> $1>::>> -> LambdaParam(> $1> $1> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > $1> $1> targs = [ >> = AppliedType { klass = > $1> targs = [ >> = C ] } ] }) @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=29:7 end=29:12} method > $1> $1> $1>#> () @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=29:7 end=31:10} argument @ Loc {file=test/testdata/rewriter/flatten_nested_sclass.rb start=??? end=???} diff --git a/test/testdata/rewriter/has_attached_class.rb b/test/testdata/rewriter/has_attached_class.rb new file mode 100644 index 0000000000..559a63a1d6 --- /dev/null +++ b/test/testdata/rewriter/has_attached_class.rb @@ -0,0 +1,18 @@ +# typed: true + +class A1 + extend T::Generic + has_attached_class! # error: `has_attached_class!` can only be used inside a `module`, not a `class` +end + +module A2 + extend T::Generic + has_attached_class!(:in) + # ^^^ error: `has_attached_class!` cannot be declared `:in`, only invariant or `:out` +end + +module A3 + has_attached_class! + #^^^^^^^^^^^^^^^^^^^ error: does not exist + #^^^^^^^^^^^^^^^^^^^ error: does not exist +end diff --git a/test/testdata/rewriter/has_attached_class.rb.autocorrects.exp b/test/testdata/rewriter/has_attached_class.rb.autocorrects.exp new file mode 100644 index 0000000000..823483fd2c --- /dev/null +++ b/test/testdata/rewriter/has_attached_class.rb.autocorrects.exp @@ -0,0 +1,20 @@ +# -- test/testdata/rewriter/has_attached_class.rb -- +# typed: true + +class A1 + extend T::Generic + has_attached_class! # error: `has_attached_class!` can only be used inside a `module`, not a `class` +end + +module A2 + extend T::Generic + has_attached_class!(:out) + # ^^^ error: `has_attached_class!` cannot be declared `:in`, only invariant or `:out` +end + +module A3 + has_attached_class! + #^^^^^^^^^^^^^^^^^^^ error: does not exist + #^^^^^^^^^^^^^^^^^^^ error: does not exist +end +# ------------------------------ diff --git a/test/testdata/rewriter/initializer.rb b/test/testdata/rewriter/initializer.rb index 8c41413a46..b39dc5f3ba 100644 --- a/test/testdata/rewriter/initializer.rb +++ b/test/testdata/rewriter/initializer.rb @@ -174,3 +174,44 @@ def initialize(x) @x = x end end + +class TProcBindInInitializerLet + extend T::Sig + + sig {params(blk: T.proc.bind(String).void).void } + def initialize(&blk) + @blk = blk + end +end + +class NoBindInTProcInitializerLet + extend T::Sig + + sig {params(blk: T.proc.void).void } + def initialize(&blk) + @blk = blk + end +end + +class TProcBindInInitializerLastSend + extend T::Sig + + sig {params(blk: T.proc.bind(String)).void } + # ^^^^^^^^^^^^^^^^^^^ error: Malformed T.proc: You must specify a return type + # ^^^^^^^^^^^^^^^^^^^ error: Malformed T.proc: You must specify a return type + # ^^^^^^^^^^^^^^^^^^^ error: Using `bind` is not permitted here + def initialize(&blk) + @blk = blk + end +end + +class TProcBindInInitializerManyBinds + extend T::Sig + + sig {params(blk: T.proc.bind(String).bind(String).void).void } + # ^^^^^^^^^^^^^^^^^^^ error: Malformed `bind`: Multiple calls to `.bind` + # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Using `bind` is not permitted here + def initialize(&blk) + @blk = blk + end +end diff --git a/test/testdata/rewriter/not_prop.rb.rewrite-tree.exp b/test/testdata/rewriter/not_prop.rb.rewrite-tree.exp index e0cf0146b1..c080e13eb5 100644 --- a/test/testdata/rewriter/not_prop.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/not_prop.rb.rewrite-tree.exp @@ -5,7 +5,7 @@ class <>> < (::) end def array_of_explicit<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -13,7 +13,7 @@ class <>> < (::) end def array_of_explicit=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end .prop(:type, :type, ::) diff --git a/test/testdata/rewriter/prop.rb.rewrite-tree-raw.exp b/test/testdata/rewriter/prop.rb.rewrite-tree-raw.exp index 6ece35e9d2..aeb0b302d0 100644 --- a/test/testdata/rewriter/prop.rb.rewrite-tree-raw.exp +++ b/test/testdata/rewriter/prop.rb.rewrite-tree-raw.exp @@ -1390,21 +1390,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -1475,21 +1463,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -1796,21 +1772,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -1881,21 +1845,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -1962,21 +1914,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -2071,21 +2011,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -2140,21 +2068,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -2225,21 +2141,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -2309,21 +2213,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -2424,21 +2316,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -2512,21 +2392,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -2635,21 +2503,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -2704,21 +2560,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -2773,21 +2617,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -2842,21 +2674,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -2927,21 +2747,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -2996,21 +2804,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -3081,21 +2877,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -3196,21 +2980,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -3299,21 +3071,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -3368,21 +3128,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -3453,21 +3201,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -3568,21 +3304,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -3671,21 +3395,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -3740,21 +3452,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -3825,21 +3525,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -3940,21 +3628,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -4043,21 +3719,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -4112,21 +3776,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -4197,21 +3849,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -4308,21 +3948,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -4419,21 +4047,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -4488,21 +4104,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -4573,21 +4177,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -4654,21 +4246,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -4763,21 +4343,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -4832,21 +4400,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -4917,21 +4473,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -4986,21 +4530,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -5071,21 +4603,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -5729,21 +5249,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -5814,21 +5322,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -5883,21 +5379,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -5968,21 +5452,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -6148,21 +5620,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -6233,21 +5693,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -6302,21 +5750,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr diff --git a/test/testdata/rewriter/prop.rb.rewrite-tree.exp b/test/testdata/rewriter/prop.rb.rewrite-tree.exp index 2c26413ca9..8b966cb32b 100644 --- a/test/testdata/rewriter/prop.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/prop.rb.rewrite-tree.exp @@ -76,7 +76,7 @@ class <>> < (::) end def foo<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -84,7 +84,7 @@ class <>> < (::) end def foo=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig() do || @@ -127,7 +127,7 @@ class <>> < (::) end def default<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -135,7 +135,7 @@ class <>> < (::) end def default=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -143,7 +143,7 @@ class <>> < (::) end def t_nilable<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -151,7 +151,7 @@ class <>> < (::) end def t_nilable=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -159,7 +159,7 @@ class <>> < (::) end def array<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -167,7 +167,7 @@ class <>> < (::) end def array=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -175,7 +175,7 @@ class <>> < (::) end def t_array<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -183,7 +183,7 @@ class <>> < (::) end def t_array=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -191,7 +191,7 @@ class <>> < (::) end def hash_of<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -199,7 +199,7 @@ class <>> < (::) end def hash_of=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -207,7 +207,7 @@ class <>> < (::) end def const_explicit<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -215,7 +215,7 @@ class <>> < (::) end def const<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -223,7 +223,7 @@ class <>> < (::) end def enum_prop<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -231,7 +231,7 @@ class <>> < (::) end def enum_prop=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -239,7 +239,7 @@ class <>> < (::) end def foreign<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -247,7 +247,7 @@ class <>> < (::) end def foreign=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -255,7 +255,7 @@ class <>> < (::) end def foreign_<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -263,7 +263,7 @@ class <>> < (::) end def foreign_!<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -271,7 +271,7 @@ class <>> < (::) end def foreign_lazy<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -279,7 +279,7 @@ class <>> < (::) end def foreign_lazy=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -287,7 +287,7 @@ class <>> < (::) end def foreign_lazy_<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -295,7 +295,7 @@ class <>> < (::) end def foreign_lazy_!<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -303,7 +303,7 @@ class <>> < (::) end def foreign_proc<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -311,7 +311,7 @@ class <>> < (::) end def foreign_proc=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -319,7 +319,7 @@ class <>> < (::) end def foreign_proc_<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -327,7 +327,7 @@ class <>> < (::) end def foreign_proc_!<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -335,7 +335,7 @@ class <>> < (::) end def foreign_invalid<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -343,7 +343,7 @@ class <>> < (::) end def foreign_invalid=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -351,7 +351,7 @@ class <>> < (::) end def foreign_invalid_<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -359,7 +359,7 @@ class <>> < (::) end def foreign_invalid_!<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -367,7 +367,7 @@ class <>> < (::) end def ifunset<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -375,7 +375,7 @@ class <>> < (::) end def ifunset=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -383,7 +383,7 @@ class <>> < (::) end def ifunset_nilable<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -391,7 +391,7 @@ class <>> < (::) end def ifunset_nilable=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -399,7 +399,7 @@ class <>> < (::) end def empty_hash_rules<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -407,7 +407,7 @@ class <>> < (::) end def empty_hash_rules=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -415,7 +415,7 @@ class <>> < (::) end def hash_rules<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -423,7 +423,7 @@ class <>> < (::) end def hash_rules=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end .include(::::) @@ -541,7 +541,7 @@ class <>> < (::) end def token<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -549,7 +549,7 @@ class <>> < (::) end def token=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -557,7 +557,7 @@ class <>> < (::) end def created<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -565,7 +565,7 @@ class <>> < (::) end def created=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end .include(::::) @@ -601,7 +601,7 @@ class <>> < (::) end def token<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -609,7 +609,7 @@ class <>> < (::) end def token=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -617,7 +617,7 @@ class <>> < (::) end def created<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end .include(::::) diff --git a/test/testdata/rewriter/prop.rb.symbol-table-raw.exp b/test/testdata/rewriter/prop.rb.symbol-table-raw.exp index 8011a7126e..656038a4b2 100644 --- a/test/testdata/rewriter/prop.rb.symbol-table-raw.exp +++ b/test/testdata/rewriter/prop.rb.symbol-table-raw.exp @@ -112,10 +112,8 @@ class >> < > () type-member(+) >::>::> $1>::>> -> LambdaParam(>::>::> $1>::>>, lower=T.noreturn, upper=Chalk::ODM::Document) @ Loc {file=test/testdata/rewriter/prop.rb start=82:1 end=82:27} method >::>::> $1>#> () @ Loc {file=test/testdata/rewriter/prop.rb start=82:1 end=83:4} argument @ Loc {file=test/testdata/rewriter/prop.rb start=??? end=???} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/rewriter/prop.rb start=82:7 end=82:17} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Chalk::ODM) @ Loc {file=test/testdata/rewriter/prop.rb start=82:7 end=82:17} - class > $1>[>>] < > () @ Loc {file=test/testdata/rewriter/prop.rb start=82:7 end=82:12} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Chalk) @ Loc {file=test/testdata/rewriter/prop.rb start=82:7 end=82:12} + class >::> $1> < > () @ Loc {file=test/testdata/rewriter/prop.rb start=82:7 end=82:17} + class > $1> < > () @ Loc {file=test/testdata/rewriter/prop.rb start=82:7 end=82:12} class > < > (>) @ Loc {file=test/testdata/rewriter/prop.rb start=87:1 end=87:20} method ># () -> String | NilClass @ Loc {file=test/testdata/rewriter/prop.rb start=91:3 end=91:56} argument -> T.untyped @ Loc {file=test/testdata/rewriter/prop.rb start=??? end=???} @@ -164,16 +162,11 @@ class >> < > () type-member(+) >::>::>::>::>::> $1>::>> -> LambdaParam(>::>::>::>::>::> $1>::>>, lower=T.noreturn, upper=Opus::DB::Model::Mixins::Encryptable::EncryptedValue) @ Loc {file=test/testdata/rewriter/prop.rb start=84:1 end=84:82} method >::>::>::>::>::> $1>#> () @ Loc {file=test/testdata/rewriter/prop.rb start=84:1 end=85:4} argument @ Loc {file=test/testdata/rewriter/prop.rb start=??? end=???} - class >::>::>::>::> $1>[>>] < > () @ Loc {file=test/testdata/rewriter/prop.rb start=84:7 end=84:43} - type-member(+) >::>::>::>::> $1>::>> -> LambdaParam(>::>::>::>::> $1>::>>, lower=T.noreturn, upper=Opus::DB::Model::Mixins::Encryptable) @ Loc {file=test/testdata/rewriter/prop.rb start=84:7 end=84:43} - class >::>::>::> $1>[>>] < > () @ Loc {file=test/testdata/rewriter/prop.rb start=84:7 end=84:30} - type-member(+) >::>::>::> $1>::>> -> LambdaParam(>::>::>::> $1>::>>, lower=T.noreturn, upper=Opus::DB::Model::Mixins) @ Loc {file=test/testdata/rewriter/prop.rb start=84:7 end=84:30} - class >::>::> $1>[>>] < > () @ Loc {file=test/testdata/rewriter/prop.rb start=84:7 end=84:22} - type-member(+) >::>::> $1>::>> -> LambdaParam(>::>::> $1>::>>, lower=T.noreturn, upper=Opus::DB::Model) @ Loc {file=test/testdata/rewriter/prop.rb start=84:7 end=84:22} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/rewriter/prop.rb start=84:7 end=84:15} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Opus::DB) @ Loc {file=test/testdata/rewriter/prop.rb start=84:7 end=84:15} - class > $1>[>>] < > () @ Loc {file=test/testdata/rewriter/prop.rb start=84:7 end=84:11} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Opus) @ Loc {file=test/testdata/rewriter/prop.rb start=84:7 end=84:11} + class >::>::>::>::> $1> < > () @ Loc {file=test/testdata/rewriter/prop.rb start=84:7 end=84:43} + class >::>::>::> $1> < > () @ Loc {file=test/testdata/rewriter/prop.rb start=84:7 end=84:30} + class >::>::> $1> < > () @ Loc {file=test/testdata/rewriter/prop.rb start=84:7 end=84:22} + class >::> $1> < > () @ Loc {file=test/testdata/rewriter/prop.rb start=84:7 end=84:15} + class > $1> < > () @ Loc {file=test/testdata/rewriter/prop.rb start=84:7 end=84:11} class > < > (>) @ Loc {file=test/testdata/rewriter/prop.rb start=64:1 end=64:18} method ># () -> Float @ Loc {file=test/testdata/rewriter/prop.rb start=69:3 end=69:15} argument -> T.untyped @ Loc {file=test/testdata/rewriter/prop.rb start=??? end=???} diff --git a/test/testdata/rewriter/prop_compiled.rb.rewrite-tree-raw.exp b/test/testdata/rewriter/prop_compiled.rb.rewrite-tree-raw.exp index e9f2360224..790440cb87 100644 --- a/test/testdata/rewriter/prop_compiled.rb.rewrite-tree-raw.exp +++ b/test/testdata/rewriter/prop_compiled.rb.rewrite-tree-raw.exp @@ -3409,21 +3409,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -3729,21 +3717,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -3832,21 +3808,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -4152,21 +4116,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -4255,21 +4207,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -4575,21 +4515,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -4678,21 +4606,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -4994,21 +4910,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -5105,21 +5009,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -5174,21 +5066,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -5359,21 +5239,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr @@ -5878,21 +5746,9 @@ ClassDef{ } }] rhs = Send{ flags = {rewriterSynthesized} - recv = Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::T) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (module ::Kernel) - orig = nullptr - } - ] + recv = ConstantLit{ + symbol = (module ::Kernel) + orig = nullptr } fun = block = nullptr diff --git a/test/testdata/rewriter/prop_compiled.rb.rewrite-tree.exp b/test/testdata/rewriter/prop_compiled.rb.rewrite-tree.exp index b0aa4090ad..22c7432ebc 100644 --- a/test/testdata/rewriter/prop_compiled.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/prop_compiled.rb.rewrite-tree.exp @@ -276,7 +276,7 @@ class <>> < (::) end def enum_prop=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -306,7 +306,7 @@ class <>> < (::) end def foreign_<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -314,7 +314,7 @@ class <>> < (::) end def foreign_!<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -344,7 +344,7 @@ class <>> < (::) end def foreign_lazy_<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -352,7 +352,7 @@ class <>> < (::) end def foreign_lazy_!<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -382,7 +382,7 @@ class <>> < (::) end def foreign_proc_<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -390,7 +390,7 @@ class <>> < (::) end def foreign_proc_!<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -420,7 +420,7 @@ class <>> < (::) end def foreign_invalid_<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -428,7 +428,7 @@ class <>> < (::) end def foreign_invalid_!<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -436,7 +436,7 @@ class <>> < (::) end def ifunset<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -455,7 +455,7 @@ class <>> < (::) end def ifunset_nilable<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -507,7 +507,7 @@ class <>> < (::) end def hash_rules=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end .include(::::) diff --git a/test/testdata/rewriter/prop_computed_by.rb.rewrite-tree.exp b/test/testdata/rewriter/prop_computed_by.rb.rewrite-tree.exp index 703144e373..41814233a1 100644 --- a/test/testdata/rewriter/prop_computed_by.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/prop_computed_by.rb.rewrite-tree.exp @@ -7,7 +7,7 @@ class <>> < (::) def num_ok<>(&) begin (.class().compute_num_ok(::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented")), , ::) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end end @@ -26,7 +26,7 @@ class <>> < (::) def missing<>(&) begin (.class().compute_missing(::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented")), , ::) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end end @@ -37,7 +37,7 @@ class <>> < (::) def num_wrong_value<>(&) begin (.class().compute_num_wrong_value(::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented")), , ::) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end end @@ -56,7 +56,7 @@ class <>> < (::) def num_wrong_type<>(&) begin (.class().compute_num_wrong_type(::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented")), , ::) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end end @@ -73,7 +73,7 @@ class <>> < (::) end def not_a_symbol<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -81,7 +81,7 @@ class <>> < (::) end def symbol_in_variable<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -91,7 +91,7 @@ class <>> < (::) def num_unknown_type<>(&) begin (.class().compute_num_unknown_type(::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented")), , ::) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end end diff --git a/test/testdata/rewriter/prop_computed_by_compiled.rb.rewrite-tree.exp b/test/testdata/rewriter/prop_computed_by_compiled.rb.rewrite-tree.exp index f0876c2ed5..1769615933 100644 --- a/test/testdata/rewriter/prop_computed_by_compiled.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/prop_computed_by_compiled.rb.rewrite-tree.exp @@ -7,7 +7,7 @@ class <>> < (::) def num_ok<>(&) begin (.class().compute_num_ok(::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented")), , ::) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end end @@ -26,7 +26,7 @@ class <>> < (::) def missing<>(&) begin (.class().compute_missing(::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented")), , ::) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end end @@ -37,7 +37,7 @@ class <>> < (::) def num_wrong_value<>(&) begin (.class().compute_num_wrong_value(::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented")), , ::) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end end @@ -56,7 +56,7 @@ class <>> < (::) def num_wrong_type<>(&) begin (.class().compute_num_wrong_type(::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented")), , ::) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end end @@ -97,7 +97,7 @@ class <>> < (::) def num_unknown_type<>(&) begin (.class().compute_num_unknown_type(::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented")), , ::) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end end diff --git a/test/testdata/rewriter/prop_foreign.rb.rewrite-tree.exp b/test/testdata/rewriter/prop_foreign.rb.rewrite-tree.exp index ef6e7d4f6f..1dc24e0748 100644 --- a/test/testdata/rewriter/prop_foreign.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/prop_foreign.rb.rewrite-tree.exp @@ -8,7 +8,7 @@ class <>> < (::) end def foreign_lazy<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -16,7 +16,7 @@ class <>> < (::) end def foreign_lazy=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -24,7 +24,7 @@ class <>> < (::) end def foreign_lazy_<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -32,7 +32,7 @@ class <>> < (::) end def foreign_lazy_!<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -40,7 +40,7 @@ class <>> < (::) end def foreign_proc<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -48,7 +48,7 @@ class <>> < (::) end def foreign_proc=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -56,7 +56,7 @@ class <>> < (::) end def foreign_proc_<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -64,7 +64,7 @@ class <>> < (::) end def foreign_proc_!<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -72,7 +72,7 @@ class <>> < (::) end def foreign_invalid<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -80,7 +80,7 @@ class <>> < (::) end def foreign_invalid=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -88,7 +88,7 @@ class <>> < (::) end def foreign_invalid_<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -96,7 +96,7 @@ class <>> < (::) end def foreign_invalid_!<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end .include(::::) diff --git a/test/testdata/rewriter/prop_foreign_compiled.rb.rewrite-tree.exp b/test/testdata/rewriter/prop_foreign_compiled.rb.rewrite-tree.exp index 85cdaacf9e..e0a8d376ab 100644 --- a/test/testdata/rewriter/prop_foreign_compiled.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/prop_foreign_compiled.rb.rewrite-tree.exp @@ -30,7 +30,7 @@ class <>> < (::) end def foreign_lazy_<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -38,7 +38,7 @@ class <>> < (::) end def foreign_lazy_!<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -68,7 +68,7 @@ class <>> < (::) end def foreign_proc_<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -76,7 +76,7 @@ class <>> < (::) end def foreign_proc_!<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -106,7 +106,7 @@ class <>> < (::) end def foreign_invalid_<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -114,7 +114,7 @@ class <>> < (::) end def foreign_invalid_!<>(allow_direct_mutation: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end .include(::::) diff --git a/test/testdata/rewriter/prop_in_module.rb.rewrite-tree.exp b/test/testdata/rewriter/prop_in_module.rb.rewrite-tree.exp index befd5d1548..9968baf91d 100644 --- a/test/testdata/rewriter/prop_in_module.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/prop_in_module.rb.rewrite-tree.exp @@ -5,7 +5,7 @@ class <>> < (::) end def foo<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -13,7 +13,7 @@ class <>> < (::) end def foo=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end .include(::::) diff --git a/test/testdata/rewriter/prop_missing.rb.rewrite-tree.exp b/test/testdata/rewriter/prop_missing.rb.rewrite-tree.exp index e8cd4e4935..94bc82737f 100644 --- a/test/testdata/rewriter/prop_missing.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/prop_missing.rb.rewrite-tree.exp @@ -5,7 +5,7 @@ class <>> < (::) end def foo<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -13,7 +13,7 @@ class <>> < (::) end def foo=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -21,7 +21,7 @@ class <>> < (::) end def bar<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end .prop(:foo, ::, :without_accessors, true) diff --git a/test/testdata/rewriter/prop_updated.rb.rewrite-tree.exp b/test/testdata/rewriter/prop_updated.rb.rewrite-tree.exp index 310841340e..adae0c0dab 100644 --- a/test/testdata/rewriter/prop_updated.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/prop_updated.rb.rewrite-tree.exp @@ -28,19 +28,19 @@ class <>> < (::) end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || - .returns(::T.nilable(::::::)) + .returns(::T.nilable(::)) end def updated<>(&) - .instance_variable_get(:@updated) + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || - .params(:arg0, ::T.nilable(::::::)).returns(::T.nilable(::::::)) + .params(:arg0, ::T.nilable(::)).returns(::T.nilable(::)) end def updated=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end .extend(::::) diff --git a/test/testdata/rewriter/prop_updated_compiled.rb.rewrite-tree.exp b/test/testdata/rewriter/prop_updated_compiled.rb.rewrite-tree.exp index 12870e8323..f5f9b858dd 100644 --- a/test/testdata/rewriter/prop_updated_compiled.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/prop_updated_compiled.rb.rewrite-tree.exp @@ -28,15 +28,18 @@ class <>> < (::) end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || - .returns(::T.nilable(::::::)) + .returns(::T.nilable(::)) end def updated<>(&) - .instance_variable_get(:@updated) + begin + arg2 = .instance_variable_get(:@updated) + .class().decorator().prop_get_logic(, :updated, arg2) + end end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || - .params(:arg0, ::T.nilable(::::::)).returns(::T.nilable(::::::)) + .params(:arg0, ::T.nilable(::)).returns(::T.nilable(::)) end def updated=<>(arg0, &) @@ -52,7 +55,7 @@ class <>> < (::) .updated_prop(:without_accessors, true) - ::Sorbet::Private::Static.keep_def(, :updated, :attr_reader) + ::Sorbet::Private::Static.keep_def(, :updated, :genericPropGetter) ::Sorbet::Private::Static.keep_def(, :updated=, :normal) end diff --git a/test/testdata/rewriter/quoted_symbol_error.rb b/test/testdata/rewriter/quoted_symbol_error.rb new file mode 100644 index 0000000000..010f24aad5 --- /dev/null +++ b/test/testdata/rewriter/quoted_symbol_error.rb @@ -0,0 +1,12 @@ +# typed: true + +class A < T::Struct + prop :'foo', Integer + prop :"bar", Integer + prop :"", Integer + prop :"#{A.name}", Integer +end + +a = A.new(foo: 0, bar: 1) # error: Missing required keyword argument `` for method `A#initialize` +a.foo = '' # error: Assigning a value to `foo` that does not match expected type `Integer` +a.bar = '' # error: Assigning a value to `bar` that does not match expected type `Integer` \ No newline at end of file diff --git a/test/testdata/rewriter/rails/thread_cattr_accessor.rb b/test/testdata/rewriter/rails/thread_cattr_accessor.rb new file mode 100644 index 0000000000..a47d20cd32 --- /dev/null +++ b/test/testdata/rewriter/rails/thread_cattr_accessor.rb @@ -0,0 +1,42 @@ +# typed: strict + +class GoodUsages + extend T::Sig + thread_cattr_accessor :both, :foo + thread_cattr_accessor :no_instance, instance_accessor: false + thread_cattr_accessor :no_instance_reader, instance_reader: false + thread_cattr_accessor :bar, :no_instance_writer, instance_writer: false + + sig {void} + def usages + both + self.both = 1 + + no_instance # error: Method `no_instance` does not exist + self.no_instance = 1 # error: Method `no_instance=` does not exist + + no_instance_reader # error: Method `no_instance_reader` does not exist + self.no_instance_reader= 1 + + no_instance_writer + self.no_instance_writer = 1 # error: Method `no_instance_writer=` does not exist + end + + both + self.both = 1 + + no_instance + self.no_instance = 1 + + no_instance_reader + self.no_instance_reader = 1 + + no_instance_writer + self.no_instance_writer = 1 +end + +class IgnoredUsages + thread_cattr_accessor # error: Method `thread_cattr_accessor` does not exist + thread_cattr_accessor instance_accessor: false # error: Method `thread_cattr_accessor` does not exist + thread_cattr_accessor "foo" # error: Method `thread_cattr_accessor` does not exist +end diff --git a/test/testdata/rewriter/rails/thread_cattr_accessor.rb.rewrite-tree.exp b/test/testdata/rewriter/rails/thread_cattr_accessor.rb.rewrite-tree.exp new file mode 100644 index 0000000000..a52e59af31 --- /dev/null +++ b/test/testdata/rewriter/rails/thread_cattr_accessor.rb.rewrite-tree.exp @@ -0,0 +1,238 @@ +class <>> < (::) + class ::<>> < (::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def both<>(&) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def self.both<>(&) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:arg0, ::T.untyped()).returns(::T.untyped()) + end + + def both=<>(arg0, &) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:arg0, ::T.untyped()).returns(::T.untyped()) + end + + def self.both=<>(arg0, &) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def foo<>(&) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def self.foo<>(&) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:arg0, ::T.untyped()).returns(::T.untyped()) + end + + def foo=<>(arg0, &) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:arg0, ::T.untyped()).returns(::T.untyped()) + end + + def self.foo=<>(arg0, &) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def self.no_instance<>(&) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:arg0, ::T.untyped()).returns(::T.untyped()) + end + + def self.no_instance=<>(arg0, &) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def self.no_instance_reader<>(&) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:arg0, ::T.untyped()).returns(::T.untyped()) + end + + def no_instance_reader=<>(arg0, &) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:arg0, ::T.untyped()).returns(::T.untyped()) + end + + def self.no_instance_reader=<>(arg0, &) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def bar<>(&) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def self.bar<>(&) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:arg0, ::T.untyped()).returns(::T.untyped()) + end + + def self.bar=<>(arg0, &) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def no_instance_writer<>(&) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def self.no_instance_writer<>(&) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:arg0, ::T.untyped()).returns(::T.untyped()) + end + + def self.no_instance_writer=<>(arg0, &) + + end + + ::Sorbet::Private::Static.sig() do || + .void() + end + + def usages<>(&) + begin + .both() + .both=(1) + .no_instance() + .no_instance=(1) + .no_instance_reader() + .no_instance_reader=(1) + .no_instance_writer() + .no_instance_writer=(1) + end + end + + .extend(::::) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .both() + + .both=(1) + + .no_instance() + + .no_instance=(1) + + .no_instance_reader() + + .no_instance_reader=(1) + + .no_instance_writer() + + .no_instance_writer=(1) + end + + class ::<>> < (::) + .thread_cattr_accessor() + + .thread_cattr_accessor(:instance_accessor, false) + + .thread_cattr_accessor("foo") + end +end diff --git a/test/testdata/rewriter/rails/thread_mattr_accessor.rb b/test/testdata/rewriter/rails/thread_mattr_accessor.rb new file mode 100644 index 0000000000..a98f50ef0a --- /dev/null +++ b/test/testdata/rewriter/rails/thread_mattr_accessor.rb @@ -0,0 +1,42 @@ +# typed: strict + +class GoodUsages + extend T::Sig + thread_mattr_accessor :both, :foo + thread_mattr_accessor :no_instance, instance_accessor: false + thread_mattr_accessor :no_instance_reader, instance_reader: false + thread_mattr_accessor :bar, :no_instance_writer, instance_writer: false + + sig {void} + def usages + both + self.both = 1 + + no_instance # error: Method `no_instance` does not exist + self.no_instance = 1 # error: Method `no_instance=` does not exist + + no_instance_reader # error: Method `no_instance_reader` does not exist + self.no_instance_reader= 1 + + no_instance_writer + self.no_instance_writer = 1 # error: Method `no_instance_writer=` does not exist + end + + both + self.both = 1 + + no_instance + self.no_instance = 1 + + no_instance_reader + self.no_instance_reader = 1 + + no_instance_writer + self.no_instance_writer = 1 +end + +class IgnoredUsages + thread_mattr_accessor # error: Method `thread_mattr_accessor` does not exist + thread_mattr_accessor instance_accessor: false # error: Method `thread_mattr_accessor` does not exist + thread_mattr_accessor "foo" # error: Method `thread_mattr_accessor` does not exist +end diff --git a/test/testdata/rewriter/rails/thread_mattr_accessor.rb.rewrite-tree.exp b/test/testdata/rewriter/rails/thread_mattr_accessor.rb.rewrite-tree.exp new file mode 100644 index 0000000000..208976d724 --- /dev/null +++ b/test/testdata/rewriter/rails/thread_mattr_accessor.rb.rewrite-tree.exp @@ -0,0 +1,238 @@ +class <>> < (::) + class ::<>> < (::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def both<>(&) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def self.both<>(&) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:arg0, ::T.untyped()).returns(::T.untyped()) + end + + def both=<>(arg0, &) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:arg0, ::T.untyped()).returns(::T.untyped()) + end + + def self.both=<>(arg0, &) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def foo<>(&) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def self.foo<>(&) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:arg0, ::T.untyped()).returns(::T.untyped()) + end + + def foo=<>(arg0, &) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:arg0, ::T.untyped()).returns(::T.untyped()) + end + + def self.foo=<>(arg0, &) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def self.no_instance<>(&) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:arg0, ::T.untyped()).returns(::T.untyped()) + end + + def self.no_instance=<>(arg0, &) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def self.no_instance_reader<>(&) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:arg0, ::T.untyped()).returns(::T.untyped()) + end + + def no_instance_reader=<>(arg0, &) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:arg0, ::T.untyped()).returns(::T.untyped()) + end + + def self.no_instance_reader=<>(arg0, &) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def bar<>(&) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def self.bar<>(&) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:arg0, ::T.untyped()).returns(::T.untyped()) + end + + def self.bar=<>(arg0, &) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def no_instance_writer<>(&) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def self.no_instance_writer<>(&) + + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:arg0, ::T.untyped()).returns(::T.untyped()) + end + + def self.no_instance_writer=<>(arg0, &) + + end + + ::Sorbet::Private::Static.sig() do || + .void() + end + + def usages<>(&) + begin + .both() + .both=(1) + .no_instance() + .no_instance=(1) + .no_instance_reader() + .no_instance_reader=(1) + .no_instance_writer() + .no_instance_writer=(1) + end + end + + .extend(::::) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .both() + + .both=(1) + + .no_instance() + + .no_instance=(1) + + .no_instance_reader() + + .no_instance_reader=(1) + + .no_instance_writer() + + .no_instance_writer=(1) + end + + class ::<>> < (::) + .thread_mattr_accessor() + + .thread_mattr_accessor(:instance_accessor, false) + + .thread_mattr_accessor("foo") + end +end diff --git a/test/testdata/rewriter/shard_by_merchant_prop.rb.rewrite-tree.exp b/test/testdata/rewriter/shard_by_merchant_prop.rb.rewrite-tree.exp index f7678ddc93..edd5153e34 100644 --- a/test/testdata/rewriter/shard_by_merchant_prop.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/shard_by_merchant_prop.rb.rewrite-tree.exp @@ -26,7 +26,7 @@ class <>> < (::) end def merchant<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end .include(::::) @@ -44,7 +44,7 @@ class <>> < (::) end def merchant<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end .include(::::) diff --git a/test/testdata/rewriter/singleton.rb.rewrite-tree.exp b/test/testdata/rewriter/singleton.rb.rewrite-tree.exp index 09844bf140..75ba5ac250 100644 --- a/test/testdata/rewriter/singleton.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/singleton.rb.rewrite-tree.exp @@ -1,15 +1,5 @@ class <>> < (::) class ::<>> < (::) - ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || - .returns(::T.attached_class()) - end - - def self.instance<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") - end - - - .include(::) end @@ -21,16 +11,6 @@ class <>> < (::) ::.reveal_type(::.instance()) class ::<>> < (::) - ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime, :final) do || - .returns(::T.attached_class()) - end - - def self.instance<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") - end - - - .include(::) .extend(::::) diff --git a/test/testdata/rewriter/strong_command.rb b/test/testdata/rewriter/strong_command.rb new file mode 100644 index 0000000000..936b2585e2 --- /dev/null +++ b/test/testdata/rewriter/strong_command.rb @@ -0,0 +1,15 @@ +# typed: strong + +module Opus + class Command + end +end + +class MyCommand < Opus::Command + extend T::Sig + + sig {void} + def call + nil + end +end diff --git a/test/testdata/rewriter/strong_prop.rb b/test/testdata/rewriter/strong_prop.rb new file mode 100644 index 0000000000..5813bc51d0 --- /dev/null +++ b/test/testdata/rewriter/strong_prop.rb @@ -0,0 +1,11 @@ +# typed: strong + +class Foo < T::Struct + prop :a, Integer +end + +class Bar + include T::Props + + prop :b, Integer +end diff --git a/test/testdata/rewriter/struct.rb.rewrite-tree.exp b/test/testdata/rewriter/struct.rb.rewrite-tree.exp index 0d56d27a98..2535bab4af 100644 --- a/test/testdata/rewriter/struct.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/struct.rb.rewrite-tree.exp @@ -305,9 +305,9 @@ class <>> < (::) .include(::) - ::.().x() + .new().x() - ::.().foo() + .new().foo() end class ::<>> < (::::) @@ -339,13 +339,13 @@ class <>> < (::) .include(::) - ::.().x() + .new().x() - ::.().foo() + .new().foo() - ::.(, 1, 2) + .new(1, 2) - ::.(, :giberish, 1) + .new(:giberish, 1) end ::.new(1, 2) @@ -358,17 +358,17 @@ class <>> < (::) end class ::<>> < (::) - :: = ::.new() + :: = >(::.new(), , ::) - :: = ::.new(:giberish, 1) + :: = >(::.new(:giberish, 1), , ::) - :: = ::.new(:keyword_init, true) + :: = >(::.new(:keyword_init, true), , ::) local = true - :: = ::.new(:keyword_init, local) + :: = >(::.new(:keyword_init, local), , ::) - :: = ::.new(:a, :keyword_init, local) + :: = >(::.new(:a, :keyword_init, local), , ::) end class ::<>> < (::) @@ -450,7 +450,7 @@ class <>> < (::) end - :: = ::::::.new() + :: = >(::::::.new(), , ::::::) ::.new().a() diff --git a/test/testdata/rewriter/struct.rb.symbol-table-raw.exp b/test/testdata/rewriter/struct.rb.symbol-table-raw.exp index d328a9ed28..009822e6fb 100644 --- a/test/testdata/rewriter/struct.rb.symbol-table-raw.exp +++ b/test/testdata/rewriter/struct.rb.symbol-table-raw.exp @@ -15,7 +15,7 @@ class >> < > () method >::># (foo, ) @ Loc {file=test/testdata/rewriter/struct.rb start=42:21 end=42:24} argument foo<> @ Loc {file=test/testdata/rewriter/struct.rb start=42:21 end=42:24} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># (foo, bar, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=42:5 end=42:31} + method >::># : private (foo, bar, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=42:5 end=42:31} argument foo -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=42:21 end=42:24} argument bar -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=42:27 end=42:30} argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} @@ -33,11 +33,11 @@ class >> < > () method > $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=37:1 end=43:4} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} class > < > () @ Loc {file=test/testdata/rewriter/struct.rb start=78:1 end=78:16} - static-field >::> @ Loc {file=test/testdata/rewriter/struct.rb start=79:3 end=79:4} - static-field >::> @ Loc {file=test/testdata/rewriter/struct.rb start=80:3 end=80:4} - static-field >::> @ Loc {file=test/testdata/rewriter/struct.rb start=82:3 end=82:4} - static-field >::> @ Loc {file=test/testdata/rewriter/struct.rb start=85:3 end=85:4} - static-field >::> @ Loc {file=test/testdata/rewriter/struct.rb start=87:3 end=87:4} + static-field >::> -> AppliedType { klass = > targs = [ > = T.untyped ] } @ Loc {file=test/testdata/rewriter/struct.rb start=79:3 end=79:4} + static-field >::> -> AppliedType { klass = > targs = [ > = T.untyped ] } @ Loc {file=test/testdata/rewriter/struct.rb start=80:3 end=80:4} + static-field >::> -> AppliedType { klass = > targs = [ > = T.untyped ] } @ Loc {file=test/testdata/rewriter/struct.rb start=82:3 end=82:4} + static-field >::> -> AppliedType { klass = > targs = [ > = T.untyped ] } @ Loc {file=test/testdata/rewriter/struct.rb start=85:3 end=85:4} + static-field >::> -> AppliedType { klass = > targs = [ > = T.untyped ] } @ Loc {file=test/testdata/rewriter/struct.rb start=87:3 end=87:4} class > $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=78:1 end=78:16} type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=BadUsages) @ Loc {file=test/testdata/rewriter/struct.rb start=78:1 end=78:16} method > $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=78:1 end=88:4} @@ -48,8 +48,7 @@ class >> < > () type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Foo::Struct) @ Loc {file=test/testdata/rewriter/struct.rb start=5:5 end=5:17} method >::> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=5:5 end=6:8} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - class > $1>[>>] < > () @ Loc {file=test/testdata/rewriter/struct.rb start=4:1 end=4:11} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Foo) @ Loc {file=test/testdata/rewriter/struct.rb start=4:1 end=4:11} + class > $1> < > () @ Loc {file=test/testdata/rewriter/struct.rb start=4:1 end=4:11} method > $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=4:1 end=7:4} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} class > < > () @ Loc {file=test/testdata/rewriter/struct.rb start=115:1 end=115:33} @@ -60,14 +59,14 @@ class >> < > () method >::># (a, ) @ Loc {file=test/testdata/rewriter/struct.rb start=117:23 end=117:24} argument a<> @ Loc {file=test/testdata/rewriter/struct.rb start=117:23 end=117:24} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># (a, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=117:3 end=117:25} + method >::># : private (a, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=117:3 end=117:25} argument a -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=117:23 end=117:24} argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} class >::> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=117:3 end=117:25} type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = >::> targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/rewriter/struct.rb start=117:3 end=117:25} method >::> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=117:3 end=117:25} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - static-field >::> @ Loc {file=test/testdata/rewriter/struct.rb start=118:3 end=118:6} + static-field >::> -> Foo::Struct @ Loc {file=test/testdata/rewriter/struct.rb start=118:3 end=118:6} class >::> < > () @ Loc {file=test/testdata/rewriter/struct.rb start=116:3 end=116:23} type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=116:3 end=116:23} method >::># () @ Loc {file=test/testdata/rewriter/struct.rb start=116:21 end=116:22} @@ -75,7 +74,7 @@ class >> < > () method >::># (a, ) @ Loc {file=test/testdata/rewriter/struct.rb start=116:21 end=116:22} argument a<> @ Loc {file=test/testdata/rewriter/struct.rb start=116:21 end=116:22} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># (a, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=116:3 end=116:23} + method >::># : private (a, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=116:3 end=116:23} argument a -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=116:21 end=116:22} argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} class >::> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=116:3 end=116:23} @@ -90,7 +89,7 @@ class >> < > () field ># -> String @ Loc {file=test/testdata/rewriter/struct.rb start=128:10 end=128:11} method ># () -> String @ Loc {file=test/testdata/rewriter/struct.rb start=128:3 end=128:19} argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method ># (b, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=124:1 end=124:37} + method ># : private (b, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=124:1 end=124:37} argument b -> String @ Loc {file=test/testdata/rewriter/struct.rb start=128:3 end=128:19} argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} class > $1>[>>] < >::> $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=124:1 end=124:37} @@ -110,7 +109,7 @@ class >> < > () method >::># (foo=, ) @ Loc {file=test/testdata/rewriter/struct.rb start=46:19 end=46:23} argument foo=<> @ Loc {file=test/testdata/rewriter/struct.rb start=46:19 end=46:23} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># (foo=, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=46:3 end=46:24} + method >::># : private (foo=, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=46:3 end=46:24} argument foo= -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=46:19 end=46:23} argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} class >::> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=46:3 end=46:24} @@ -131,7 +130,7 @@ class >> < > () class > < > () @ Loc {file=test/testdata/rewriter/struct.rb start=49:1 end=49:18} class >::> < > (>) @ Loc {file=test/testdata/rewriter/struct.rb start=60:3 end=68:6} type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=60:3 end=68:6} - method >::># (x, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=60:3 end=68:6} + method >::># : private (x, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=60:3 end=68:6} argument x -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=60:37 end=60:38} argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} method >::># () @ Loc {file=test/testdata/rewriter/struct.rb start=60:37 end=60:38} @@ -146,13 +145,12 @@ class >> < > () module >::> < >::>::>::> () @ Loc {file=test/testdata/rewriter/struct.rb start=50:3 end=50:17} method >::># () @ Loc {file=test/testdata/rewriter/struct.rb start=51:5 end=51:12} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - class >::> $1>[>>] < > () @ Loc {file=test/testdata/rewriter/struct.rb start=50:3 end=50:17} - type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=MixinStruct::MyMixin) @ Loc {file=test/testdata/rewriter/struct.rb start=50:3 end=50:17} + class >::> $1> < > () @ Loc {file=test/testdata/rewriter/struct.rb start=50:3 end=50:17} method >::> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=50:3 end=52:6} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} class >::> < > (>) @ Loc {file=test/testdata/rewriter/struct.rb start=54:3 end=58:6} type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=54:3 end=58:6} - method >::># (x, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=54:3 end=58:6} + method >::># : private (x, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=54:3 end=58:6} argument x -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=54:26 end=54:27} argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} method >::># () @ Loc {file=test/testdata/rewriter/struct.rb start=54:26 end=54:27} @@ -187,7 +185,7 @@ class >> < > () method >::># (foo, ) @ Loc {file=test/testdata/rewriter/struct.rb start=15:21 end=15:24} argument foo<> @ Loc {file=test/testdata/rewriter/struct.rb start=15:21 end=15:24} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># (foo, bar, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=15:5 end=15:31} + method >::># : private (foo, bar, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=15:5 end=15:31} argument foo -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=15:21 end=15:24} argument bar -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=15:27 end=15:30} argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} @@ -207,7 +205,7 @@ class >> < > () method >::># (foo, ) @ Loc {file=test/testdata/rewriter/struct.rb start=16:31 end=16:34} argument foo<> @ Loc {file=test/testdata/rewriter/struct.rb start=16:31 end=16:34} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># (foo, bar, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=16:5 end=16:61} + method >::># : private (foo, bar, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=16:5 end=16:61} argument foo -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=16:31 end=16:34} argument bar -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=16:37 end=16:40} argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} @@ -252,7 +250,7 @@ class >> < > () method >::># (foo, ) @ Loc {file=test/testdata/rewriter/struct.rb start=33:21 end=33:24} argument foo<> @ Loc {file=test/testdata/rewriter/struct.rb start=33:21 end=33:24} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># (foo, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=33:5 end=33:25} + method >::># : private (foo, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=33:5 end=33:25} argument foo -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=33:21 end=33:24} argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} class >::> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=33:5 end=33:25} @@ -266,7 +264,7 @@ class >> < > () method >::># (foo, ) @ Loc {file=test/testdata/rewriter/struct.rb start=34:21 end=34:24} argument foo<> @ Loc {file=test/testdata/rewriter/struct.rb start=34:21 end=34:24} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># (foo, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=34:5 end=34:25} + method >::># : private (foo, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=34:5 end=34:25} argument foo -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=34:21 end=34:24} argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} class >::> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=34:5 end=34:25} diff --git a/test/testdata/rewriter/t_enum_snapshot.rb.cfg-text.exp b/test/testdata/rewriter/t_enum_snapshot.rb.cfg-text.exp index 657e5c3488..029ac65cb4 100644 --- a/test/testdata/rewriter/t_enum_snapshot.rb.cfg-text.exp +++ b/test/testdata/rewriter/t_enum_snapshot.rb.cfg-text.exp @@ -39,9 +39,9 @@ bb1[rubyRegionId=0, firstDead=-1](): method ::# { bb0[rubyRegionId=0, firstDead=-1](): - $30: MyEnum::X = alias - $49: MyEnum::Y = alias - $69: MyEnum::Z = alias + $29: MyEnum::X = alias + $48: MyEnum::Y = alias + $68: MyEnum::Z = alias : T.class_of(MyEnum) = cast(: NilClass, T.class_of(MyEnum)); $6: T.class_of(T::Helpers) = alias $3: T.class_of(MyEnum) = : T.class_of(MyEnum).extend($6: T.class_of(T::Helpers)) @@ -59,7 +59,7 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb5(rubyRegionId=1) -bb2[rubyRegionId=1, firstDead=-1](: T.class_of(MyEnum), $13: Sorbet::Private::Static::Void, $14: T.class_of(MyEnum), $30: MyEnum::X, $49: MyEnum::Y, $69: MyEnum::Z): +bb2[rubyRegionId=1, firstDead=-1](: T.class_of(MyEnum), $13: Sorbet::Private::Static::Void, $14: T.class_of(MyEnum), $29: MyEnum::X, $48: MyEnum::Y, $68: MyEnum::Z): # outerLoops: 1 -> (NilClass ? bb5 : bb3) @@ -72,48 +72,48 @@ bb3[rubyRegionId=0, firstDead=2]($13: Sorbet::Private::Stat # backedges # - bb2(rubyRegionId=1) -bb5[rubyRegionId=1, firstDead=40](: T.class_of(MyEnum), $13: Sorbet::Private::Static::Void, $14: T.class_of(MyEnum), $30: MyEnum::X, $49: MyEnum::Y, $69: MyEnum::Z): +bb5[rubyRegionId=1, firstDead=40](: T.class_of(MyEnum), $13: Sorbet::Private::Static::Void, $14: T.class_of(MyEnum), $29: MyEnum::X, $48: MyEnum::Y, $68: MyEnum::Z): # outerLoops: 1 : T.class_of(MyEnum) = loadSelf(enums) - $21: T.class_of(Sorbet::Private::Static) = alias - $23: T.class_of(MyEnum::X) = alias - $19: Sorbet::Private::Static::Void = $21: T.class_of(Sorbet::Private::Static).keep_for_ide($23: T.class_of(MyEnum::X)) - $26: T.class_of(Sorbet::Private::Static) = alias - $28: T.class_of(MyEnum) = alias - $24: Sorbet::Private::Static::Void = $26: T.class_of(Sorbet::Private::Static).keep_for_ide($28: T.class_of(MyEnum)) - $32: T.class_of(MyEnum::X) = alias - keep_for_ide$31: T.class_of(MyEnum::X) = $32 - keep_for_ide$31: T.untyped = keep_for_ide$31 - $35: T.class_of(MyEnum::X) = alias - $33: MyEnum::X = $35: T.class_of(MyEnum::X).new() - $30: MyEnum::X = $33 - $40: T.class_of(Sorbet::Private::Static) = alias - $42: T.class_of(MyEnum::Y) = alias - $38: Sorbet::Private::Static::Void = $40: T.class_of(Sorbet::Private::Static).keep_for_ide($42: T.class_of(MyEnum::Y)) - $45: T.class_of(Sorbet::Private::Static) = alias - $47: T.class_of(MyEnum) = alias - $43: Sorbet::Private::Static::Void = $45: T.class_of(Sorbet::Private::Static).keep_for_ide($47: T.class_of(MyEnum)) - $51: T.class_of(MyEnum::Y) = alias - keep_for_ide$50: T.class_of(MyEnum::Y) = $51 - keep_for_ide$50: T.untyped = keep_for_ide$50 - $54: T.class_of(MyEnum::Y) = alias - $55: String("y") = "y" - $52: MyEnum::Y = $54: T.class_of(MyEnum::Y).new($55: String("y")) - $49: MyEnum::Y = $52 - $60: T.class_of(Sorbet::Private::Static) = alias - $62: T.class_of(MyEnum::Z) = alias - $58: Sorbet::Private::Static::Void = $60: T.class_of(Sorbet::Private::Static).keep_for_ide($62: T.class_of(MyEnum::Z)) - $65: T.class_of(Sorbet::Private::Static) = alias - $67: T.class_of(MyEnum) = alias - $63: Sorbet::Private::Static::Void = $65: T.class_of(Sorbet::Private::Static).keep_for_ide($67: T.class_of(MyEnum)) - $71: T.class_of(MyEnum::Z) = alias - keep_for_ide$70: T.class_of(MyEnum::Z) = $71 - keep_for_ide$70: T.untyped = keep_for_ide$70 - $74: T.class_of(MyEnum::Z) = alias - $72: MyEnum::Z = $74: T.class_of(MyEnum::Z).new() - $69: MyEnum::Z = $72 - $16: NilClass = nil - $75: T.noreturn = blockreturn $16: NilClass + $20: T.class_of(Sorbet::Private::Static) = alias + $22: T.class_of(MyEnum::X) = alias + $18: Sorbet::Private::Static::Void = $20: T.class_of(Sorbet::Private::Static).keep_for_ide($22: T.class_of(MyEnum::X)) + $25: T.class_of(Sorbet::Private::Static) = alias + $27: T.class_of(MyEnum) = alias + $23: Sorbet::Private::Static::Void = $25: T.class_of(Sorbet::Private::Static).keep_for_ide($27: T.class_of(MyEnum)) + $31: T.class_of(MyEnum::X) = alias + keep_for_ide$30: T.class_of(MyEnum::X) = $31 + keep_for_ide$30: T.untyped = keep_for_ide$30 + $34: T.class_of(MyEnum::X) = alias + $32: MyEnum::X = $34: T.class_of(MyEnum::X).new() + $29: MyEnum::X = $32 + $39: T.class_of(Sorbet::Private::Static) = alias + $41: T.class_of(MyEnum::Y) = alias + $37: Sorbet::Private::Static::Void = $39: T.class_of(Sorbet::Private::Static).keep_for_ide($41: T.class_of(MyEnum::Y)) + $44: T.class_of(Sorbet::Private::Static) = alias + $46: T.class_of(MyEnum) = alias + $42: Sorbet::Private::Static::Void = $44: T.class_of(Sorbet::Private::Static).keep_for_ide($46: T.class_of(MyEnum)) + $50: T.class_of(MyEnum::Y) = alias + keep_for_ide$49: T.class_of(MyEnum::Y) = $50 + keep_for_ide$49: T.untyped = keep_for_ide$49 + $53: T.class_of(MyEnum::Y) = alias + $54: String("y") = "y" + $51: MyEnum::Y = $53: T.class_of(MyEnum::Y).new($54: String("y")) + $48: MyEnum::Y = $51 + $59: T.class_of(Sorbet::Private::Static) = alias + $61: T.class_of(MyEnum::Z) = alias + $57: Sorbet::Private::Static::Void = $59: T.class_of(Sorbet::Private::Static).keep_for_ide($61: T.class_of(MyEnum::Z)) + $64: T.class_of(Sorbet::Private::Static) = alias + $66: T.class_of(MyEnum) = alias + $62: Sorbet::Private::Static::Void = $64: T.class_of(Sorbet::Private::Static).keep_for_ide($66: T.class_of(MyEnum)) + $70: T.class_of(MyEnum::Z) = alias + keep_for_ide$69: T.class_of(MyEnum::Z) = $70 + keep_for_ide$69: T.untyped = keep_for_ide$69 + $73: T.class_of(MyEnum::Z) = alias + $71: MyEnum::Z = $73: T.class_of(MyEnum::Z).new() + $68: MyEnum::Z = $71 + $15: NilClass = nil + $74: T.noreturn = blockreturn $15: NilClass -> bb2 } @@ -163,8 +163,8 @@ bb1[rubyRegionId=0, firstDead=-1](): method ::# { bb0[rubyRegionId=0, firstDead=-1](): - $9: T.untyped = alias - $16: NotAnEnum = alias + $8: T.untyped = alias + $13: NotAnEnum = alias : T.class_of(NotAnEnum) = cast(: NilClass, T.class_of(NotAnEnum)); $4: Sorbet::Private::Static::Void = : T.class_of(NotAnEnum).enums() $5: T.class_of(NotAnEnum) = @@ -178,7 +178,7 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb5(rubyRegionId=1) -bb2[rubyRegionId=1, firstDead=-1](: T.class_of(NotAnEnum), $4: Sorbet::Private::Static::Void, $5: T.class_of(NotAnEnum), $9: T.untyped, $16: NotAnEnum): +bb2[rubyRegionId=1, firstDead=-1](: T.class_of(NotAnEnum), $4: Sorbet::Private::Static::Void, $5: T.class_of(NotAnEnum), $8: T.untyped, $13: NotAnEnum): # outerLoops: 1 -> (NilClass ? bb5 : bb3) @@ -191,20 +191,18 @@ bb3[rubyRegionId=0, firstDead=2]($4: Sorbet::Private::Stati # backedges # - bb2(rubyRegionId=1) -bb5[rubyRegionId=1, firstDead=12](: T.class_of(NotAnEnum), $4: Sorbet::Private::Static::Void, $5: T.class_of(NotAnEnum), $9: T.untyped, $16: NotAnEnum): +bb5[rubyRegionId=1, firstDead=10](: T.class_of(NotAnEnum), $4: Sorbet::Private::Static::Void, $5: T.class_of(NotAnEnum), $8: T.untyped, $13: NotAnEnum): # outerLoops: 1 : T.class_of(NotAnEnum) = loadSelf(enums) - $11: T.class_of() = alias > - $14: T.class_of() = alias > - $12: T.attached_class (of NotAnEnum) = $14: T.class_of().(: T.class_of(NotAnEnum)) - $9: T.attached_class (of NotAnEnum) = $11: T.class_of().($12: T.attached_class (of NotAnEnum)) - keep_for_ide$17: T.class_of(NotAnEnum) = - keep_for_ide$17: T.untyped = keep_for_ide$17 - $20: T.class_of() = alias > - $18: T.attached_class (of NotAnEnum) = $20: T.class_of().(: T.class_of(NotAnEnum)) - $16: NotAnEnum = cast($18: T.attached_class (of NotAnEnum), NotAnEnum); - $7: NotAnEnum = $16 - $22: T.noreturn = blockreturn $7: NotAnEnum + $10: T.class_of() = alias > + $11: T.attached_class (of NotAnEnum) = : T.class_of(NotAnEnum).new() + $8: T.attached_class (of NotAnEnum) = $10: T.class_of().($11: T.attached_class (of NotAnEnum)) + keep_for_ide$14: T.class_of(NotAnEnum) = + keep_for_ide$14: T.untyped = keep_for_ide$14 + $15: T.attached_class (of NotAnEnum) = : T.class_of(NotAnEnum).new() + $13: NotAnEnum = cast($15: T.attached_class (of NotAnEnum), NotAnEnum); + $6: NotAnEnum = $13 + $17: T.noreturn = blockreturn $6: NotAnEnum -> bb2 } @@ -226,9 +224,9 @@ bb1[rubyRegionId=0, firstDead=-1](): method ::# { bb0[rubyRegionId=0, firstDead=-1](): - $30: EnumsDoEnum::X = alias - $49: EnumsDoEnum::Y = alias - $69: EnumsDoEnum::Z = alias + $29: EnumsDoEnum::X = alias + $48: EnumsDoEnum::Y = alias + $68: EnumsDoEnum::Z = alias : T.class_of(EnumsDoEnum) = cast(: NilClass, T.class_of(EnumsDoEnum)); $6: T.class_of(T::Helpers) = alias $3: T.class_of(EnumsDoEnum) = : T.class_of(EnumsDoEnum).extend($6: T.class_of(T::Helpers)) @@ -246,7 +244,7 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb5(rubyRegionId=1) -bb2[rubyRegionId=1, firstDead=-1](: T.class_of(EnumsDoEnum), $13: Sorbet::Private::Static::Void, $14: T.class_of(EnumsDoEnum), $30: EnumsDoEnum::X, $49: EnumsDoEnum::Y, $69: EnumsDoEnum::Z): +bb2[rubyRegionId=1, firstDead=-1](: T.class_of(EnumsDoEnum), $13: Sorbet::Private::Static::Void, $14: T.class_of(EnumsDoEnum), $29: EnumsDoEnum::X, $48: EnumsDoEnum::Y, $68: EnumsDoEnum::Z): # outerLoops: 1 -> (NilClass ? bb5 : bb3) @@ -259,48 +257,48 @@ bb3[rubyRegionId=0, firstDead=2]($13: Sorbet::Private::Stat # backedges # - bb2(rubyRegionId=1) -bb5[rubyRegionId=1, firstDead=40](: T.class_of(EnumsDoEnum), $13: Sorbet::Private::Static::Void, $14: T.class_of(EnumsDoEnum), $30: EnumsDoEnum::X, $49: EnumsDoEnum::Y, $69: EnumsDoEnum::Z): +bb5[rubyRegionId=1, firstDead=40](: T.class_of(EnumsDoEnum), $13: Sorbet::Private::Static::Void, $14: T.class_of(EnumsDoEnum), $29: EnumsDoEnum::X, $48: EnumsDoEnum::Y, $68: EnumsDoEnum::Z): # outerLoops: 1 : T.class_of(EnumsDoEnum) = loadSelf(enums) - $21: T.class_of(Sorbet::Private::Static) = alias - $23: T.class_of(EnumsDoEnum::X) = alias - $19: Sorbet::Private::Static::Void = $21: T.class_of(Sorbet::Private::Static).keep_for_ide($23: T.class_of(EnumsDoEnum::X)) - $26: T.class_of(Sorbet::Private::Static) = alias - $28: T.class_of(EnumsDoEnum) = alias - $24: Sorbet::Private::Static::Void = $26: T.class_of(Sorbet::Private::Static).keep_for_ide($28: T.class_of(EnumsDoEnum)) - $32: T.class_of(EnumsDoEnum::X) = alias - keep_for_ide$31: T.class_of(EnumsDoEnum::X) = $32 - keep_for_ide$31: T.untyped = keep_for_ide$31 - $35: T.class_of(EnumsDoEnum::X) = alias - $33: EnumsDoEnum::X = $35: T.class_of(EnumsDoEnum::X).new() - $30: EnumsDoEnum::X = $33 - $40: T.class_of(Sorbet::Private::Static) = alias - $42: T.class_of(EnumsDoEnum::Y) = alias - $38: Sorbet::Private::Static::Void = $40: T.class_of(Sorbet::Private::Static).keep_for_ide($42: T.class_of(EnumsDoEnum::Y)) - $45: T.class_of(Sorbet::Private::Static) = alias - $47: T.class_of(EnumsDoEnum) = alias - $43: Sorbet::Private::Static::Void = $45: T.class_of(Sorbet::Private::Static).keep_for_ide($47: T.class_of(EnumsDoEnum)) - $51: T.class_of(EnumsDoEnum::Y) = alias - keep_for_ide$50: T.class_of(EnumsDoEnum::Y) = $51 - keep_for_ide$50: T.untyped = keep_for_ide$50 - $54: T.class_of(EnumsDoEnum::Y) = alias - $55: String("y") = "y" - $52: EnumsDoEnum::Y = $54: T.class_of(EnumsDoEnum::Y).new($55: String("y")) - $49: EnumsDoEnum::Y = $52 - $60: T.class_of(Sorbet::Private::Static) = alias - $62: T.class_of(EnumsDoEnum::Z) = alias - $58: Sorbet::Private::Static::Void = $60: T.class_of(Sorbet::Private::Static).keep_for_ide($62: T.class_of(EnumsDoEnum::Z)) - $65: T.class_of(Sorbet::Private::Static) = alias - $67: T.class_of(EnumsDoEnum) = alias - $63: Sorbet::Private::Static::Void = $65: T.class_of(Sorbet::Private::Static).keep_for_ide($67: T.class_of(EnumsDoEnum)) - $71: T.class_of(EnumsDoEnum::Z) = alias - keep_for_ide$70: T.class_of(EnumsDoEnum::Z) = $71 - keep_for_ide$70: T.untyped = keep_for_ide$70 - $74: T.class_of(EnumsDoEnum::Z) = alias - $72: EnumsDoEnum::Z = $74: T.class_of(EnumsDoEnum::Z).new() - $69: EnumsDoEnum::Z = $72 - $16: NilClass = nil - $75: T.noreturn = blockreturn $16: NilClass + $20: T.class_of(Sorbet::Private::Static) = alias + $22: T.class_of(EnumsDoEnum::X) = alias + $18: Sorbet::Private::Static::Void = $20: T.class_of(Sorbet::Private::Static).keep_for_ide($22: T.class_of(EnumsDoEnum::X)) + $25: T.class_of(Sorbet::Private::Static) = alias + $27: T.class_of(EnumsDoEnum) = alias + $23: Sorbet::Private::Static::Void = $25: T.class_of(Sorbet::Private::Static).keep_for_ide($27: T.class_of(EnumsDoEnum)) + $31: T.class_of(EnumsDoEnum::X) = alias + keep_for_ide$30: T.class_of(EnumsDoEnum::X) = $31 + keep_for_ide$30: T.untyped = keep_for_ide$30 + $34: T.class_of(EnumsDoEnum::X) = alias + $32: EnumsDoEnum::X = $34: T.class_of(EnumsDoEnum::X).new() + $29: EnumsDoEnum::X = $32 + $39: T.class_of(Sorbet::Private::Static) = alias + $41: T.class_of(EnumsDoEnum::Y) = alias + $37: Sorbet::Private::Static::Void = $39: T.class_of(Sorbet::Private::Static).keep_for_ide($41: T.class_of(EnumsDoEnum::Y)) + $44: T.class_of(Sorbet::Private::Static) = alias + $46: T.class_of(EnumsDoEnum) = alias + $42: Sorbet::Private::Static::Void = $44: T.class_of(Sorbet::Private::Static).keep_for_ide($46: T.class_of(EnumsDoEnum)) + $50: T.class_of(EnumsDoEnum::Y) = alias + keep_for_ide$49: T.class_of(EnumsDoEnum::Y) = $50 + keep_for_ide$49: T.untyped = keep_for_ide$49 + $53: T.class_of(EnumsDoEnum::Y) = alias + $54: String("y") = "y" + $51: EnumsDoEnum::Y = $53: T.class_of(EnumsDoEnum::Y).new($54: String("y")) + $48: EnumsDoEnum::Y = $51 + $59: T.class_of(Sorbet::Private::Static) = alias + $61: T.class_of(EnumsDoEnum::Z) = alias + $57: Sorbet::Private::Static::Void = $59: T.class_of(Sorbet::Private::Static).keep_for_ide($61: T.class_of(EnumsDoEnum::Z)) + $64: T.class_of(Sorbet::Private::Static) = alias + $66: T.class_of(EnumsDoEnum) = alias + $62: Sorbet::Private::Static::Void = $64: T.class_of(Sorbet::Private::Static).keep_for_ide($66: T.class_of(EnumsDoEnum)) + $70: T.class_of(EnumsDoEnum::Z) = alias + keep_for_ide$69: T.class_of(EnumsDoEnum::Z) = $70 + keep_for_ide$69: T.untyped = keep_for_ide$69 + $73: T.class_of(EnumsDoEnum::Z) = alias + $71: EnumsDoEnum::Z = $73: T.class_of(EnumsDoEnum::Z).new() + $68: EnumsDoEnum::Z = $71 + $15: NilClass = nil + $74: T.noreturn = blockreturn $15: NilClass -> bb2 } @@ -352,11 +350,11 @@ method ::# { bb0[rubyRegionId=0, firstDead=-1](): $24: BadConsts::Before = alias $31: Integer = alias - $51: BadConsts::Inside = alias - $58: Integer = alias - $73: BadConsts::After = alias - $80: Integer = alias - $82: Integer = alias + $50: BadConsts::Inside = alias + $57: Integer = alias + $72: BadConsts::After = alias + $79: Integer = alias + $81: Integer = alias : T.class_of(BadConsts) = cast(: NilClass, T.class_of(BadConsts)); $6: T.class_of(T::Helpers) = alias $3: T.class_of(BadConsts) = : T.class_of(BadConsts).extend($6: T.class_of(T::Helpers)) @@ -387,55 +385,55 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb5(rubyRegionId=1) -bb2[rubyRegionId=1, firstDead=-1](: T.class_of(BadConsts), $34: Sorbet::Private::Static::Void, $35: T.class_of(BadConsts), $51: BadConsts::Inside, $58: Integer, $73: BadConsts::After, $80: Integer, $82: Integer): +bb2[rubyRegionId=1, firstDead=-1](: T.class_of(BadConsts), $34: Sorbet::Private::Static::Void, $35: T.class_of(BadConsts), $50: BadConsts::Inside, $57: Integer, $72: BadConsts::After, $79: Integer, $81: Integer): # outerLoops: 1 -> (NilClass ? bb5 : bb3) # backedges # - bb2(rubyRegionId=1) -bb3[rubyRegionId=0, firstDead=20]($34: Sorbet::Private::Static::Void, $35: T.class_of(BadConsts), $73: BadConsts::After, $80: Integer, $82: Integer): +bb3[rubyRegionId=0, firstDead=20]($34: Sorbet::Private::Static::Void, $35: T.class_of(BadConsts), $72: BadConsts::After, $79: Integer, $81: Integer): $32: Sorbet::Private::Static::Void = Solve<$34, enums> - $64: T.class_of(Sorbet::Private::Static) = alias - $66: T.class_of(BadConsts::After) = alias - $62: Sorbet::Private::Static::Void = $64: T.class_of(Sorbet::Private::Static).keep_for_ide($66: T.class_of(BadConsts::After)) - $69: T.class_of(Sorbet::Private::Static) = alias - $71: T.class_of(BadConsts) = alias - $67: Sorbet::Private::Static::Void = $69: T.class_of(Sorbet::Private::Static).keep_for_ide($71: T.class_of(BadConsts)) - $75: T.class_of(BadConsts::After) = alias - keep_for_ide$74: T.class_of(BadConsts::After) = $75 - keep_for_ide$74: T.untyped = keep_for_ide$74 - $78: T.class_of(BadConsts::After) = alias - $76: BadConsts::After = $78: T.class_of(BadConsts::After).new() - $73: BadConsts::After = $76 - $80: Integer(3) = 3 - $84: T.class_of(Integer) = alias - keep_for_ide$83: T.class_of(Integer) = $84 - keep_for_ide$83: T.untyped = keep_for_ide$83 - $85: Integer(1) = 1 - $82: Integer = cast($85: Integer(1), Integer); + $63: T.class_of(Sorbet::Private::Static) = alias + $65: T.class_of(BadConsts::After) = alias + $61: Sorbet::Private::Static::Void = $63: T.class_of(Sorbet::Private::Static).keep_for_ide($65: T.class_of(BadConsts::After)) + $68: T.class_of(Sorbet::Private::Static) = alias + $70: T.class_of(BadConsts) = alias + $66: Sorbet::Private::Static::Void = $68: T.class_of(Sorbet::Private::Static).keep_for_ide($70: T.class_of(BadConsts)) + $74: T.class_of(BadConsts::After) = alias + keep_for_ide$73: T.class_of(BadConsts::After) = $74 + keep_for_ide$73: T.untyped = keep_for_ide$73 + $77: T.class_of(BadConsts::After) = alias + $75: BadConsts::After = $77: T.class_of(BadConsts::After).new() + $72: BadConsts::After = $75 + $79: Integer(3) = 3 + $83: T.class_of(Integer) = alias + keep_for_ide$82: T.class_of(Integer) = $83 + keep_for_ide$82: T.untyped = keep_for_ide$82 + $84: Integer(1) = 1 + $81: Integer = cast($84: Integer(1), Integer); : T.noreturn = return $2: NilClass -> bb1 # backedges # - bb2(rubyRegionId=1) -bb5[rubyRegionId=1, firstDead=16](: T.class_of(BadConsts), $34: Sorbet::Private::Static::Void, $35: T.class_of(BadConsts), $51: BadConsts::Inside, $58: Integer, $73: BadConsts::After, $80: Integer, $82: Integer): +bb5[rubyRegionId=1, firstDead=16](: T.class_of(BadConsts), $34: Sorbet::Private::Static::Void, $35: T.class_of(BadConsts), $50: BadConsts::Inside, $57: Integer, $72: BadConsts::After, $79: Integer, $81: Integer): # outerLoops: 1 : T.class_of(BadConsts) = loadSelf(enums) - $42: T.class_of(Sorbet::Private::Static) = alias - $44: T.class_of(BadConsts::Inside) = alias - $40: Sorbet::Private::Static::Void = $42: T.class_of(Sorbet::Private::Static).keep_for_ide($44: T.class_of(BadConsts::Inside)) - $47: T.class_of(Sorbet::Private::Static) = alias - $49: T.class_of(BadConsts) = alias - $45: Sorbet::Private::Static::Void = $47: T.class_of(Sorbet::Private::Static).keep_for_ide($49: T.class_of(BadConsts)) - $53: T.class_of(BadConsts::Inside) = alias - keep_for_ide$52: T.class_of(BadConsts::Inside) = $53 - keep_for_ide$52: T.untyped = keep_for_ide$52 - $56: T.class_of(BadConsts::Inside) = alias - $54: BadConsts::Inside = $56: T.class_of(BadConsts::Inside).new() - $51: BadConsts::Inside = $54 - $58: Integer(2) = 2 - $37: NilClass = nil - $59: T.noreturn = blockreturn $37: NilClass + $41: T.class_of(Sorbet::Private::Static) = alias + $43: T.class_of(BadConsts::Inside) = alias + $39: Sorbet::Private::Static::Void = $41: T.class_of(Sorbet::Private::Static).keep_for_ide($43: T.class_of(BadConsts::Inside)) + $46: T.class_of(Sorbet::Private::Static) = alias + $48: T.class_of(BadConsts) = alias + $44: Sorbet::Private::Static::Void = $46: T.class_of(Sorbet::Private::Static).keep_for_ide($48: T.class_of(BadConsts)) + $52: T.class_of(BadConsts::Inside) = alias + keep_for_ide$51: T.class_of(BadConsts::Inside) = $52 + keep_for_ide$51: T.untyped = keep_for_ide$51 + $55: T.class_of(BadConsts::Inside) = alias + $53: BadConsts::Inside = $55: T.class_of(BadConsts::Inside).new() + $50: BadConsts::Inside = $53 + $57: Integer(2) = 2 + $36: NilClass = nil + $58: T.noreturn = blockreturn $36: NilClass -> bb2 } diff --git a/test/testdata/rewriter/t_enum_snapshot.rb.rewrite-tree.exp b/test/testdata/rewriter/t_enum_snapshot.rb.rewrite-tree.exp index 8b4df32ab3..7d6edfbb32 100644 --- a/test/testdata/rewriter/t_enum_snapshot.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/t_enum_snapshot.rb.rewrite-tree.exp @@ -25,8 +25,8 @@ class <>> < (::) class ::<>> < (::) .enums() do || begin - :: = ::.() - :: = (::.(), , ) + :: = .new() + :: = (.new(), , ) end end end diff --git a/test/testdata/rewriter/t_struct/inexact.rb.rewrite-tree.exp b/test/testdata/rewriter/t_struct/inexact.rb.rewrite-tree.exp index 9c77d36c62..128fe1b603 100644 --- a/test/testdata/rewriter/t_struct/inexact.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/t_struct/inexact.rb.rewrite-tree.exp @@ -57,7 +57,7 @@ class <>> < (::) end def qux<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -65,7 +65,7 @@ class <>> < (::) end def qux=<>(arg0, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end .prop(:qux, ::, :without_accessors, true) diff --git a/test/testdata/todo/class_of.rb b/test/testdata/todo/class_of.rb deleted file mode 100644 index 49858015d2..0000000000 --- a/test/testdata/todo/class_of.rb +++ /dev/null @@ -1,21 +0,0 @@ -# typed: true - -module Mixin; end -class Foo - include Mixin; -end - -class Main - extend T::Sig - - sig {params(a: T.class_of(Mixin)).returns(T.class_of(Mixin))} - def bar(a) - a - end - - def main - bar(Mixin) - bar(Foo) # error: Expected `T.class_of(Mixin)` but found `T.class_of(Foo)` for argument `a` - # TODO: RUBYPLAT-504 - end -end diff --git a/test/whitequark/test_forwarded_argument_with_kwrestarg_0.parse-tree-whitequark.exp b/test/whitequark/test_forwarded_argument_with_kwrestarg_0.parse-tree-whitequark.exp new file mode 100644 index 0000000000..07980292d4 --- /dev/null +++ b/test/whitequark/test_forwarded_argument_with_kwrestarg_0.parse-tree-whitequark.exp @@ -0,0 +1,8 @@ +s(:def, :foo, + s(:args, + s(:arg, :argument), + s(:kwrestarg, :**)), + s(:send, nil, :bar, + s(:lvar, :argument), + s(:hash, + s(:forwarded_kwrestarg)))) diff --git a/test/whitequark/test_forwarded_argument_with_kwrestarg_0.rb b/test/whitequark/test_forwarded_argument_with_kwrestarg_0.rb new file mode 100644 index 0000000000..636df4b17c --- /dev/null +++ b/test/whitequark/test_forwarded_argument_with_kwrestarg_0.rb @@ -0,0 +1,3 @@ +# typed: true + +def foo(argument, **); bar(argument, **); end diff --git a/test/whitequark/test_forwarded_argument_with_kwrestarg_1.rb b/test/whitequark/test_forwarded_argument_with_kwrestarg_1.rb new file mode 100644 index 0000000000..fc08a92614 --- /dev/null +++ b/test/whitequark/test_forwarded_argument_with_kwrestarg_1.rb @@ -0,0 +1,3 @@ +# typed: true + +def foo; bar(argument, **); end # error: no anonymous keyword rest parameter diff --git a/test/whitequark/test_forwarded_argument_with_restarg_0.parse-tree-whitequark.exp b/test/whitequark/test_forwarded_argument_with_restarg_0.parse-tree-whitequark.exp new file mode 100644 index 0000000000..70cd45f86e --- /dev/null +++ b/test/whitequark/test_forwarded_argument_with_restarg_0.parse-tree-whitequark.exp @@ -0,0 +1,7 @@ +s(:def, :foo, + s(:args, + s(:arg, :argument), + s(:restarg, :*)), + s(:send, nil, :bar, + s(:lvar, :argument), + s(:forwarded_restarg))) diff --git a/test/whitequark/test_forwarded_argument_with_restarg_0.rb b/test/whitequark/test_forwarded_argument_with_restarg_0.rb new file mode 100644 index 0000000000..ec16b8b440 --- /dev/null +++ b/test/whitequark/test_forwarded_argument_with_restarg_0.rb @@ -0,0 +1,3 @@ +# typed: true + +def foo(argument, *); bar(argument, *); end diff --git a/test/whitequark/test_forwarded_argument_with_restarg_1.rb b/test/whitequark/test_forwarded_argument_with_restarg_1.rb new file mode 100644 index 0000000000..59934502ca --- /dev/null +++ b/test/whitequark/test_forwarded_argument_with_restarg_1.rb @@ -0,0 +1,3 @@ +# typed: true + +def foo; bar(argument, *); end # error: no anonymous rest parameter diff --git a/test/whitequark/test_forwarded_kwrestarg_0.parse-tree-whitequark.exp b/test/whitequark/test_forwarded_kwrestarg_0.parse-tree-whitequark.exp new file mode 100644 index 0000000000..5869bde2bc --- /dev/null +++ b/test/whitequark/test_forwarded_kwrestarg_0.parse-tree-whitequark.exp @@ -0,0 +1,6 @@ +s(:def, :foo, + s(:args, + s(:kwrestarg, :**)), + s(:send, nil, :bar, + s(:hash, + s(:forwarded_kwrestarg)))) diff --git a/test/whitequark/test_forwarded_kwrestarg_0.rb b/test/whitequark/test_forwarded_kwrestarg_0.rb new file mode 100644 index 0000000000..188cf4378b --- /dev/null +++ b/test/whitequark/test_forwarded_kwrestarg_0.rb @@ -0,0 +1,3 @@ +# typed: true + +def foo(**); bar(**); end diff --git a/test/whitequark/test_forwarded_kwrestarg_1.rb b/test/whitequark/test_forwarded_kwrestarg_1.rb new file mode 100644 index 0000000000..c2bc26421f --- /dev/null +++ b/test/whitequark/test_forwarded_kwrestarg_1.rb @@ -0,0 +1,3 @@ +# typed: true + +def foo; bar(**); end # error: no anonymous keyword rest parameter diff --git a/test/whitequark/test_forwarded_restarg_0.parse-tree-whitequark.exp b/test/whitequark/test_forwarded_restarg_0.parse-tree-whitequark.exp new file mode 100644 index 0000000000..ad399ed4bf --- /dev/null +++ b/test/whitequark/test_forwarded_restarg_0.parse-tree-whitequark.exp @@ -0,0 +1,5 @@ +s(:def, :foo, + s(:args, + s(:restarg, :*)), + s(:send, nil, :bar, + s(:forwarded_restarg))) diff --git a/test/whitequark/test_forwarded_restarg_0.rb b/test/whitequark/test_forwarded_restarg_0.rb new file mode 100644 index 0000000000..1796cd55c9 --- /dev/null +++ b/test/whitequark/test_forwarded_restarg_0.rb @@ -0,0 +1,3 @@ +# typed: true + +def foo(*); bar(*); end diff --git a/test/whitequark/test_forwarded_restarg_1.rb b/test/whitequark/test_forwarded_restarg_1.rb new file mode 100644 index 0000000000..1a241a6c59 --- /dev/null +++ b/test/whitequark/test_forwarded_restarg_1.rb @@ -0,0 +1,3 @@ +# typed: true + +def foo; bar(*); end # error: no anonymous rest parameter diff --git a/test/whitequark/test_pattern_matching_single_line_allowed_omission_of_parentheses_4.parse-tree-whitequark.exp b/test/whitequark/test_pattern_matching_single_line_allowed_omission_of_parentheses_4.parse-tree-whitequark.exp new file mode 100644 index 0000000000..6b093807b6 --- /dev/null +++ b/test/whitequark/test_pattern_matching_single_line_allowed_omission_of_parentheses_4.parse-tree-whitequark.exp @@ -0,0 +1,11 @@ +s(:begin, + s(:match_pattern_p, + s(:hash, + s(:pair, + s(:sym, :key), + s(:sym, :value))), + s(:hash_pattern, + s(:pair, + s(:sym, :key), + s(:match_var, :value)))), + s(:lvar, :value)) diff --git a/test/whitequark/test_pattern_matching_single_line_allowed_omission_of_parentheses_4.rb b/test/whitequark/test_pattern_matching_single_line_allowed_omission_of_parentheses_4.rb new file mode 100644 index 0000000000..61f8e6f1d9 --- /dev/null +++ b/test/whitequark/test_pattern_matching_single_line_allowed_omission_of_parentheses_4.rb @@ -0,0 +1,3 @@ +# typed: true + +{key: :value} in key: value; value diff --git a/test/whitequark/test_pattern_matching_single_line_allowed_omission_of_parentheses_5.parse-tree-whitequark.exp b/test/whitequark/test_pattern_matching_single_line_allowed_omission_of_parentheses_5.parse-tree-whitequark.exp new file mode 100644 index 0000000000..c72b873b29 --- /dev/null +++ b/test/whitequark/test_pattern_matching_single_line_allowed_omission_of_parentheses_5.parse-tree-whitequark.exp @@ -0,0 +1,11 @@ +s(:begin, + s(:match_pattern, + s(:hash, + s(:pair, + s(:sym, :key), + s(:sym, :value))), + s(:hash_pattern, + s(:pair, + s(:sym, :key), + s(:match_var, :value)))), + s(:lvar, :value)) diff --git a/test/whitequark/test_pattern_matching_single_line_allowed_omission_of_parentheses_5.rb b/test/whitequark/test_pattern_matching_single_line_allowed_omission_of_parentheses_5.rb new file mode 100644 index 0000000000..1988583348 --- /dev/null +++ b/test/whitequark/test_pattern_matching_single_line_allowed_omission_of_parentheses_5.rb @@ -0,0 +1,3 @@ +# typed: true + +{key: :value} => key: value; value diff --git a/test/whitequark/test_pin_expr_6.parse-tree-whitequark.exp b/test/whitequark/test_pin_expr_6.parse-tree-whitequark.exp new file mode 100644 index 0000000000..d784094f33 --- /dev/null +++ b/test/whitequark/test_pin_expr_6.parse-tree-whitequark.exp @@ -0,0 +1,6 @@ +s(:case_match, + s(:lvar, :foo), + s(:in_pattern, + s(:pin, + s(:begin, + s(:int, "1"))), nil, nil), nil) diff --git a/test/whitequark/test_pin_expr_6.rb b/test/whitequark/test_pin_expr_6.rb new file mode 100644 index 0000000000..59300643f0 --- /dev/null +++ b/test/whitequark/test_pin_expr_6.rb @@ -0,0 +1,4 @@ +# typed: true + +case foo; in ^(1 +); end diff --git a/third_party/doctest.BUILD b/third_party/doctest.BUILD deleted file mode 100644 index e0da3cb1ee..0000000000 --- a/third_party/doctest.BUILD +++ /dev/null @@ -1,38 +0,0 @@ -cc_library( - name = "doctest", - hdrs = glob(["doctest/**/*.h"]), - defines = [ - "DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL", - "DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS", - ], - strip_include_prefix = "doctest", - visibility = ["//visibility:public"], -) - -genrule( - name = "dummy-main", - outs = ["dummy-main.cc"], - cmd = """ - echo '#include "doctest/doctest.h"' > $@ - """, -) - -cc_library( - name = "doctest_main", - testonly = True, - srcs = glob(["doctest/**/*.h"]) + ["dummy-main.cc"], - local_defines = ["DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN"], - visibility = ["//visibility:public"], -) - -cc_library( - name = "doctest_custom_main", - # NOTE(varun): sprintf has been deprecated in newer macOS SDKs, and - # I was running into puzzling include related issues when upgrading - # to doctest 2.4.9, so temporarily silence these warnings. - copts = ["-Wno-deprecated-declarations"], - testonly = True, - srcs = glob(["doctest/**/*.h"]) + ["dummy-main.cc"], - local_defines = ["DOCTEST_CONFIG_IMPLEMENT"], - visibility = ["//visibility:public"], -) diff --git a/third_party/externals.bzl b/third_party/externals.bzl index d921bbcdff..892d0ec5df 100644 --- a/third_party/externals.bzl +++ b/third_party/externals.bzl @@ -17,10 +17,9 @@ def register_sorbet_dependencies(): http_archive( name = "doctest", - urls = _github_public_urls("doctest/doctest/archive/2.4.1.zip"), - sha256 = "d8d304db5a2e6d42e290b23a08a68db05478755e64db57b067cd805738e2c56f", - build_file = "@com_stripe_ruby_typer//third_party:doctest.BUILD", - strip_prefix = "doctest-2.4.1", + urls = _github_public_urls("doctest/doctest/archive/v2.4.9.zip"), + sha256 = "88a552f832ef3e4e7b733f9ab4eff5d73d7c37e75bebfef4a3339bf52713350d", + strip_prefix = "doctest-2.4.9", ) http_archive( @@ -169,12 +168,19 @@ def register_sorbet_dependencies(): strip_prefix = "bazel-compilation-database-6b9329e37295eab431f82af5fe24219865403e0f", ) + http_archive( + name = "rules_cc", + sha256 = "b6f34b3261ec02f85dbc5a8bdc9414ce548e1f5f67e000d7069571799cb88b25", + strip_prefix = "rules_cc-726dd8157557f1456b3656e26ab21a1646653405", + urls = ["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/bazelbuild/rules_cc/archive/726dd8157557f1456b3656e26ab21a1646653405.tar.gz"], + ) + # NOTE: we use the sorbet branch for development to keep our changes rebasable on grailio/bazel-toolchain http_archive( name = "com_grail_bazel_toolchain", - urls = _github_public_urls("sorbet/bazel-toolchain/archive/a685e1e6bd1e7cc9a5b84f832539585bb68d8ab4.zip"), - sha256 = "90c59f14cada755706a38bdd0f5ad8f0402cbf766387929cfbee9c3f1b4c82d7", - strip_prefix = "bazel-toolchain-a685e1e6bd1e7cc9a5b84f832539585bb68d8ab4", + urls = _github_public_urls("sorbet/bazel-toolchain/archive/4124470e037b4464a88db71c8565ad44af29664d.zip"), + sha256 = "23e9aa7318a6c3bfb2712c4c85e731f1cde8bfdd0d84b39aab6b8746ab7c391e", + strip_prefix = "bazel-toolchain-4124470e037b4464a88db71c8565ad44af29664d", ) http_archive( @@ -308,15 +314,6 @@ def register_sorbet_dependencies(): build_file = "@com_stripe_ruby_typer//third_party/openssl:linux.BUILD", ) - http_archive( - name = "rules_rust", - sha256 = "727b93eb5d57ec411f2afda7e3993e22d7772d0b2555ba745c3dec7323ea955a", - strip_prefix = "rules_rust-0768a7f00de134910c3cbdab7bbfdd011d995766", - - # Master branch as of 2021-06-29 - urls = _github_public_urls("bazelbuild/rules_rust/archive/0768a7f00de134910c3cbdab7bbfdd011d995766.tar.gz"), - ) - http_archive( name = "bazel_skylib", sha256 = "9a737999532daca978a158f94e77e9af6a6a169709c0cee274f0a4c3359519bd", diff --git a/third_party/jemalloc.BUILD b/third_party/jemalloc.BUILD index ee88e80ced..5062682774 100644 --- a/third_party/jemalloc.BUILD +++ b/third_party/jemalloc.BUILD @@ -29,18 +29,20 @@ JEMALLOC_BUILD_COMMAND = """ export OBJCOPY=$$(absolutize $(OBJCOPY)) export CFLAGS=$(CC_FLAGS) export CXXFLAGS=$(CC_FLAGS) - export LTOFLAGS="$$([ "$$(uname)" = "Linux" ] && echo "-flto=thin")" # todo: on next clang toolchain upgrade, check if it's fixed and we can re-enable thinlto on mac + export LTOFLAGS="$$([ "$$(uname)" = "Linux" ] && echo "-flto=thin -Wl,--thinlto-jobs=all")" # todo: on next clang toolchain upgrade, check if it's fixed and we can re-enable thinlto on mac export EXTRA_CFLAGS="$${LTOFLAGS}" export EXTRA_CXXFLAGS="-stdlib=libc++ $${EXTRA_CFLAGS}" LDFLAGS="$${LTOFLAGS}" + AUTOGEN_FLAGS= case "$$(uname)" in Linux) LDFLAGS="$${LDFLAGS} -fuse-ld=lld" ;; Darwin) LDFLAGS="$${LDFLAGS} -mlinker-version=400" + AUTOGEN_FLAGS=--with-lg-vaddr=48 ;; esac @@ -51,7 +53,7 @@ JEMALLOC_BUILD_COMMAND = """ pushd $$(dirname $(location autogen.sh)) > /dev/null - if compile_output=$$(./autogen.sh --without-export --disable-shared --enable-static 2>&1 && make build_lib_static -j4 2>&1); then + if compile_output=$$(./autogen.sh --without-export --disable-shared --enable-static $$AUTOGEN_FLAGS 2>&1 && make build_lib_static -j4 2>&1); then popd > /dev/null mv $$(dirname $(location autogen.sh))/lib/libjemalloc.a $(location lib/libjemalloc.a) mv $$(dirname $(location autogen.sh))/include/jemalloc/jemalloc.h $(location include/jemalloc/jemalloc.h) @@ -98,7 +100,7 @@ cc_library( srcs = [":jemalloc_genrule"], hdrs = ["include/jemalloc/jemalloc.h"], linkopts = select({ - "@com_stripe_ruby_typer//tools/config:linux": ["-ldl"], # side step https://github.com/jemalloc/jemalloc/issues/948 + "@platforms//os:linux": ["-ldl"], # side step https://github.com/jemalloc/jemalloc/issues/948 "//conditions:default": [], }), linkstatic = 1, diff --git a/third_party/llvm/llvm.bzl b/third_party/llvm/llvm.bzl index e3edb437f4..bb8f966862 100644 --- a/third_party/llvm/llvm.bzl +++ b/third_party/llvm/llvm.bzl @@ -299,7 +299,7 @@ win32_cmake_vars = { # TODO(phawkins): use a better method to select the right host triple, rather # than hardcoding x86_64. llvm_all_cmake_vars = select({ - "@com_stripe_ruby_typer//tools/config:darwin": cmake_var_string( + "@com_stripe_ruby_typer//tools/config:darwin_x86_64": cmake_var_string( _dict_add( cmake_vars, llvm_target_cmake_vars("X86", "x86_64-apple-darwin"), @@ -307,7 +307,7 @@ llvm_all_cmake_vars = select({ darwin_cmake_vars, ), ), - "@com_stripe_ruby_typer//tools/config:linux": cmake_var_string( + "@com_stripe_ruby_typer//tools/config:linux_x86_64": cmake_var_string( _dict_add( cmake_vars, llvm_target_cmake_vars("X86", "x86_64-unknown-linux_gnu"), diff --git a/third_party/openssl/linux.BUILD b/third_party/openssl/linux.BUILD index 30ae31a9ce..c6513a4f84 100644 --- a/third_party/openssl/linux.BUILD +++ b/third_party/openssl/linux.BUILD @@ -1,6 +1,9 @@ cc_import( name = "ssl-import", - shared_library = "lib/x86_64-linux-gnu/libssl.so", + shared_library = select({ + "@platforms//cpu:x86_64": "lib/x86_64-linux-gnu/libssl.so", + "@platforms//cpu:arm64": "lib/aarch64-linux-gnu/libssl.so", + }), visibility = ["//visibility:private"], ) @@ -13,7 +16,10 @@ cc_library( cc_import( name = "crypto-import", - shared_library = "lib/x86_64-linux-gnu/libcrypto.so", + shared_library = select({ + "@platforms//cpu:x86_64": "lib/x86_64-linux-gnu/libcrypto.so", + "@platforms//cpu:arm64": "lib/aarch64-linux-gnu/libcrypto.so", + }), visibility = ["//visibility:private"], ) diff --git a/third_party/ruby/BUILD b/third_party/ruby/BUILD index 5f3a3bc5fa..e69de29bb2 100644 --- a/third_party/ruby/BUILD +++ b/third_party/ruby/BUILD @@ -1 +0,0 @@ -exports_files(["sorbet_ruby_bundler.patch"]) diff --git a/third_party/ruby/build-ruby.bzl b/third_party/ruby/build-ruby.bzl index 18d0084235..29304b4d46 100644 --- a/third_party/ruby/build-ruby.bzl +++ b/third_party/ruby/build-ruby.bzl @@ -151,11 +151,6 @@ cp "$out_dir/bin/bundle" "$out_dir/bin/bundler" {install_gems} -# Since we get our version of bundler from update_rubygems, we have to apply -# this patch after everything is built. This is a bit of a hack, but the need -# for the patch should go away once we upgrade Ruby and/or bundler anyway. -{post_build_patch_command} - popd > /dev/null rm -rf "$build_dir" @@ -269,18 +264,10 @@ def _build_ruby_impl(ctx): install_gems = [_INSTALL_GEM.format(file = file.path) for file in ctx.files.gems] - post_build_patches = ctx.files.post_build_patches - - post_build_patch_commands = [] - for patch in post_build_patches: - dirname = patch.dirname - install_extra_srcs.append(_INSTALL_EXTRA_SRC.format(file = patch.path, dirname = dirname, basename = patch.basename)) - post_build_patch_commands.append(_APPLY_PATCH.format(path = patch.path)) - # Build ctx.actions.run_shell( mnemonic = "BuildRuby", - inputs = deps + ctx.files.src + ctx.files.rubygems + ctx.files.gems + ctx.files.extra_srcs + ctx.files.append_srcs + post_build_patches, + inputs = deps + ctx.files.src + ctx.files.rubygems + ctx.files.gems + ctx.files.extra_srcs + ctx.files.append_srcs, outputs = outputs, command = ctx.expand_location(_BUILD_RUBY.format( cc = cc, @@ -299,7 +286,6 @@ def _build_ruby_impl(ctx): extra_srcs_object_files = " ".join(extra_srcs_object_files), install_append_srcs = "\n".join(install_append_srcs), install_gems = "\n".join(install_gems), - post_build_patch_command = "\n".join(post_build_patch_commands), )), ) @@ -351,10 +337,6 @@ _build_ruby = rule( default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), ), "sysroot_flag": attr.string(), - "post_build_patches": attr.label_list( - allow_files = True, - doc = "Patches to apply to the output tree after Ruby is built and gems are installed", - ), }, fragments = ["cpp"], provides = [ @@ -547,7 +529,7 @@ _ruby_internal_headers = rule( implementation = _ruby_internal_headers_impl, ) -def ruby(rubygems, gems, extra_srcs = None, append_srcs = None, configure_flags = [], copts = [], cppopts = [], linkopts = [], deps = [], post_build_patches = []): +def ruby(rubygems, gems, extra_srcs = None, append_srcs = None, configure_flags = [], copts = [], cppopts = [], linkopts = [], deps = []): """ Define a ruby build. """ @@ -572,10 +554,9 @@ def ruby(rubygems, gems, extra_srcs = None, append_srcs = None, configure_flags gems = gems, # This is a hack because macOS Catalina changed the way that system headers and libraries work. sysroot_flag = select({ - "@com_stripe_ruby_typer//tools/config:darwin": "-isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk", + "@platforms//os:osx": "-isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk", "//conditions:default": "", }), - post_build_patches = post_build_patches, ) _ruby_headers( diff --git a/third_party/ruby/gc-add-need-major-by.patch b/third_party/ruby/gc-add-need-major-by.patch new file mode 100644 index 0000000000..c97fec32f9 --- /dev/null +++ b/third_party/ruby/gc-add-need-major-by.patch @@ -0,0 +1,52 @@ +diff --git gc.c gc.c +index 67a709ff79..2825466788 100644 +--- gc.c ++++ gc.c +@@ -8738,7 +8738,7 @@ gc_count(rb_execution_context_t *ec, VALUE self) + static VALUE + gc_info_decode(rb_objspace_t *objspace, const VALUE hash_or_key, const int orig_flags) + { +- static VALUE sym_major_by = Qnil, sym_gc_by, sym_immediate_sweep, sym_have_finalizer, sym_state; ++ static VALUE sym_major_by = Qnil, sym_gc_by, sym_immediate_sweep, sym_have_finalizer, sym_state, sym_need_major_by; + static VALUE sym_nofree, sym_oldgen, sym_shady, sym_force, sym_stress; + #if RGENGC_ESTIMATE_OLDMALLOC + static VALUE sym_oldmalloc; +@@ -8746,8 +8746,9 @@ gc_info_decode(rb_objspace_t *objspace, const VALUE hash_or_key, const int orig_ + static VALUE sym_newobj, sym_malloc, sym_method, sym_capi; + static VALUE sym_none, sym_marking, sym_sweeping; + VALUE hash = Qnil, key = Qnil; +- VALUE major_by; ++ VALUE major_by, need_major_by; + VALUE flags = orig_flags ? orig_flags : objspace->profile.latest_gc_info; ++ unsigned int need_major_flags = objspace->rgengc.need_major_gc; + + if (SYMBOL_P(hash_or_key)) { + key = hash_or_key; +@@ -8766,6 +8767,7 @@ gc_info_decode(rb_objspace_t *objspace, const VALUE hash_or_key, const int orig_ + S(immediate_sweep); + S(have_finalizer); + S(state); ++ S(need_major_by); + + S(stress); + S(nofree); +@@ -8803,6 +8805,19 @@ gc_info_decode(rb_objspace_t *objspace, const VALUE hash_or_key, const int orig_ + Qnil; + SET(major_by, major_by); + ++ if (orig_flags == 0) { /* set need_major_by only if flags not set explicitly */ ++ need_major_by = ++ (need_major_flags & GPR_FLAG_MAJOR_BY_NOFREE) ? sym_nofree : ++ (need_major_flags & GPR_FLAG_MAJOR_BY_OLDGEN) ? sym_oldgen : ++ (need_major_flags & GPR_FLAG_MAJOR_BY_SHADY) ? sym_shady : ++ (need_major_flags & GPR_FLAG_MAJOR_BY_FORCE) ? sym_force : ++#if RGENGC_ESTIMATE_OLDMALLOC ++ (need_major_flags & GPR_FLAG_MAJOR_BY_OLDMALLOC) ? sym_oldmalloc : ++#endif ++ Qnil; ++ SET(need_major_by, need_major_by); ++ } ++ + SET(gc_by, + (flags & GPR_FLAG_NEWOBJ) ? sym_newobj : + (flags & GPR_FLAG_MALLOC) ? sym_malloc : diff --git a/third_party/ruby/gc-fix-malloc-increase-calculation.patch b/third_party/ruby/gc-fix-malloc-increase-calculation.patch new file mode 100644 index 0000000000..5be732080c --- /dev/null +++ b/third_party/ruby/gc-fix-malloc-increase-calculation.patch @@ -0,0 +1,31 @@ +diff --git gc.c gc.c +index 67a709ff79..57996b2f9d 100644 +--- gc.c ++++ gc.c +@@ -7166,7 +7166,7 @@ ready_to_gc(rb_objspace_t *objspace) + } + + static void +-gc_reset_malloc_info(rb_objspace_t *objspace) ++gc_reset_malloc_info(rb_objspace_t *objspace, bool full_mark) + { + gc_prof_set_malloc_info(objspace); + { +@@ -7200,7 +7200,7 @@ gc_reset_malloc_info(rb_objspace_t *objspace) + + /* reset oldmalloc info */ + #if RGENGC_ESTIMATE_OLDMALLOC +- if (!is_full_marking(objspace)) { ++ if (!full_mark) { + if (objspace->rgengc.oldmalloc_increase > objspace->rgengc.oldmalloc_increase_limit) { + objspace->rgengc.need_major_gc |= GPR_FLAG_MAJOR_BY_OLDMALLOC; + objspace->rgengc.oldmalloc_increase_limit = +@@ -7343,7 +7343,7 @@ gc_start(rb_objspace_t *objspace, int reason) + objspace->profile.total_allocated_objects_at_gc_start = objspace->total_allocated_objects; + objspace->profile.heap_used_at_gc_start = heap_allocated_pages; + gc_prof_setup_new_record(objspace, reason); +- gc_reset_malloc_info(objspace); ++ gc_reset_malloc_info(objspace, do_full_mark); + rb_transient_heap_start_marking(do_full_mark); + + gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_START, 0 /* TODO: pass minor/immediate flag? */); diff --git a/third_party/ruby/ruby-versions.txt b/third_party/ruby/ruby-versions.txt index 50fa329326..15c55feafa 100644 --- a/third_party/ruby/ruby-versions.txt +++ b/third_party/ruby/ruby-versions.txt @@ -1,2 +1,3 @@ -sorbet_ruby_2_7_unpatched sorbet_ruby_2_7 +sorbet_ruby_3_0 +sorbet_ruby_3_1 diff --git a/third_party/ruby/ruby.BUILD b/third_party/ruby/ruby.BUILD index 478259c741..7c7f9dcfba 100644 --- a/third_party/ruby/ruby.BUILD +++ b/third_party/ruby/ruby.BUILD @@ -39,23 +39,23 @@ ruby( "@bundler_stripe//file", ], linkopts = select({ - "@com_stripe_ruby_typer//tools/config:linux": [ + "@platforms//os:linux": [ "-Wl,-Bsymbolic-functions", "-Wl,-z,relro", "-Wl,-z,noexecstack", ], - "@com_stripe_ruby_typer//tools/config:darwin": [ + "@platforms//os:osx": [ "-mlinker-version=400", ], "//conditions:default": [], }), rubygems = "@rubygems_update_stripe//file", deps = select({ - "@com_stripe_ruby_typer//tools/config:darwin": [ + "@platforms//os:osx": [ "@system_ssl_darwin//:ssl", "@system_ssl_darwin//:crypto", ], - "@com_stripe_ruby_typer//tools/config:linux": [ + "@platforms//os:linux": [ "@system_ssl_linux//:ssl", "@system_ssl_linux//:crypto", ], diff --git a/third_party/ruby/ruby_for_compiler.BUILD b/third_party/ruby/ruby_for_compiler.BUILD index a2d41c9a3c..b3aa84ac15 100644 --- a/third_party/ruby/ruby_for_compiler.BUILD +++ b/third_party/ruby/ruby_for_compiler.BUILD @@ -45,24 +45,23 @@ ruby( "@bundler_stripe//file", ], linkopts = select({ - "@com_stripe_ruby_typer//tools/config:linux": [ + "@platforms//os:linux": [ "-Wl,-Bsymbolic-functions", "-Wl,-z,relro", "-Wl,-z,noexecstack", ], - "@com_stripe_ruby_typer//tools/config:darwin": [ + "@platforms//os:osx": [ "-mlinker-version=400", ], "//conditions:default": [], }), - post_build_patches = ["@com_stripe_ruby_typer//third_party/ruby:sorbet_ruby_bundler.patch"], rubygems = "@rubygems_update_stripe//file", deps = select({ - "@com_stripe_ruby_typer//tools/config:darwin": [ + "@platforms//os:osx": [ "@system_ssl_darwin//:ssl", "@system_ssl_darwin//:crypto", ], - "@com_stripe_ruby_typer//tools/config:linux": [ + "@platforms//os:linux": [ "@system_ssl_linux//:ssl", "@system_ssl_linux//:crypto", ], diff --git a/third_party/ruby/sorbet_ruby_bundler.patch b/third_party/ruby/sorbet_ruby_bundler.patch deleted file mode 100644 index 4b85455f8a..0000000000 --- a/third_party/ruby/sorbet_ruby_bundler.patch +++ /dev/null @@ -1,286 +0,0 @@ -From 074be8ed31b9f5df253ca07dd9cc9a12443bcfd9 Mon Sep 17 00:00:00 2001 -From: Adam Procter -Date: Mon, 18 Oct 2021 14:23:48 -0700 -Subject: [PATCH] Patch bundler to avoid loading "set" too early - ---- - lib/bundler/definition.rb | 5 +++-- - lib/bundler/index.rb | 16 +++++++++++----- - lib/bundler/resolver.rb | 6 ++++-- - lib/bundler/source_list.rb | 12 ++++++++---- - lib/bundler/spec_set.rb | 10 +++++++--- - .../molinillo/lib/molinillo/dependency_graph.rb | 5 +++-- - .../lib/molinillo/dependency_graph/vertex.rb | 12 +++++++++--- - lib/bundler/vendor/thor/lib/thor.rb | 15 +++++++++------ - 8 files changed, 54 insertions(+), 27 deletions(-) - -diff --git a/lib/ruby/site_ruby/2.7.0/bundler/definition.rb b/lib/ruby/site_ruby/2.7.0/bundler/definition.rb -index d6fbb0b5b7d..abd66cae638 100644 ---- a/lib/ruby/site_ruby/2.7.0/bundler/definition.rb -+++ b/lib/ruby/site_ruby/2.7.0/bundler/definition.rb -@@ -1,7 +1,6 @@ - # frozen_string_literal: true - - require_relative "lockfile_parser" --require "set" - - module Bundler - class Definition -@@ -589,7 +588,9 @@ def dependencies_for_source_changed?(source, locked_source = source) - deps_for_source = @dependencies.select {|s| s.source == source } - locked_deps_for_source = @locked_deps.values.select {|dep| dep.source == locked_source } - -- Set.new(deps_for_source) != Set.new(locked_deps_for_source) -+ deps_h = deps_for_source.each_with_object(Hash.new(false)) { |v, h| h[v] = true } -+ locked_deps_h = locked_deps_for_source.each_with_object(Hash.new(false)) { |v, h| h[v] = true } -+ deps_h != locked_deps_h - end - - def specs_for_source_changed?(source) -diff --git a/lib/ruby/site_ruby/2.7.0/bundler/index.rb b/lib/ruby/site_ruby/2.7.0/bundler/index.rb -index 9166a927388..b853fa754f5 100644 ---- a/lib/ruby/site_ruby/2.7.0/bundler/index.rb -+++ b/lib/ruby/site_ruby/2.7.0/bundler/index.rb -@@ -1,7 +1,5 @@ - # frozen_string_literal: true - --require "set" -- - module Bundler - class Index - include Enumerable -@@ -65,11 +63,16 @@ def search(query, base = nil) - def unsorted_search(query, base) - results = local_search(query, base) - -- seen = results.map(&:full_name).to_set unless @sources.empty? -+ if !@sources.empty? -+ seen = results.map(&:full_name).each_with_object(Hash.new(false)) { |v, h| h[v] = true } -+ end - - @sources.each do |source| - source.unsorted_search(query, base).each do |spec| -- results << spec if seen.add?(spec.full_name) -+ if not seen[spec.full_name] -+ seen[spec.full_name] = true -+ results << spec -+ end - end - end - -@@ -170,7 +173,10 @@ def ==(other) - def dependencies_eql?(spec, other_spec) - deps = spec.dependencies.select {|d| d.type != :development } - other_deps = other_spec.dependencies.select {|d| d.type != :development } -- Set.new(deps) == Set.new(other_deps) -+ -+ deps_h = deps.each_with_object(Hash.new(false)) { |v, h| h[v] = true } -+ other_h = other_deps.each_with_object(Hash.new(false)) { |v, h| h[v] = true } -+ deps_h == other_h - end - - def add_source(index) -diff --git a/lib/ruby/site_ruby/2.7.0/bundler/resolver.rb b/lib/ruby/site_ruby/2.7.0/bundler/resolver.rb -index c7caf01c7d3..134234ccf78 100644 ---- a/lib/ruby/site_ruby/2.7.0/bundler/resolver.rb -+++ b/lib/ruby/site_ruby/2.7.0/bundler/resolver.rb -@@ -16,7 +16,9 @@ class Resolver - # ,nil:: If the list of dependencies can be resolved, a - # collection of gemspecs is returned. Otherwise, nil is returned. - def self.resolve(requirements, index, source_requirements = {}, base = [], gem_version_promoter = GemVersionPromoter.new, additional_base_requirements = [], platforms = nil) -- platforms = Set.new(platforms) if platforms -+ if platforms -+ platforms = platforms.each_with_object(Hash.new(false)) { |v, h| h[v] = true } -+ end - base = SpecSet.new(base) unless base.is_a?(SpecSet) - resolver = new(index, source_requirements, base, gem_version_promoter, additional_base_requirements, platforms) - result = resolver.start(requirements) -@@ -184,7 +186,7 @@ def name_for_locking_dependency_source - - def requirement_satisfied_by?(requirement, activated, spec) - return false unless requirement.matches_spec?(spec) || spec.source.is_a?(Source::Gemspec) -- spec.activate_platform!(requirement.__platform) if !@platforms || @platforms.include?(requirement.__platform) -+ spec.activate_platform!(requirement.__platform) if !@platforms || @platforms[requirement.__platform] - true - end - -diff --git a/lib/ruby/site_ruby/2.7.0/bundler/source_list.rb b/lib/ruby/site_ruby/2.7.0/bundler/source_list.rb -index d3f649a12c3..058fee8120c 100644 ---- a/lib/ruby/site_ruby/2.7.0/bundler/source_list.rb -+++ b/lib/ruby/site_ruby/2.7.0/bundler/source_list.rb -@@ -1,7 +1,5 @@ - # frozen_string_literal: true - --require "set" -- - module Bundler - class SourceList - attr_reader :path_sources, -@@ -99,7 +97,10 @@ def replace_sources!(replacement_sources) - @rubygems_aggregate = replacement_rubygems if replacement_rubygems - - return true if !equal_sources?(lock_sources, replacement_sources) && !equivalent_sources?(lock_sources, replacement_sources) -- return true if replacement_rubygems && rubygems_remotes.to_set != replacement_rubygems.remotes.to_set -+ return false if !replacement_rubygems -+ rubygems_h = rubygems_remotes.each_with_object(Hash.new(false)) { |v, h| h[v] = true } -+ replacement_h = replacement_rubygems.remotes.each_with_object(Hash.new(false)) { |v, h| h[v] = true } -+ return true if rubygems_h != replacement_h - - false - end -@@ -153,7 +154,10 @@ def warn_on_git_protocol(source) - end - - def equal_sources?(lock_sources, replacement_sources) -- lock_sources.to_set == replacement_sources.to_set -+ # lock_sources.to_set == replacement_sources.to_set -+ lock_sources_h = lock_sources.each_with_object(Hash.new(false)) { |v, h| h[v] = true } -+ replacement_sources_h = replacement_sources.each_with_object(Hash.new(false)) { |v, h| h[v] = true } -+ lock_sources_h == replacement_sources_h - end - - def equal_source?(source, other_source) -diff --git a/lib/ruby/site_ruby/2.7.0/bundler/spec_set.rb b/lib/ruby/site_ruby/2.7.0/bundler/spec_set.rb -index 463113ef8e0..f0e933b24a9 100644 ---- a/lib/ruby/site_ruby/2.7.0/bundler/spec_set.rb -+++ b/lib/ruby/site_ruby/2.7.0/bundler/spec_set.rb -@@ -1,7 +1,6 @@ - # frozen_string_literal: true - - require "tsort" --require "set" - - module Bundler - class SpecSet -@@ -13,14 +12,19 @@ def initialize(specs) - end - - def for(dependencies, skip = [], check = false, match_current_platform = false, raise_on_missing = true) -- handled = Set.new -+ handled = Hash.new(false) - deps = dependencies.dup - specs = [] - skip += ["bundler"] - - loop do - break unless dep = deps.shift -- next if !handled.add?(dep) || skip.include?(dep.name) -+ if handled[dep] -+ next -+ else -+ handled[dep] = true -+ next if skip.include?(dep.name) -+ end - - if spec = spec_for_dependency(dep, match_current_platform) - specs << spec -diff --git a/lib/ruby/site_ruby/2.7.0/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb b/lib/ruby/site_ruby/2.7.0/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb -index 31578bb5bf9..9690b1143c5 100644 ---- a/lib/ruby/site_ruby/2.7.0/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb -+++ b/lib/ruby/site_ruby/2.7.0/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb -@@ -1,6 +1,5 @@ - # frozen_string_literal: true - --require 'set' - require 'tsort' - - require_relative 'dependency_graph/log' -@@ -134,7 +133,9 @@ def ==(other) - other_vertex = other.vertex_named(name) - return false unless other_vertex - return false unless vertex.payload == other_vertex.payload -- return false unless other_vertex.successors.to_set == vertex.successors.to_set -+ succs = vertex.successors.each_with_object(Hash.new(false)) { |v, h| h[v] = true } -+ other_succs = other_vertex.successors.each_with_object(Hash.new(false)) { |v, h| h[v] = true } -+ return false unless succs == other_succs - end - end - -diff --git a/lib/ruby/site_ruby/2.7.0/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb b/lib/ruby/site_ruby/2.7.0/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb -index 41bc013143c..7d91f2fdb3a 100644 ---- a/lib/ruby/site_ruby/2.7.0/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb -+++ b/lib/ruby/site_ruby/2.7.0/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb -@@ -59,7 +59,9 @@ def recursive_predecessors - # @param [Set] vertices the set to add the predecessors to - # @return [Set] the vertices of {#graph} where `self` is a - # {#descendent?} -- def _recursive_predecessors(vertices = Set.new) -+ def _recursive_predecessors(vertices = nil) -+ require 'set' -+ vertices ||= Set.new - incoming_edges.each do |edge| - vertex = edge.origin - next unless vertices.add?(vertex) -@@ -85,7 +87,9 @@ def recursive_successors - # @param [Set] vertices the set to add the successors to - # @return [Set] the vertices of {#graph} where `self` is an - # {#ancestor?} -- def _recursive_successors(vertices = Set.new) -+ def _recursive_successors(vertices = nil) -+ require 'set' -+ vertices ||= Set.new - outgoing_edges.each do |edge| - vertex = edge.destination - next unless vertices.add?(vertex) -@@ -138,7 +142,9 @@ def path_to?(other) - # @param [Vertex] other the vertex to check if there's a path to - # @param [Set] visited the vertices of {#graph} that have been visited - # @return [Boolean] whether there is a path to `other` from `self` -- def _path_to?(other, visited = Set.new) -+ def _path_to?(other, visited = nil) -+ require 'set' -+ visited ||= Set.new - return false unless visited.add?(self) - return true if equal?(other) - successors.any? { |v| v._path_to?(other, visited) } -diff --git a/lib/ruby/site_ruby/2.7.0/bundler/vendor/thor/lib/thor.rb b/lib/ruby/site_ruby/2.7.0/bundler/vendor/thor/lib/thor.rb -index 01c0b2f83c8..11332d04262 100644 ---- a/lib/ruby/site_ruby/2.7.0/bundler/vendor/thor/lib/thor.rb -+++ b/lib/ruby/site_ruby/2.7.0/bundler/vendor/thor/lib/thor.rb -@@ -1,4 +1,3 @@ --require "set" - require_relative "thor/base" - - class Bundler::Thor -@@ -323,7 +322,7 @@ def check_unknown_options?(config) #:nodoc: - # ==== Parameters - # Symbol ...:: A list of commands that should be affected. - def stop_on_unknown_option!(*command_names) -- stop_on_unknown_option.merge(command_names) -+ command_names.each { |k| stop_on_unknown_option[k] = true } - end - - def stop_on_unknown_option?(command) #:nodoc: -@@ -337,11 +336,11 @@ def stop_on_unknown_option?(command) #:nodoc: - # ==== Parameters - # Symbol ...:: A list of commands that should be affected. - def disable_required_check!(*command_names) -- disable_required_check.merge(command_names) -+ command_names.each { |k| disable_required_check[k] = true } - end - - def disable_required_check?(command) #:nodoc: -- command && disable_required_check.include?(command.name.to_sym) -+ command && disable_required_check[command.name.to_sym] - end - - def deprecation_warning(message) #:nodoc: -@@ -354,12 +353,16 @@ def deprecation_warning(message) #:nodoc: - protected - - def stop_on_unknown_option #:nodoc: -- @stop_on_unknown_option ||= Set.new -+ @stop_on_unknown_option ||= Hash.new(false) - end - - # help command has the required check disabled by default. - def disable_required_check #:nodoc: -- @disable_required_check ||= Set.new([:help]) -+ if not @disable_required_check -+ @disable_required_check = Hash.new(false) -+ @disable_required_check[:help] = true -+ end -+ @disable_required_check - end - - # The method responsible for dispatching given the args. diff --git a/third_party/ruby_externals.bzl b/third_party/ruby_externals.bzl index 1808909150..72fedfdb27 100644 --- a/third_party/ruby_externals.bzl +++ b/third_party/ruby_externals.bzl @@ -10,8 +10,8 @@ def register_ruby_dependencies(): http_file( name = "rubygems_update_stripe", - urls = _rubygems_urls("rubygems-update-3.1.2.gem"), - sha256 = "7bfe4e5e274191e56da8d127c79df10d9120feb8650e4bad29238f4b2773a661", + urls = _rubygems_urls("rubygems-update-3.3.3.gem"), + sha256 = "610aef544e0c15ff3cd5492dff3f5f46bd2062896f4f62c7191432c6f1d681c9", ) ruby_build = "@com_stripe_ruby_typer//third_party/ruby:ruby.BUILD" @@ -47,6 +47,8 @@ def register_ruby_dependencies(): "@com_stripe_ruby_typer//third_party/ruby:gc-remove-write-barrier.patch", "@com_stripe_ruby_typer//third_party/ruby:dtoa.patch", "@com_stripe_ruby_typer//third_party/ruby:penelope_procc.patch", + "@com_stripe_ruby_typer//third_party/ruby:gc-fix-malloc-increase-calculation.patch", # https://github.com/ruby/ruby/pull/4860 + "@com_stripe_ruby_typer//third_party/ruby:gc-add-need-major-by.patch", # https://github.com/ruby/ruby/pull/6791 ], ) diff --git a/third_party/test_gem.BUILD b/third_party/test_gem.BUILD index 7d69da58e8..e8e804ed45 100644 --- a/third_party/test_gem.BUILD +++ b/third_party/test_gem.BUILD @@ -2,4 +2,4 @@ filegroup( name = "all", srcs = glob(["**/*"]), visibility = ["//visibility:public"], -) \ No newline at end of file +) diff --git a/tools/BUILD b/tools/BUILD index 29b67a8d2b..bdec469b72 100644 --- a/tools/BUILD +++ b/tools/BUILD @@ -26,6 +26,7 @@ compilation_database( "//common:common", "//common:common_test", "//common/concurrency:concurrency", + "//common/counters:counters", "//common/crypto_hashing:crypto_hashing", "//common/enforce_no_timer:enforce_no_timer", "//common/exception:exception", @@ -33,7 +34,10 @@ compilation_database( "//common/kvstore:kvstore", "//common/kvstore:kvstore_test", "//common/os:os", + "//common/sort:sort", "//common/statsd:statsd", + "//common/strings:strings", + "//common/timers:timers", "//common/web_tracer_framework:tracing", # "//compiler:sorbet", # "//compiler/Core:Core", diff --git a/tools/config/BUILD b/tools/config/BUILD index 3c66534fec..8cb7039792 100644 --- a/tools/config/BUILD +++ b/tools/config/BUILD @@ -1,6 +1,6 @@ package(default_visibility = ["//visibility:public"]) -platform( +config_setting( name = "darwin_x86_64", constraint_values = [ "@platforms//os:osx", @@ -8,7 +8,7 @@ platform( ], ) -platform( +config_setting( name = "linux_x86_64", constraint_values = [ "@platforms//os:linux", @@ -17,18 +17,10 @@ platform( ) config_setting( - name = "darwin", - constraint_values = [ - "@platforms//os:osx", - "@platforms//cpu:x86_64", - ], -) - -config_setting( - name = "linux", + name = "linux_arm64", constraint_values = [ "@platforms//os:linux", - "@platforms//cpu:x86_64", + "@platforms//cpu:arm64", ], ) diff --git a/tools/platforms/BUILD b/tools/platforms/BUILD new file mode 100644 index 0000000000..31766e643e --- /dev/null +++ b/tools/platforms/BUILD @@ -0,0 +1,17 @@ +package(default_visibility = ["//visibility:public"]) + +platform( + name = "darwin_x86_64", + constraint_values = [ + "@platforms//os:osx", + "@platforms//cpu:x86_64", + ], +) + +platform( + name = "linux_x86_64", + constraint_values = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + ], +) diff --git a/tools/scripts/import_whitequark.sh b/tools/scripts/import_whitequark.sh index e61403a27d..321df3e719 100755 --- a/tools/scripts/import_whitequark.sh +++ b/tools/scripts/import_whitequark.sh @@ -14,8 +14,8 @@ set -euo pipefail set -x -REF=3e421d6bc4d7a480d0a0e7aae23afe714a61ea87 -TARGET_RUBY_VERSION="3.1" +REF=fbc2d7b14b838845af35b3c4596ae61c3f696ad1 +TARGET_RUBY_VERSION="3.2" SCRIPT=$(realpath "$0") ROOT="$(cd "$(dirname "$SCRIPT")/../.."; pwd)" @@ -54,6 +54,7 @@ bundle exec racc --superclass=Parser::Base lib/parser/ruby26.y -o lib/parser/rub bundle exec racc --superclass=Parser::Base lib/parser/ruby27.y -o lib/parser/ruby27.rb --no-line-convert bundle exec racc --superclass=Parser::Base lib/parser/ruby30.y -o lib/parser/ruby30.rb --no-line-convert bundle exec racc --superclass=Parser::Base lib/parser/ruby31.y -o lib/parser/ruby31.rb --no-line-convert +bundle exec racc --superclass=Parser::Base lib/parser/ruby32.y -o lib/parser/ruby32.rb --no-line-convert bundle exec racc --superclass=Parser::Base lib/parser/macruby.y -o lib/parser/macruby.rb --no-line-convert bundle exec racc --superclass=Parser::Base lib/parser/rubymotion.y -o lib/parser/rubymotion.rb --no-line-convert diff --git a/vscode_extension/.eslintrc.yaml b/vscode_extension/.eslintrc.yaml index 6dd6f6dc6f..0b504593f9 100644 --- a/vscode_extension/.eslintrc.yaml +++ b/vscode_extension/.eslintrc.yaml @@ -99,8 +99,8 @@ rules: 'newline-per-chained-call': [2, {ignoreChainWithDepth: 5}] # Disallow multiple empty lines 'no-multiple-empty-lines': 2 - # Allow dangling underscores in identifiers - 'no-underscore-dangle': 0 + # Disallow dangling underscores + 'no-underscore-dangle': [2, {enforceInMethodNames: true}] # Allow useless computed keys. # We disable useless computed _string_ keys in a forked version of this # rule, no-useless-computed-string-key. We allow computed number keys diff --git a/vscode_extension/.vscode/launch.json b/vscode_extension/.vscode/launch.json index 59912088a2..61cebfb112 100644 --- a/vscode_extension/.vscode/launch.json +++ b/vscode_extension/.vscode/launch.json @@ -6,6 +6,9 @@ "type": "extensionHost", "request": "launch", "runtimeExecutable": "${execPath}", + "env": { + "VSCODE_SORBETEXT_LOG_LEVEL": "trace" + }, "args": [ "--extensionDevelopmentPath=${workspaceRoot}" ], diff --git a/vscode_extension/CHANGELOG.md b/vscode_extension/CHANGELOG.md index 2d41454c20..55f284ed21 100644 --- a/vscode_extension/CHANGELOG.md +++ b/vscode_extension/CHANGELOG.md @@ -1,4 +1,6 @@ # Version history +## 0.3.20 +- `Sorbet` status bar item shows a quick-pick drop down instead of a notification dialog when clicked. ## 0.3.7 diff --git a/vscode_extension/package.json b/vscode_extension/package.json index 8634e8c38e..426c672a59 100644 --- a/vscode_extension/package.json +++ b/vscode_extension/package.json @@ -4,7 +4,7 @@ "description": "Ruby IDE features, powered by Sorbet.", "author": "Stripe Inc.", "license": "Apache-2.0", - "version": "0.3.18", + "version": "0.3.19", "publisher": "sorbet", "icon": "icon.png", "repository": { @@ -27,7 +27,9 @@ "onCommand:sorbet.disable", "onCommand:sorbet.enable", "onCommand:sorbet.restart", + "onCommand:sorbet.setLogLevel", "onCommand:sorbet.showOutput", + "onCommand:sorbet.toggleHighlightUntyped", "onLanguage:ruby", "workspaceContains:sorbet/*" ], @@ -52,6 +54,12 @@ "title": "Configure", "category": "Sorbet" }, + { + "command": "sorbet.copySymbolToClipboard", + "title": "Copy Symbol to Clipboard", + "category": "Sorbet", + "enablement": "editorLangId == ruby" + }, { "command": "sorbet.disable", "title": "Disable", @@ -62,25 +70,32 @@ "title": "Enable", "category": "Sorbet" }, + { + "command": "sorbet.rename", + "title": "Rename Symbol", + "category": "Sorbet", + "enablement": "editorLangId == ruby" + }, { "command": "sorbet.restart", "title": "Restart", "category": "Sorbet" }, { - "command": "sorbet.showOutput", - "title": "Show Output", + "command": "sorbet.setLogLevel", + "title": "Set Log Level…", "category": "Sorbet" }, { - "command": "sorbet.copySymbolToClipboard", - "title": "Copy Symbol to Clipboard", + "command": "sorbet.showOutput", + "title": "Show Output", "category": "Sorbet" }, { - "command": "sorbet.rename", - "title": "Rename Symbol", - "category": "Sorbet" + "command": "sorbet.toggleHighlightUntyped", + "title": "Toggle highlighting untyped code", + "category": "Sorbet", + "enablement": "workbenchState != empty" } ], "configuration": { @@ -91,11 +106,11 @@ "type": "boolean" }, "sorbet.selectedLspConfigId": { - "description": "The default configuration to use from `sorbet.userLspConfigs` or `sorbet.lspConfigs`. If unset, defaults to the first item in `sorbet.userLspConfigs` or `sorbet.lspConfigs`.", + "markdownDescription": "The default configuration to use from `sorbet.userLspConfigs` or `sorbet.lspConfigs`. If unset, defaults to the first item in `sorbet.userLspConfigs` or `sorbet.lspConfigs`.", "type": "string" }, "sorbet.lspConfigs": { - "description": "Standard Ruby LSP configurations. If you commit your VSCode settings to source control, you probably want to commit *this* setting, not `sorbet.userLspConfigs`.", + "markdownDescription": "Standard Ruby LSP configurations. If you commit your VSCode settings to source control, you probably want to commit *this* setting, not `sorbet.userLspConfigs`.", "type": "array", "default": [ { @@ -178,7 +193,7 @@ } }, "sorbet.userLspConfigs": { - "description": "Custom user LSP configurations that supplement `sorbet.lspConfigs` (and override configurations with the same id). If you commit your VSCode settings to source control, you probably want to commit `sorbet.lspConfigs`, not this value.", + "markdownDescription": "Custom user LSP configurations that supplement `sorbet.lspConfigs` (and override configurations with the same id). If you commit your VSCode settings to source control, you probably want to commit `sorbet.lspConfigs`, not this value.", "type": "array", "default": [], "items": { @@ -238,6 +253,11 @@ "description": "Show the extension output window on errors.", "default": false }, + "sorbet.highlightUntyped": { + "type": "boolean", + "description": "Shows warning for untyped values.", + "default": false + }, "sorbet.configFilePatterns": { "type": "array", "description": "List of workspace file patterns that contribute to Sorbet's configuration. Changes to any of those files should trigger a restart of any actively running Sorbet language server.", @@ -258,24 +278,6 @@ } }, "menus": { - "commandPalette": [ - { - "command": "sorbet.configure" - }, - { - "command": "sorbet.disable" - }, - { - "command": "sorbet.enable" - }, - { - "command": "sorbet.restart" - }, - { - "command": "sorbet.showOutput", - "when": "editorLangId == ruby" - } - ], "editor/context": [ { "when": "resourceLangId == ruby", @@ -299,14 +301,12 @@ "dependencies": { "async": "^2.6.4", "elegant-spinner": "^2.0.0", - "lodash": "^4.17.21", - "minimatch": "^3.0.3", + "minimatch": "^3.0.5", "vscode-languageclient": "7.0.0" }, "devDependencies": { "@types/elegant-spinner": "^1.0.0", "@types/glob": "^7.1.1", - "@types/lodash": "^4.14.144", "@types/mocha": "^5.2.7", "@types/node": "^10.11.7", "@types/sinon": "^7.5.0", diff --git a/vscode_extension/src/ConfigPicker.ts b/vscode_extension/src/ConfigPicker.ts deleted file mode 100644 index 6f154e2458..0000000000 --- a/vscode_extension/src/ConfigPicker.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { QuickPickItem, window } from "vscode"; -import { isEqual } from "lodash"; -import { SorbetExtensionConfig, SorbetLspConfig } from "./config"; - -interface SorbetQuickPickItem extends QuickPickItem { - lspConfig: SorbetLspConfig | null; -} - -export default class SorbetConfigPicker { - private readonly _extensionConfig: SorbetExtensionConfig; - public constructor(c: SorbetExtensionConfig) { - this._extensionConfig = c; - } - - public async show(): Promise { - const { activeLspConfig, lspConfigs } = this._extensionConfig; - const pickOptions: SorbetQuickPickItem[] = lspConfigs.map((c) => { - return { - label: `${isEqual(activeLspConfig, c) ? "• " : ""}${c.name}`, - description: c.description, - detail: c.command.join(" "), - lspConfig: c, - }; - }); - pickOptions.push({ - label: `${activeLspConfig ? "" : "• "}Disable Sorbet`, - description: "Disable the Sorbet extension", - lspConfig: null, - }); - const selected = await window.showQuickPick( - pickOptions, - { - placeHolder: "Select a Sorbet configuration", - ignoreFocusOut: false, - }, - ); - if (selected) { - const { lspConfig } = selected; - if (lspConfig) { - this._extensionConfig.setActiveLspConfigId(lspConfig.id); - } else { - this._extensionConfig.setEnabled(false); - } - } - } -} diff --git a/vscode_extension/src/SorbetStatusBarEntry.ts b/vscode_extension/src/SorbetStatusBarEntry.ts deleted file mode 100644 index 2982364f6b..0000000000 --- a/vscode_extension/src/SorbetStatusBarEntry.ts +++ /dev/null @@ -1,198 +0,0 @@ -import * as Spinner from "elegant-spinner"; -import { commands, OutputChannel, StatusBarAlignment, window } from "vscode"; - -import { ShowOperationParams, ServerStatus, RestartReason } from "./types"; -import { SorbetExtensionConfig } from "./config"; -import SorbetConfigPicker from "./ConfigPicker"; - -/** - * Actions provided when 'Sorbet' fails to start. - */ -const enum Action { - EnableSorbet = "Enable Sorbet", - ConfigureSorbet = "Configure Sorbet", - RestartSorbet = "Restart Sorbet", - DisableSorbet = "Disable Sorbet", - ViewOutput = "View Output", -} - -export default class SorbetStatusBarEntry { - private readonly _statusBarItem = window.createStatusBarItem( - StatusBarAlignment.Left, - 10, - ); - - private _operationStack: ShowOperationParams[] = []; - private _serverStatus: ServerStatus = ServerStatus.DISABLED; - private _lastError = ""; - private readonly _spinner = Spinner(); - private _spinnerTimer?: NodeJS.Timer; - - constructor( - private readonly _outputChannel: OutputChannel, - private readonly _sorbetExtensionConfig: SorbetExtensionConfig, - private readonly _restartSorbet: (reason: RestartReason) => void, - ) { - // Note: Internal command. Not advertised to users in `package.json`. - const statusBarClickedCommand = "_sorbet.statusBarClicked"; - this._statusBarItem.command = statusBarClickedCommand; - this._render(); - this._statusBarItem.show(); - commands.registerCommand(statusBarClickedCommand, () => - this.handleStatusBarClicked(), - ); - _sorbetExtensionConfig.onLspConfigChange(() => this._render()); - } - - private _runAction(action?: string) { - switch (action) { - case Action.ViewOutput: - this._outputChannel.show(); - break; - case Action.ConfigureSorbet: - new SorbetConfigPicker(this._sorbetExtensionConfig).show(); - break; - case Action.EnableSorbet: - this._sorbetExtensionConfig.setEnabled(true); - break; - case Action.DisableSorbet: - this._sorbetExtensionConfig.setEnabled(false); - break; - case Action.RestartSorbet: - this._restartSorbet(RestartReason.STATUS_BAR_BUTTON); - break; - default: - // Nothing selected. - break; - } - } - - public async handleStatusBarClicked(): Promise { - switch (this._serverStatus) { - case ServerStatus.ERROR: { - const actions = [ - Action.ViewOutput, - Action.ConfigureSorbet, - Action.RestartSorbet, - ]; - const message = await window.showErrorMessage( - this._lastError, - ...actions, - ); - return this._runAction(message); - } - case ServerStatus.DISABLED: { - // Switch to Sorbet option. - return this._runAction( - await window.showInformationMessage( - "Sorbet: Select action...", - Action.ViewOutput, - Action.ConfigureSorbet, - Action.EnableSorbet, - ), - ); - } - default: { - return this._runAction( - await window.showInformationMessage( - "Sorbet: Select action...", - Action.ViewOutput, - Action.ConfigureSorbet, - Action.RestartSorbet, - Action.DisableSorbet, - ), - ); - } - } - } - - public changeServerStatus(s: ServerStatus, lastError: string = "") { - const isError = this._serverStatus !== s && s === ServerStatus.ERROR; - this._serverStatus = s; - this._lastError = lastError; - this._render(); - if (isError) { - this._restartSorbet(RestartReason.CRASH_EXT_ERROR); - } - } - - public handleShowOperation(p: ShowOperationParams) { - if (p.status === "end") { - this._operationStack = this._operationStack.filter( - (otherP) => otherP.operationName !== p.operationName, - ); - } else { - this._operationStack.push(p); - } - this._render(); - } - - public clearOperations() { - this._operationStack = []; - this._render(); - } - - public dispose() { - this._statusBarItem.dispose(); - } - - private _getSpinner() { - if (this._spinnerTimer) { - clearTimeout(this._spinnerTimer); - } - // Animate the spinner with setTimeout. - this._spinnerTimer = setTimeout(() => this._render(), 100); - return this._spinner(); - } - - private _render() { - const numOperations = this._operationStack.length; - const { activeLspConfig } = this._sorbetExtensionConfig; - const sorbetName = activeLspConfig ? activeLspConfig.name : "Sorbet"; - let text: string; - let tooltip: string; - // Errors should suppress operation animations / feedback. - if ( - activeLspConfig && - this._serverStatus !== ServerStatus.ERROR && - numOperations > 0 - ) { - const latestOp = this._operationStack[numOperations - 1]; - text = `${sorbetName}: ${latestOp.description} ${this._getSpinner()}`; - tooltip = latestOp.description; - } else { - switch (this._serverStatus) { - case ServerStatus.DISABLED: - text = `${sorbetName}: Disabled`; - tooltip = "The Sorbet server is disabled."; - break; - case ServerStatus.ERROR: - text = `${sorbetName}: Error`; - tooltip = `${this._lastError} Click for remediation items.`; - break; - case ServerStatus.INITIALIZING: - text = `${sorbetName}: Initializing ${this._getSpinner()}`; - tooltip = "The Sorbet server is initializing."; - break; - case ServerStatus.RESTARTING: - text = `${sorbetName}: Restarting ${this._getSpinner()}`; - tooltip = "The Sorbet server is restarting."; - break; - case ServerStatus.RUNNING: - text = `${sorbetName}: Idle`; - tooltip = "The Sorbet server is currently running."; - break; - default: - this._outputChannel.appendLine( - `Invalid ServerStatus: ${this._serverStatus}`, - ); - text = ""; - tooltip = ""; - break; - } - } - - this._statusBarItem.text = text; - this._statusBarItem.tooltip = tooltip; - } -} diff --git a/vscode_extension/src/commandIds.ts b/vscode_extension/src/commandIds.ts new file mode 100644 index 0000000000..26f0ce1b0b --- /dev/null +++ b/vscode_extension/src/commandIds.ts @@ -0,0 +1,34 @@ +/** + * Set log level available actions. + */ +export const SET_LOGLEVEL_COMMAND_ID = "sorbet.setLogLevel"; + +/** + * Show available actions. + */ +export const SHOW_ACTIONS_COMMAND_ID = "sorbet.showAvailableActions"; + +/** + * Show Configuration picker. + */ +export const SHOW_CONFIG_PICKER_COMMAND_ID = "sorbet.configure"; + +/** + * Show Sorbet Output panel. + */ +export const SHOW_OUTPUT_COMMAND_ID = "sorbet.showOutput"; + +/** + * Enable Sorbet. + */ +export const SORBET_ENABLE_COMMAND_ID = "sorbet.enable"; + +/** + * Disable Sorbet. + */ +export const SORBET_DISABLE_COMMAND_ID = "sorbet.disable"; + +/** + * Restart Sorbet. + */ +export const SORBET_RESTART_COMMAND_ID = "sorbet.restart"; diff --git a/vscode_extension/src/commands/setLogLevel.ts b/vscode_extension/src/commands/setLogLevel.ts new file mode 100644 index 0000000000..54d915ba46 --- /dev/null +++ b/vscode_extension/src/commands/setLogLevel.ts @@ -0,0 +1,55 @@ +import { QuickPickItem, window } from "vscode"; +import { LogLevel } from "../log"; +import { SorbetExtensionContext } from "../sorbetExtensionContext"; + +export type LogLevelQuickPickItem = QuickPickItem & { + level: LogLevel; +}; + +/** + * Set logging level on associated 'Log' instance. + */ +export class SetLogLevel { + private readonly context: SorbetExtensionContext; + + constructor(context: SorbetExtensionContext) { + this.context = context; + } + + /** + * Execute command. + * @param level Log level. If not provided, user will be prompted for it. + */ + public async execute(level?: LogLevel): Promise { + const newLevel = level ?? (await this.getLogLevel()); + if (newLevel === undefined) { + return; // Canceled + } + this.context.log.level = newLevel; + } + + private async getLogLevel(): Promise { + const items = [ + LogLevel.Trace, + LogLevel.Debug, + LogLevel.Info, + LogLevel.Warning, + LogLevel.Error, + LogLevel.Critical, + LogLevel.Off, + ].map((logLevel) => { + const item = { + label: `${this.context.log.level === logLevel ? "• " : ""}${ + LogLevel[logLevel] + }`, + level: logLevel, + }; + return item; + }); + + const selectedLevel = await window.showQuickPick(items, { + placeHolder: "Select log level", + }); + return selectedLevel?.level; + } +} diff --git a/vscode_extension/src/commands/showSorbetActions.ts b/vscode_extension/src/commands/showSorbetActions.ts new file mode 100644 index 0000000000..e28e983a67 --- /dev/null +++ b/vscode_extension/src/commands/showSorbetActions.ts @@ -0,0 +1,80 @@ +import { commands, window } from "vscode"; +import { + SHOW_CONFIG_PICKER_COMMAND_ID, + SHOW_OUTPUT_COMMAND_ID, + SORBET_DISABLE_COMMAND_ID, + SORBET_ENABLE_COMMAND_ID, + SORBET_RESTART_COMMAND_ID, +} from "../commandIds"; +import { SorbetExtensionContext } from "../sorbetExtensionContext"; +import { SorbetStatusProvider } from "../sorbetStatusProvider"; +import { RestartReason, ServerStatus } from "../types"; + +export const enum Action { + ConfigureSorbet = "Configure Sorbet", + DisableSorbet = "Disable Sorbet", + EnableSorbet = "Enable Sorbet", + RestartSorbet = "Restart Sorbet", + ViewOutput = "View Output", +} + +/** + * Show available actions in a drop-down. + */ +export class ShowSorbetActions { + private readonly statusProvider: SorbetStatusProvider; + + constructor(context: SorbetExtensionContext) { + this.statusProvider = context.statusProvider; + } + + public async execute(): Promise { + const actions = this.getAvailableActions(); + const selectedAction = await window.showQuickPick(actions, { + placeHolder: "Sorbet: Select action...", + }); + + switch (selectedAction) { + case Action.ConfigureSorbet: + await commands.executeCommand(SHOW_CONFIG_PICKER_COMMAND_ID); + break; + case Action.DisableSorbet: + await commands.executeCommand(SORBET_DISABLE_COMMAND_ID); + break; + case Action.EnableSorbet: + await commands.executeCommand(SORBET_ENABLE_COMMAND_ID); + break; + case Action.RestartSorbet: + await commands.executeCommand( + SORBET_RESTART_COMMAND_ID, + RestartReason.STATUS_BAR_BUTTON, + ); + break; + case Action.ViewOutput: + await commands.executeCommand(SHOW_OUTPUT_COMMAND_ID); + break; + default: + break; // User canceled + } + } + + /** + * Get available {@link Action actions} based on Sorbet' status. + */ + public getAvailableActions(): Action[] { + const actions = [Action.ViewOutput]; + switch (this.statusProvider.serverStatus) { + case ServerStatus.ERROR: + actions.push(Action.RestartSorbet); + break; + case ServerStatus.DISABLED: + actions.push(Action.EnableSorbet); + break; + default: + actions.push(Action.RestartSorbet, Action.DisableSorbet); + break; + } + actions.push(Action.ConfigureSorbet); + return actions; + } +} diff --git a/vscode_extension/src/commands/showSorbetConfigurationPicker.ts b/vscode_extension/src/commands/showSorbetConfigurationPicker.ts new file mode 100644 index 0000000000..68babd8b5d --- /dev/null +++ b/vscode_extension/src/commands/showSorbetConfigurationPicker.ts @@ -0,0 +1,44 @@ +import { QuickPickItem, window } from "vscode"; +import { SorbetExtensionConfig, SorbetLspConfig } from "../config"; +import { SorbetExtensionContext } from "../sorbetExtensionContext"; + +interface SorbetQuickPickItem extends QuickPickItem { + lspConfig?: SorbetLspConfig; +} + +/** + * Show Sorbet Configuration picker. + */ +export class ShowSorbetConfigurationPicker { + private readonly configuration: SorbetExtensionConfig; + + public constructor(context: SorbetExtensionContext) { + this.configuration = context.configuration; + } + + public async execute(): Promise { + const { activeLspConfig, lspConfigs } = this.configuration; + const items: SorbetQuickPickItem[] = lspConfigs.map((config) => ({ + label: `${config.isEqualTo(activeLspConfig) ? "• " : ""}${config.name}`, + description: config.description, + detail: config.command.join(" "), + lspConfig: config, + })); + items.push({ + label: `${activeLspConfig ? "" : "• "}Disable Sorbet`, + description: "Disable the Sorbet extension", + }); + + const selectedItem = await window.showQuickPick(items, { + placeHolder: "Select a Sorbet configuration", + }); + if (selectedItem) { + const { lspConfig } = selectedItem; + if (lspConfig) { + this.configuration.setActiveLspConfigId(lspConfig.id); + } else { + this.configuration.setEnabled(false); + } + } + } +} diff --git a/vscode_extension/src/config.ts b/vscode_extension/src/config.ts index aa7a726f18..2666c8b9ab 100644 --- a/vscode_extension/src/config.ts +++ b/vscode_extension/src/config.ts @@ -1,18 +1,24 @@ -import { isEqual } from "lodash"; import { - workspace, - Event, - EventEmitter, ConfigurationChangeEvent, Disposable, + Event, + EventEmitter, ExtensionContext, - Memento, FileSystemWatcher, + Memento, Uri, + workspace, WorkspaceFolder, } from "vscode"; import * as fs from "fs"; +/** + * Compare two `string` arrays for deep, in-order equality. + */ +function deepEqual(a: ReadonlyArray, b: ReadonlyArray) { + return a.length === b.length && a.every((itemA, index) => itemA === b[index]); +} + interface ISorbetLspConfig { readonly id: string; /** Display name suitable for short-form fields like menu items or status fields. */ @@ -74,7 +80,7 @@ export class SorbetLspConfig { if (this.cwd !== other.cwd) { return false; } - if (!isEqual(this.command, other.command)) { + if (!deepEqual(this.command, other.command)) { return false; } return true; @@ -122,47 +128,48 @@ export interface ISorbetWorkspaceContext { /** Default implementation accesses `workspace` directly. */ export class DefaultSorbetWorkspaceContext implements ISorbetWorkspaceContext { - static _workspaceStateChangeEmitter = new EventEmitter(); - private _workspaceState: Memento; - private _cachedSorbetConfiguration = workspace.getConfiguration("sorbet"); - private _emitter = new EventEmitter(); + static workspaceStateChangeEmitter = new EventEmitter(); + private workspaceState: Memento; + private cachedSorbetConfiguration = workspace.getConfiguration("sorbet"); + private onConfigurationChangeEmitter = new EventEmitter< + ConfigurationChangeEvent + >(); + constructor(extensionContext: ExtensionContext) { - this._workspaceState = extensionContext.workspaceState; + this.workspaceState = extensionContext.workspaceState; workspace.onDidChangeConfiguration((e) => { if (e.affectsConfiguration("sorbet")) { // update the cached configuration before firing - this._cachedSorbetConfiguration = workspace.getConfiguration("sorbet"); - this._emitter.fire(e); + this.cachedSorbetConfiguration = workspace.getConfiguration("sorbet"); + this.onConfigurationChangeEmitter.fire(e); } }); - DefaultSorbetWorkspaceContext._workspaceStateChangeEmitter.event((k) => { - this._emitter.fire({ + DefaultSorbetWorkspaceContext.workspaceStateChangeEmitter.event((k) => { + this.onConfigurationChangeEmitter.fire({ affectsConfiguration: () => k.startsWith("sorbet."), }); }); } public get(section: string, defaultValue: T): T { - const workspaceStateValue = this._workspaceState.get( - `sorbet.${section}`, - ); + const workspaceStateValue = this.workspaceState.get(`sorbet.${section}`); if (workspaceStateValue !== undefined) { return workspaceStateValue; } - return this._cachedSorbetConfiguration.get(section, defaultValue); + return this.cachedSorbetConfiguration.get(section, defaultValue); } public update(section: string, value: any): Thenable { const key = `sorbet.${section}`; - return this._workspaceState + return this.workspaceState .update(key, value) .then(() => - DefaultSorbetWorkspaceContext._workspaceStateChangeEmitter.fire(key), + DefaultSorbetWorkspaceContext.workspaceStateChangeEmitter.fire(key), ); } public get onDidChangeConfiguration(): Event { - return this._emitter.event; + return this.onConfigurationChangeEmitter.event; } public workspaceFolders(): readonly WorkspaceFolder[] | undefined { @@ -173,18 +180,18 @@ export class DefaultSorbetWorkspaceContext implements ISorbetWorkspaceContext { * This function is a workaround to make it possible to enable Sorbet on first launch. * * The `sorbet.enabled` setting always has its default value set to `false` from `package.json` and cannot be - * undefined. That means that invoking `workspaceContext.get("enabled", this._enabled)` will always return `false` on - * first launch regardless of the value of `this._enabled`. + * undefined. That means that invoking `workspaceContext.get("enabled", this.enabled)` will always return `false` on + * first launch regardless of the value of `this.enabled`. * * To workaround this, we check if `sorbet.enabled` is still undefined in the workspace state and in every type of * configuration other than the `defaultValue`. If that's the case, then we can update the workspace state and enable * Sorbet on first launch. */ public initializeEnabled(enabled: boolean) { - const stateEnabled = this._workspaceState.get("sorbet.enabled"); + const stateEnabled = this.workspaceState.get("sorbet.enabled"); if (stateEnabled === undefined) { - const cachedConfig = this._cachedSorbetConfiguration.inspect("enabled"); + const cachedConfig = this.cachedSorbetConfiguration.inspect("enabled"); if ( cachedConfig?.globalValue === undefined && @@ -201,77 +208,81 @@ export class DefaultSorbetWorkspaceContext implements ISorbetWorkspaceContext { } export class SorbetExtensionConfig implements Disposable { - private _sorbetWorkspaceContext: ISorbetWorkspaceContext; - private _onLspConfigChangeEmitter = new EventEmitter< + private sorbetWorkspaceContext: ISorbetWorkspaceContext; + private readonly onLspConfigChangeEmitter = new EventEmitter< SorbetLspConfigChangeEvent >(); /** "Standard" LSP configs. */ - private _lspConfigs: ReadonlyArray = []; + private wrappedLspConfigs: ReadonlyArray = []; /** * "Custom" LSP configs that override/supplement "standard" LSP configs. * * If there is a '_lspConfig' and a '_userLspConfigs' */ - private _userLspConfigs: ReadonlyArray = []; - private _selectedLspConfigId: string | undefined = undefined; + private userLspConfigs: ReadonlyArray = []; + private selectedLspConfigId: string | undefined = undefined; - private _enabled: boolean; - private _revealOutputOnError: boolean = false; - private _configFilePatterns: ReadonlyArray = []; - private _configFileWatchers: ReadonlyArray = []; + private wrappedEnabled: boolean; + private wrappedRevealOutputOnError: boolean = false; + private wrappedHighlightUntyped: boolean = false; + private configFilePatterns: ReadonlyArray = []; + private configFileWatchers: ReadonlyArray = []; constructor(sorbetWorkspaceContext: ISorbetWorkspaceContext) { - this._sorbetWorkspaceContext = sorbetWorkspaceContext; - this._sorbetWorkspaceContext.onDidChangeConfiguration((_) => - this._refresh(), - ); + this.sorbetWorkspaceContext = sorbetWorkspaceContext; + this.sorbetWorkspaceContext.onDidChangeConfiguration((_) => this.refresh()); const workspaceFolders = sorbetWorkspaceContext.workspaceFolders(); - this._enabled = workspaceFolders + this.wrappedEnabled = workspaceFolders ? fs.existsSync(`${workspaceFolders[0].uri.fsPath}/sorbet/config`) : false; - this._sorbetWorkspaceContext.initializeEnabled(this._enabled); + this.sorbetWorkspaceContext.initializeEnabled(this.wrappedEnabled); - this._refresh(); + this.refresh(); } /** * Refreshes the configuration from this._sorbetWorkspaceConfiguration, * emitting change events as necessary. */ - private _refresh(): void { + private refresh(): void { const oldLspConfig = this.activeLspConfig; - const workspaceContext = this._sorbetWorkspaceContext; - this._enabled = workspaceContext.get("enabled", this._enabled); - this._revealOutputOnError = workspaceContext.get( + const workspaceContext = this.sorbetWorkspaceContext; + this.wrappedEnabled = workspaceContext.get("enabled", this.wrappedEnabled); + this.wrappedRevealOutputOnError = workspaceContext.get( "revealOutputOnError", this.revealOutputOnError, ); - const oldConfigFilePatterns = this._configFilePatterns; - this._configFilePatterns = [ - ...workspaceContext.get("configFilePatterns", this._configFilePatterns), + this.wrappedHighlightUntyped = workspaceContext.get( + "highlightUntyped", + this.highlightUntyped, + ); + + const oldConfigFilePatterns = this.configFilePatterns; + this.configFilePatterns = [ + ...workspaceContext.get("configFilePatterns", this.configFilePatterns), ]; - Disposable.from(...this._configFileWatchers).dispose(); - this._configFileWatchers = this._configFilePatterns.map((pattern) => { + Disposable.from(...this.configFileWatchers).dispose(); + this.configFileWatchers = this.configFilePatterns.map((pattern) => { const watcher = workspace.createFileSystemWatcher(pattern); - const _onConfigChange = (_: Uri) => { + const onConfigChange = (_uri: Uri) => { const c = this.activeLspConfig; - this._onLspConfigChangeEmitter.fire({ + this.onLspConfigChangeEmitter.fire({ oldLspConfig: c, newLspConfig: c, }); }; - watcher.onDidChange(_onConfigChange); - watcher.onDidCreate(_onConfigChange); - watcher.onDidDelete(_onConfigChange); + watcher.onDidChange(onConfigChange); + watcher.onDidCreate(onConfigChange); + watcher.onDidDelete(onConfigChange); return watcher; }); const iLspConfigs = workspaceContext.get("lspConfigs", []); - this._lspConfigs = iLspConfigs.map((c) => new SorbetLspConfig(c)); + this.wrappedLspConfigs = iLspConfigs.map((c) => new SorbetLspConfig(c)); const iUserLspConfigs = workspaceContext.get("userLspConfigs", []); - this._userLspConfigs = iUserLspConfigs.map((c) => new SorbetLspConfig(c)); + this.userLspConfigs = iUserLspConfigs.map((c) => new SorbetLspConfig(c)); let configId = workspaceContext.get( "selectedLspConfigId", undefined, @@ -284,13 +295,13 @@ export class SorbetExtensionConfig implements Disposable { configId = configs[0].id; } } - this._selectedLspConfigId = configId; + this.selectedLspConfigId = configId; const newLspConfig = this.activeLspConfig; if ( !SorbetLspConfig.areEqual(oldLspConfig, newLspConfig) || - !isEqual(oldConfigFilePatterns, this._configFilePatterns) + !deepEqual(oldConfigFilePatterns, this.configFilePatterns) ) { - this._onLspConfigChangeEmitter.fire({ + this.onLspConfigChangeEmitter.fire({ oldLspConfig, newLspConfig, }); @@ -301,7 +312,7 @@ export class SorbetExtensionConfig implements Disposable { * An event that fires when the (effective) active configuration changes. */ public get onLspConfigChange(): Event { - return this._onLspConfigChangeEmitter.event; + return this.onLspConfigChangeEmitter.event; } /** @@ -310,7 +321,7 @@ export class SorbetExtensionConfig implements Disposable { public get lspConfigs(): ReadonlyArray { const results: Array = []; const resultIds = new Set(); - [...this._userLspConfigs, ...this._lspConfigs].forEach((c) => { + [...this.userLspConfigs, ...this.wrappedLspConfigs].forEach((c) => { if (!resultIds.has(c.id)) { results.push(c); resultIds.add(c.id); @@ -336,7 +347,7 @@ export class SorbetExtensionConfig implements Disposable { * the `id` refers to a `SorbetLspConfig` that does not exist, return `undefined`. */ public get selectedLspConfig(): SorbetLspConfig | undefined { - return this.lspConfigs.find((c) => c.id === this._selectedLspConfigId); + return this.lspConfigs.find((c) => c.id === this.selectedLspConfigId); } /** @@ -346,9 +357,9 @@ export class SorbetExtensionConfig implements Disposable { * configuration.) */ public setSelectedLspConfigId(id: string): Thenable { - return this._sorbetWorkspaceContext + return this.sorbetWorkspaceContext .update("selectedLspConfigId", id) - .then(this._refresh.bind(this)); + .then(this.refresh.bind(this)); } /** @@ -359,26 +370,36 @@ export class SorbetExtensionConfig implements Disposable { */ public setActiveLspConfigId(id: string): Thenable { return Promise.all([ - this._sorbetWorkspaceContext.update("selectedLspConfigId", id), - this._sorbetWorkspaceContext.update("enabled", true), - ]).then(this._refresh.bind(this)); + this.sorbetWorkspaceContext.update("selectedLspConfigId", id), + this.sorbetWorkspaceContext.update("enabled", true), + ]).then(this.refresh.bind(this)); } public get revealOutputOnError(): boolean { - return this._revealOutputOnError; + return this.wrappedRevealOutputOnError; + } + + public get highlightUntyped(): boolean { + return this.wrappedHighlightUntyped; } public get enabled(): boolean { - return this._enabled; + return this.wrappedEnabled; } public setEnabled(b: boolean): Thenable { - return this._sorbetWorkspaceContext + return this.sorbetWorkspaceContext .update("enabled", b) - .then(this._refresh.bind(this)); + .then(this.refresh.bind(this)); + } + + public setHighlightUntyped(b: boolean): Thenable { + return this.sorbetWorkspaceContext + .update("highlightUntyped", b) + .then(this.refresh.bind(this)); } dispose() { - Disposable.from(...this._configFileWatchers).dispose(); + Disposable.from(...this.configFileWatchers).dispose(); } } diff --git a/vscode_extension/src/connections.ts b/vscode_extension/src/connections.ts index 0b0476334f..877d779bed 100644 --- a/vscode_extension/src/connections.ts +++ b/vscode_extension/src/connections.ts @@ -1,16 +1,20 @@ import { ChildProcess } from "child_process"; +import { OutputChannelLog } from "./log"; /** * Attempts to stop the given child process. Tries a SIGINT, then a SIGTERM, then a SIGKILL. */ -export async function stopProcess(p: ChildProcess | null): Promise { +export async function stopProcess( + p: ChildProcess | null, + log: OutputChannelLog, +): Promise { if (!p || !p.pid) { // Process is already dead. return; } return new Promise((res) => { let hasExited = false; - console.log(`Stopping process ${p.pid}`); + log.debug(`Stopping process ${p.pid}`); function onExit() { if (!hasExited) { hasExited = true; @@ -22,12 +26,12 @@ export async function stopProcess(p: ChildProcess | null): Promise { p.kill("SIGINT"); setTimeout(() => { if (!hasExited) { - console.log("Process did not respond to SIGINT. Sending a SIGTERM."); + log.debug("Process did not respond to SIGINT. Sending a SIGTERM."); } p.kill("SIGTERM"); setTimeout(() => { if (!hasExited) { - console.log("Process did not respond to SIGTERM. Sending a SIGKILL."); + log.debug("Process did not respond to SIGTERM. Sending a SIGKILL."); p.kill("SIGKILL"); setTimeout(res, 100); } diff --git a/vscode_extension/src/extension.ts b/vscode_extension/src/extension.ts index fdba2af700..596a4d588c 100644 --- a/vscode_extension/src/extension.ts +++ b/vscode_extension/src/extension.ts @@ -1,205 +1,106 @@ -import { - ExtensionContext, - commands, - window, - TextDocumentContentProvider, - Uri, - workspace, -} from "vscode"; +import { commands, ExtensionContext, Uri, workspace } from "vscode"; import { TextDocumentItem } from "vscode-languageclient"; - -import SorbetConfigPicker from "./ConfigPicker"; -import { - SorbetExtensionConfig, - SorbetLspConfigChangeEvent, - DefaultSorbetWorkspaceContext, -} from "./config"; -import SorbetLanguageClient from "./LanguageClient"; +import * as cmdIds from "./commandIds"; +import { SetLogLevel } from "./commands/setLogLevel"; +import { ShowSorbetActions } from "./commands/showSorbetActions"; +import { ShowSorbetConfigurationPicker } from "./commands/showSorbetConfigurationPicker"; +import { getLogLevelFromEnvironment } from "./log"; +import { SorbetExtensionContext } from "./sorbetExtensionContext"; +import { SorbetStatusBarEntry } from "./sorbetStatusBarEntry"; import { ServerStatus, RestartReason } from "./types"; -import SorbetStatusBarEntry from "./SorbetStatusBarEntry"; -import { emitCountMetric } from "./veneur"; /** * Extension entrypoint. */ export function activate(context: ExtensionContext) { - const sorbetExtensionConfig = new SorbetExtensionConfig( - new DefaultSorbetWorkspaceContext(context), - ); - sorbetExtensionConfig.onLspConfigChange(handleConfigChange); - const outputChannel = window.createOutputChannel("Sorbet"); - const statusBarEntry = new SorbetStatusBarEntry( - outputChannel, - sorbetExtensionConfig, - restartSorbet, - ); - - const emitMetric = emitCountMetric.bind( - null, - sorbetExtensionConfig, - outputChannel, - ); - - let activeSorbetLanguageClient: SorbetLanguageClient | null = null; - context.subscriptions.push(outputChannel, statusBarEntry); - - function stopSorbet(newStatus: ServerStatus) { - if (activeSorbetLanguageClient) { - activeSorbetLanguageClient.dispose(); - // Garbage collect the language client. - const i = context.subscriptions.indexOf(activeSorbetLanguageClient); - if (i !== -1) { - context.subscriptions.splice(i, 1); - } - activeSorbetLanguageClient = null; - } - // Reset status bar state impacted by previous language client. - statusBarEntry.clearOperations(); - statusBarEntry.changeServerStatus(newStatus); - } - - function restartSorbet(reason: RestartReason) { - stopSorbet(ServerStatus.RESTARTING); - - // NOTE: `reason` is an enum type with a small and finite number of values. - emitMetric(`restart.${reason}`, 1); - startSorbet(); - } - - let lastSorbetRetryTime = 0; - const minTimeBetweenRetries = 7000; - // Mutex for startSorbet. Prevents us from starting multiple processes at once. - let isStarting = false; - async function startSorbet() { - if (isStarting) return; + const sorbetExtensionContext = new SorbetExtensionContext(context); + sorbetExtensionContext.log.level = getLogLevelFromEnvironment(); - const currentTime = Date.now(); - // Debounce by 7 seconds. Returns 0 if the calculated time to sleep is negative. - const timeToSleep = Math.max( - 0, - minTimeBetweenRetries - (currentTime - lastSorbetRetryTime), - ); - if (timeToSleep > 0) { - console.log( - `Waiting ${timeToSleep.toFixed(0)} ms before restarting Sorbet...`, - ); - } - - // Wait timeToSleep ms. Use mutex, as this yields the event loop for future events. - isStarting = true; - await new Promise((res) => setTimeout(res, timeToSleep)); - isStarting = false; - - lastSorbetRetryTime = Date.now(); - - const sorbet = new SorbetLanguageClient( - sorbetExtensionConfig, - outputChannel, - filterUpdatesFromOldClients(restartSorbet), - ); - activeSorbetLanguageClient = sorbet; - context.subscriptions.push(activeSorbetLanguageClient); - - // Helper function. Drops any status updates and operations from old clients that are in the process of shutting down. - function filterUpdatesFromOldClients( - fn: (...args: TS) => void, - ): (...args: TS) => void { - return (...args: TS): void => { - if (activeSorbetLanguageClient !== sorbet) { - return; + context.subscriptions.push( + sorbetExtensionContext, + sorbetExtensionContext.configuration.onLspConfigChange( + async ({ oldLspConfig, newLspConfig }) => { + const { statusProvider } = sorbetExtensionContext; + if (oldLspConfig && newLspConfig) { + // Something about the config changed, so restart + await statusProvider.restartSorbet(RestartReason.CONFIG_CHANGE); + } else if (oldLspConfig) { + await statusProvider.stopSorbet(ServerStatus.DISABLED); + } else { + await statusProvider.startSorbet(); } - return fn(...args); - }; - } - - // Pipe updates to status bar, and reset status bar state impacted by previous language client. - statusBarEntry.changeServerStatus(sorbet.status, sorbet.lastError); - - sorbet.onStatusChange = filterUpdatesFromOldClients( - (status: ServerStatus) => { - statusBarEntry.changeServerStatus(status, sorbet.lastError); }, - ); - - sorbet.languageClient.onReady().then( - filterUpdatesFromOldClients(() => { - sorbet.languageClient.onNotification( - "sorbet/showOperation", - filterUpdatesFromOldClients( - statusBarEntry.handleShowOperation.bind(statusBarEntry), - ), - ); - }), - ); - } - - context.subscriptions.push( - commands.registerCommand("sorbet.enable", () => { - sorbetExtensionConfig.setEnabled(true); - }), - ); - - context.subscriptions.push( - commands.registerCommand("sorbet.disable", () => { - sorbetExtensionConfig.setEnabled(false); - }), + ), ); - context.subscriptions.push( - commands.registerCommand("sorbet.restart", () => { - restartSorbet(RestartReason.COMMAND); - }), - ); + const statusBarEntry = new SorbetStatusBarEntry(sorbetExtensionContext); + context.subscriptions.push(statusBarEntry); + // Register providers context.subscriptions.push( - commands.registerCommand("sorbet.configure", () => { - new SorbetConfigPicker(sorbetExtensionConfig).show(); - }), - ); - - context.subscriptions.push( - commands.registerCommand("sorbet.showOutput", () => { - outputChannel.show(); + workspace.registerTextDocumentContentProvider("sorbet", { + // URIs are of the form sorbet:[file_path] + provideTextDocumentContent: async (uri: Uri): Promise => { + let content: string; + const { activeLanguageClient } = sorbetExtensionContext.statusProvider; + sorbetExtensionContext.log.info(`Opening sorbet: file. URI:${uri}`); + if (activeLanguageClient) { + const response: TextDocumentItem = await activeLanguageClient.languageClient.sendRequest( + "sorbet/readFile", + { + uri: uri.toString(), + }, + ); + content = response.text; + } else { + sorbetExtensionContext.log.warning( + " > Cannot retrieve file content, no active client.", + ); + content = ""; + } + return content; + }, }), ); - const provider: TextDocumentContentProvider = { - provideTextDocumentContent: async (uri: Uri): Promise => { - // URIs are of the form sorbet:[file_path] - console.log(`Opening sorbet: file at uri ${uri.toString()}`); - if (activeSorbetLanguageClient) { - const response: TextDocumentItem = await activeSorbetLanguageClient.languageClient.sendRequest( - "sorbet/readFile", - { - uri: uri.toString(), - }, - ); - return response.text; - } - return ""; - }, - }; + // Register commands context.subscriptions.push( - workspace.registerTextDocumentContentProvider("sorbet", provider), + commands.registerCommand(cmdIds.SET_LOGLEVEL_COMMAND_ID, () => + new SetLogLevel(sorbetExtensionContext).execute(), + ), + commands.registerCommand(cmdIds.SHOW_ACTIONS_COMMAND_ID, () => + new ShowSorbetActions(sorbetExtensionContext).execute(), + ), + commands.registerCommand(cmdIds.SHOW_CONFIG_PICKER_COMMAND_ID, () => + new ShowSorbetConfigurationPicker(sorbetExtensionContext).execute(), + ), + commands.registerCommand(cmdIds.SHOW_OUTPUT_COMMAND_ID, () => + sorbetExtensionContext.log.outputChannel.show(true), + ), + commands.registerCommand(cmdIds.SORBET_ENABLE_COMMAND_ID, () => + sorbetExtensionContext.configuration.setEnabled(true), + ), + commands.registerCommand(cmdIds.SORBET_DISABLE_COMMAND_ID, () => + sorbetExtensionContext.configuration.setEnabled(false), + ), + commands.registerCommand( + cmdIds.SORBET_RESTART_COMMAND_ID, + (reason: RestartReason = RestartReason.COMMAND) => + sorbetExtensionContext.statusProvider.restartSorbet(reason), + ), + commands.registerCommand("sorbet.toggleHighlightUntyped", () => + sorbetExtensionContext.configuration + .setHighlightUntyped( + !sorbetExtensionContext.configuration.highlightUntyped, + ) + .then(() => + sorbetExtensionContext.statusProvider.restartSorbet( + RestartReason.CONFIG_CHANGE, + ), + ), + ), ); - function handleConfigChange(event: SorbetLspConfigChangeEvent) { - const { oldLspConfig, newLspConfig } = event; - if (oldLspConfig && newLspConfig) { - // Something about the config changed, so restart - restartSorbet(RestartReason.CONFIG_CHANGE); - } else if (oldLspConfig) { - // Stop using Sorbet - stopSorbet(ServerStatus.DISABLED); - } else if (newLspConfig) { - // Start using Sorbet - startSorbet(); - } - } - // Start the extension. - handleConfigChange({ - oldLspConfig: undefined, - newLspConfig: sorbetExtensionConfig.activeLspConfig, - }); + return sorbetExtensionContext.statusProvider.startSorbet(); } diff --git a/vscode_extension/src/LanguageClient.ts b/vscode_extension/src/languageClient.ts similarity index 58% rename from vscode_extension/src/LanguageClient.ts rename to vscode_extension/src/languageClient.ts index af4112321e..62fffbfdbf 100644 --- a/vscode_extension/src/LanguageClient.ts +++ b/vscode_extension/src/languageClient.ts @@ -1,13 +1,5 @@ import { ChildProcess, spawn } from "child_process"; -import { - workspace, - commands, - OutputChannel, - window as vscodeWindow, - env as vscodeEnv, - Uri, - Position, -} from "vscode"; +import { commands, env, Position, Uri, window, workspace } from "vscode"; import { LanguageClient, CloseAction, @@ -19,35 +11,47 @@ import { } from "vscode-languageclient/node"; import { stopProcess } from "./connections"; -import { SorbetExtensionConfig } from "./config"; +import { Tags } from "./metricsClient"; +import { SorbetExtensionContext } from "./sorbetExtensionContext"; import { ServerStatus, RestartReason } from "./types"; -import { emitCountMetric, emitTimingMetric, Tags } from "./veneur"; function nop() {} -const VALID_STATE_TRANSITIONS = new Map>(); -VALID_STATE_TRANSITIONS.set( - ServerStatus.INITIALIZING, - new Set([ServerStatus.ERROR, ServerStatus.RUNNING, ServerStatus.RESTARTING]), -); -VALID_STATE_TRANSITIONS.set( - ServerStatus.RUNNING, - new Set([ServerStatus.ERROR, ServerStatus.RESTARTING]), -); -// Restarting is a terminal state. The restart occurs by terminating this LanguageClient and creating a new one. -VALID_STATE_TRANSITIONS.set(ServerStatus.RESTARTING, new Set([])); -// Error is a terminal state for this class. -VALID_STATE_TRANSITIONS.set(ServerStatus.ERROR, new Set([])); +const VALID_STATE_TRANSITIONS: ReadonlyMap< + ServerStatus, + Set +> = new Map>([ + [ + ServerStatus.INITIALIZING, + new Set([ + ServerStatus.ERROR, + ServerStatus.RESTARTING, + ServerStatus.RUNNING, + ]), + ], + [ + ServerStatus.RUNNING, + new Set([ServerStatus.ERROR, ServerStatus.RESTARTING]), + ], + // Restarting is a terminal state. The restart occurs by terminating this LanguageClient and creating a new one. + [ServerStatus.RESTARTING, new Set()], + // Error is a terminal state for this class. + [ServerStatus.ERROR, new Set()], +]); /** * Shims the language client object so that all requests sent get timed. Exported for tests. */ export function shimLanguageClient( - lc: LanguageClient, - _emitTimingMetric: (metric: string, value: number | Date, tags: Tags) => void, + client: LanguageClient, + emitTimingMetric: (metric: string, value: number | Date, tags: Tags) => void, ) { - const originalSendRequest = lc.sendRequest; - lc.sendRequest = function(this: LanguageClient, method: any, ...args: any[]) { + const originalSendRequest = client.sendRequest; + client.sendRequest = function( + this: LanguageClient, + method: any, + ...args: any[] + ) { const now = new Date(); const requestName = typeof method === "string" ? method : method.method; // Replace some special characters with underscores. @@ -56,103 +60,93 @@ export function shimLanguageClient( const rv = originalSendRequest.apply(this, args as any); const metricName = `latency.${sanitizedRequestName}_ms`; rv.then( - () => { + () => // NOTE: This callback is only called if the request succeeds and was _not_ canceled. // If the request is canceled, the promise is rejected. - _emitTimingMetric(metricName, now, { success: "true" }); - }, - () => { + emitTimingMetric(metricName, now, { success: "true" }), + () => // This callback is called if the request failed or was canceled. - _emitTimingMetric(metricName, now, { success: "false" }); - }, + emitTimingMetric(metricName, now, { success: "false" }), ); return rv; }; } -export default class SorbetLanguageClient implements ErrorHandler { - private _languageClient: LanguageClient; - public get languageClient(): LanguageClient { - return this._languageClient; - } - - private _status = ServerStatus.INITIALIZING; +export class SorbetLanguageClient implements ErrorHandler { + private readonly context: SorbetExtensionContext; + public readonly languageClient: LanguageClient; + private wrappedStatus: ServerStatus; public get status(): ServerStatus { - return this._status; + return this.wrappedStatus; } // If status is ERROR, contains the last error message encountered. - private _lastError: string = ""; + private wrappedLastError: string; public get lastError(): string { - return this._lastError; + return this.wrappedLastError; } // Contains the Sorbet process. - private _sorbetProcess: ChildProcess | null = null; + private sorbetProcess: ChildProcess | null = null; // Note: sometimes this is actually an errno, not a process exit code. // This happens when set via the `.on("error")` handler, instead of the // `.on("exit")` handler. - private _sorbetProcessExitCode: number | null = null; + private sorbetProcessExitCode: number | null = null; // Tracks disposable subscriptions so we can clean them up when language client is disposed. - private _subscriptions: { dispose: () => void }[] = []; + private subscriptions: { dispose: () => void }[] = []; public onStatusChange: (status: ServerStatus) => void = nop; - private _emitCountMetric = emitCountMetric.bind( - null, - this._sorbetExtensionConfig, - this._outputChannel, - ); - - private _emitTimingMetric = emitTimingMetric.bind( - null, - this._sorbetExtensionConfig, - this._outputChannel, - ); - constructor( - private readonly _sorbetExtensionConfig: SorbetExtensionConfig, - private readonly _outputChannel: OutputChannel, - private readonly _restart: (reason: RestartReason) => void, + context: SorbetExtensionContext, + private readonly restart: (reason: RestartReason) => void, ) { + this.context = context; + this.wrappedLastError = ""; + this.wrappedStatus = ServerStatus.INITIALIZING; + // Create the language client and start the client. - this._languageClient = new LanguageClient( + this.languageClient = new LanguageClient( "ruby", "Sorbet", - this._startSorbetProcess.bind(this), + this.startSorbetProcess.bind(this), { documentSelector: [ { language: "ruby", scheme: "file" }, // Support queries on generated files with sorbet:// URIs that do not exist editor-side. { language: "ruby", scheme: "sorbet" }, ], - outputChannel: this._outputChannel, + outputChannel: this.context.log.outputChannel, initializationOptions: { // Opt in to sorbet/showOperation notifications. supportsOperationNotifications: true, // Let Sorbet know that we can handle sorbet:// URIs for generated files. supportsSorbetURIs: true, + highlightUntyped: this.context.configuration.highlightUntyped, }, errorHandler: this, - revealOutputChannelOn: this._sorbetExtensionConfig.revealOutputOnError + revealOutputChannelOn: this.context.configuration.revealOutputOnError ? RevealOutputChannelOn.Error : RevealOutputChannelOn.Never, }, ); - shimLanguageClient(this._languageClient, this._emitTimingMetric); - this._languageClient.onReady().then(() => { + shimLanguageClient(this.languageClient, (metric, value, tags) => + this.context.metrics.emitTimingMetric(metric, value, tags), + ); + + this.languageClient.onReady().then(() => { // Note: It's possible for `onReady` to fire after `stop()` is called on the language client. :( - if (this._status !== ServerStatus.ERROR) { + if (this.status !== ServerStatus.ERROR) { // Language client started successfully. - this._updateStatus(ServerStatus.RUNNING); + this.updateStatus(ServerStatus.RUNNING); } - const caps: any = this._languageClient.initializeResult?.capabilities; + const caps: any = this.languageClient.initializeResult?.capabilities; if (caps.sorbetShowSymbolProvider) { - this._subscriptions.push( + this.subscriptions.push( commands.registerCommand("sorbet.copySymbolToClipboard", async () => { - const editor = vscodeWindow.activeTextEditor; + const editor = window.activeTextEditor; if (!editor) { return; } @@ -168,14 +162,16 @@ export default class SorbetLanguageClient implements ErrorHandler { }, position, }; - const response: SymbolInformation = await this._languageClient.sendRequest( + const response: SymbolInformation = await this.languageClient.sendRequest( "sorbet/showSymbol", params, ); - await vscodeEnv.clipboard.writeText(response.name); + await env.clipboard.writeText(response.name); - console.log(`Copied ${response.name} to the clipboard.`); + this.context.log.debug( + `Copied symbol name to the clipboard. Name:${response.name}`, + ); }), ); } @@ -183,7 +179,7 @@ export default class SorbetLanguageClient implements ErrorHandler { // Unfortunately, we need this command as a wrapper around `editor.action.rename`, // because VSCode doesn't allow calling it from the JSON RPC // https://github.com/microsoft/vscode/issues/146767 - this._subscriptions.push( + this.subscriptions.push( commands.registerCommand( "sorbet.rename", (params: TextDocumentPositionParams) => { @@ -193,15 +189,16 @@ export default class SorbetLanguageClient implements ErrorHandler { new Position(params.position.line, params.position.character), ]); } catch (error) { - console.log( - `Failed to rename symbol at ${params.textDocument.uri}:${params.position.line}:${params.position.character}, ${error}`, + this.context.log.error( + `Failed to rename symbol at ${params.textDocument.uri}:${params.position.line}:${params.position.character}`, + error instanceof Error ? error : undefined, ); } }, ), ); }); - this._subscriptions.push(this._languageClient.start()); + this.subscriptions.push(this.languageClient.start()); } /** @@ -209,8 +206,8 @@ export default class SorbetLanguageClient implements ErrorHandler { * to keep it alive. Stops the language server and Sorbet processes, and removes UI items. */ public dispose() { - this._subscriptions.forEach((s) => s.dispose()); - this._subscriptions = []; + this.subscriptions.forEach((s) => s.dispose()); + this.subscriptions = []; let stopped = false; /* @@ -226,16 +223,16 @@ export default class SorbetLanguageClient implements ErrorHandler { */ const stopTimer = setTimeout(() => { stopped = true; - this._emitCountMetric("stop.timed_out", 1); - stopProcess(this._sorbetProcess); - this._sorbetProcess = null; + this.context.metrics.emitCountMetric("stop.timed_out", 1); + stopProcess(this.sorbetProcess, this.context.log); + this.sorbetProcess = null; }, 5000); - this._languageClient.stop().then(() => { + this.languageClient.stop().then(() => { if (!stopped) { clearTimeout(stopTimer); - this._emitCountMetric("stop.success", 1); - this._outputChannel.appendLine("Sorbet has stopped."); + this.context.metrics.emitCountMetric("stop.success", 1); + this.context.log.info("Sorbet has stopped."); } }); } @@ -243,19 +240,19 @@ export default class SorbetLanguageClient implements ErrorHandler { /** * Updates the language client's server status. Verifies that the transition is legal. */ - private _updateStatus(newStatus: ServerStatus) { - if (this._status === newStatus) { + private updateStatus(newStatus: ServerStatus) { + if (this.status === newStatus) { return; } - this._assertValid(this._status, newStatus); - this._status = newStatus; + this.assertValid(this.status, newStatus); + this.wrappedStatus = newStatus; this.onStatusChange(newStatus); } - private _assertValid(from: ServerStatus, to: ServerStatus) { + private assertValid(from: ServerStatus, to: ServerStatus) { const set = VALID_STATE_TRANSITIONS.get(from); if (!set || !set.has(to)) { - this._outputChannel.appendLine( + this.context.log.error( `Invalid Sorbet server transition: ${from} => ${to}`, ); } @@ -264,58 +261,56 @@ export default class SorbetLanguageClient implements ErrorHandler { /** * Runs a Sorbet process using the current active configuration. Debounced so that it runs Sorbet at most every 3 seconds. */ - private _startSorbetProcess(): Promise { - this._updateStatus(ServerStatus.INITIALIZING); - this._outputChannel.appendLine("Running Sorbet LSP with:"); - const [ - command, - ...args - ] = this._sorbetExtensionConfig.activeLspConfig!.command; - this._outputChannel.appendLine(` ${command} ${args.join(" ")}`); - this._sorbetProcess = spawn(command, args, { + private startSorbetProcess(): Promise { + this.updateStatus(ServerStatus.INITIALIZING); + this.context.log.info("Running Sorbet LSP."); + const [command, ...args] = + this.context.configuration.activeLspConfig?.command ?? []; + this.context.log.debug(` > ${command} ${args.join(" ")}`); + this.sorbetProcess = spawn(command, args, { cwd: workspace.rootPath, }); // N.B.: 'exit' is sometimes not invoked if the process exits with an error/fails to start, as per the Node.js docs. // So, we need to handle both events. ¯\_(ツ)_/¯ - this._sorbetProcess.on( + this.sorbetProcess.on( "exit", (code: number | null, _signal: string | null) => { - this._sorbetProcessExitCode = code; + this.sorbetProcessExitCode = code; }, ); - this._sorbetProcess.on("error", (err?: NodeJS.ErrnoException) => { + this.sorbetProcess.on("error", (err?: NodeJS.ErrnoException) => { if ( err && - this._status === ServerStatus.INITIALIZING && + this.status === ServerStatus.INITIALIZING && err.code === "ENOENT" ) { - this._emitCountMetric("error.enoent", 1); + this.context.metrics.emitCountMetric("error.enoent", 1); // We failed to start the process. The path to Sorbet is likely incorrect. - this._lastError = `Could not start Sorbet with command: '${command} ${args.join( + this.wrappedLastError = `Could not start Sorbet with command: '${command} ${args.join( " ", )}'. Encountered error '${ err.message }'. Is the path to Sorbet correct?`; - this._updateStatus(ServerStatus.ERROR); + this.updateStatus(ServerStatus.ERROR); } - this._sorbetProcess = null; - this._sorbetProcessExitCode = err?.errno ?? null; + this.sorbetProcess = null; + this.sorbetProcessExitCode = err?.errno ?? null; }); - return Promise.resolve(this._sorbetProcess); + return Promise.resolve(this.sorbetProcess); } /** ErrorHandler interface */ /** - * LanguageClient has built-in restart capabilities, but it's broken: + * LanguageClient has built-in restart capabilities but if it's broken: * * It drops all `onNotification` subscriptions after restarting, so we'll miss ShowNotification updates. * * It drops all `onReady` subscriptions after restarting, so we won't know when the Sorbet server is running. * * It doesn't reset `onReady` state, so we can't even reset our `onReady` callback. */ public error(): ErrorAction { - if (this._status !== ServerStatus.ERROR) { - this._updateStatus(ServerStatus.RESTARTING); - this._restart(RestartReason.CRASH_LC_ERROR); + if (this.status !== ServerStatus.ERROR) { + this.updateStatus(ServerStatus.RESTARTING); + this.restart(RestartReason.CRASH_LC_ERROR); } return ErrorAction.Shutdown; } @@ -324,10 +319,10 @@ export default class SorbetLanguageClient implements ErrorHandler { * Note: If the VPN is disconnected, then Sorbet will repeatedly fail to start. */ public closed(): CloseAction { - if (this._status !== ServerStatus.ERROR) { - this._updateStatus(ServerStatus.RESTARTING); + if (this.status !== ServerStatus.ERROR) { + this.updateStatus(ServerStatus.RESTARTING); let reason = RestartReason.CRASH_LC_CLOSED; - if (this._sorbetProcessExitCode === 11) { + if (this.sorbetProcessExitCode === 11) { // 11 number chosen somewhat arbitrarily. Most important is that this doesn't // clobber the exit code of Sorbet itself (which means Sorbet cannot return 11). // @@ -335,11 +330,11 @@ export default class SorbetLanguageClient implements ErrorHandler { // wrapper scripts that people use with Sorbet. If this number has to // change for some reason, we should announce that. reason = RestartReason.WRAPPER_REFUSED_SPAWN; - } else if (this._sorbetProcessExitCode === 143) { + } else if (this.sorbetProcessExitCode === 143) { // 143 = 128 + 15 and 15 is TERM signal reason = RestartReason.FORCIBLY_TERMINATED; } - this._restart(reason); + this.restart(reason); } return CloseAction.DoNotRestart; } diff --git a/vscode_extension/src/log.ts b/vscode_extension/src/log.ts new file mode 100644 index 0000000000..2a1def6cb2 --- /dev/null +++ b/vscode_extension/src/log.ts @@ -0,0 +1,157 @@ +import { Disposable, OutputChannel, window } from "vscode"; + +/** + * The severity level of a log message. + * Based on $/src/vs/vscode.proposed.d.ts + */ +export enum LogLevel { + Trace = 1, + Debug = 2, + Info = 3, + Warning = 4, + Error = 5, + Critical = 6, + Off = 7, +} +/** + * Environment variable defining log level. + */ +export const VSCODE_SORBETEXT_LOG_LEVEL = "VSCODE_SORBETEXT_LOG_LEVEL"; + +/** + * Get log-level as defined in env, otherwise returns `defaultLevel`. + * @param name Environment variable name. + * @param defaultLevel Default value if environment does not define a valid one. + */ +export function getLogLevelFromEnvironment( + name: string = VSCODE_SORBETEXT_LOG_LEVEL, + defaultLevel: LogLevel = LogLevel.Info, +): LogLevel { + let logLevel = defaultLevel; + const envLogLevel = process.env[name]?.trim(); + if (envLogLevel) { + const parsedLogLevel = getLogLevelFromString(envLogLevel); + if (parsedLogLevel !== undefined) { + logLevel = parsedLogLevel; + } + } + return logLevel; +} + +/** + * Get `LogLevel` from name, case-insensitively. + * @param name Log level name. + */ +export function getLogLevelFromString(name: string): LogLevel | undefined { + const upcaseLevel = name.toUpperCase(); + const entry = Object.entries(LogLevel).find( + (e) => e[0].toUpperCase() === upcaseLevel, + ); + return entry && entry[1]; +} + +/** + * Output Channel-based implementation of logger. + */ +export class OutputChannelLog implements Disposable { + private wrappedLevel: LogLevel; + public readonly outputChannel: OutputChannel; + + constructor(name: string, level: LogLevel = LogLevel.Info) { + this.wrappedLevel = level; + // Future: + // - VSCode 1.66 allows to pass-in a `language` to support syntax coloring. + // - VSCode 1.75 allows to create a `LogOutputChannel`. + this.outputChannel = window.createOutputChannel(name); + } + + private appendLine(level: string, message: string): void { + const formattedMessage = `${new Date().toISOString()} [${level.toLowerCase()}] ${message}`.trim(); + this.outputChannel.appendLine(formattedMessage); + } + + /** + * Appends a new debug message to the log. + * @param message Log message. + */ + public debug(message: string): void { + if (this.level <= LogLevel.Debug) { + this.appendLine("Debug", message); + } + } + + /** + * Dispose and free associated resources. + */ + public dispose() { + this.outputChannel.dispose(); + } + + /** + * Appends a new error message to the log. + * @param errorOrMessage Error or log message. + * @param error Error (only used when `errorOrMessage` is not a `string`). + */ + public error(errorOrMessage: string | Error, error?: Error): void { + if (this.level <= LogLevel.Error) { + let message: string; + if (typeof errorOrMessage === "string") { + message = errorOrMessage; + if (error) { + message += ` Error: ${err2Str(error)}`; + } + } else { + message = err2Str(errorOrMessage); + } + this.appendLine("Error", message); + } + + function err2Str(err: Error) { + return err.message || err.name || `\n${err.stack || ""}`; + } + } + + /** + * Appends a new information message to the log. + * @param message Log message. + */ + public info(message: string): void { + if (this.level <= LogLevel.Info) { + this.appendLine("Info", message); + } + } + + /** + * Log level. + */ + public get level(): LogLevel { + return this.wrappedLevel; + } + + public set level(level: LogLevel) { + if (this.wrappedLevel !== level) { + this.wrappedLevel = level; + this.outputChannel.appendLine(`Log level changed to: ${LogLevel[level]}`); + } + } + + /** + * Appends a new trace message to the log. + * @param message Log message. + */ + public trace(message: string): void { + if (this.level <= LogLevel.Trace) { + this.appendLine("Trace", message); + } + } + + /** + * Appends a new warning message to the log. + * @param message Log message. + */ + public warning(message: string): void { + if (this.level <= LogLevel.Warning) { + this.appendLine("Warning", message); + } + } +} diff --git a/vscode_extension/src/metricsClient.ts b/vscode_extension/src/metricsClient.ts new file mode 100644 index 0000000000..247e85be51 --- /dev/null +++ b/vscode_extension/src/metricsClient.ts @@ -0,0 +1,166 @@ +import { extensions, commands } from "vscode"; +import { SorbetExtensionContext } from "./sorbetExtensionContext"; + +export const METRIC_PREFIX = "ruby_typer.lsp.extension."; + +/** + * Exported by the `sorbet-internal.metrics` extension + */ +export type Tags = Readonly<{ [metric: string]: string }>; + +export interface MetricsEmitter { + /** Increments the given counter metric by the given count (default: 1 if unspecified). */ + increment(metricName: string, count?: number, tags?: Tags): Promise; + + /** Sets the given gauge metric to the given value. */ + gauge(metricName: string, value: number, tags?: Tags): Promise; + + /** + * Records a runtime for the specific metric. Is not present on older versions of metrics extension. + * TODO(jvilk): Make non-optional once new version is ~100% rolled out. + */ + timing?(metricName: string, value: number | Date, tags?: Tags): Promise; + + /** Emits any previously-unsent metrics. */ + flush(): Promise; +} + +export interface Api { + readonly metricsEmitter: MetricsEmitter; +} + +class NoOpMetricsEmitter implements MetricsEmitter { + async increment( + _metricName: string, + _count?: number, + _tags?: Tags, + ): Promise {} // eslint-disable-line no-empty-function + + async gauge( + _metricName: string, + _value: number, + _tags?: Tags, + ): Promise {} // eslint-disable-line no-empty-function + + async timing( + _metricName: string, + _value: number | Date, + _tags?: Tags, + ): Promise {} // eslint-disable-line no-empty-function + + async flush(): Promise {} // eslint-disable-line no-empty-function +} + +class NoOpApi implements Api { + readonly metricsEmitter: MetricsEmitter = new NoOpMetricsEmitter(); + static INSTANCE = new NoOpApi(); +} + +export class MetricClient { + private apiPromise: Promise; + private readonly context: SorbetExtensionContext; + private readonly sorbetExtensionVersion: string; + + constructor(context: SorbetExtensionContext) { + this.apiPromise = this.initSorbetMetricsApi(); + this.context = context; + const sorbetExtension = extensions.getExtension("sorbet-vscode-extension"); + this.sorbetExtensionVersion = + sorbetExtension?.packageJSON.version ?? "unknown"; + } + + /** + * Build a tag set. + * @param tags Tags to add to, or override, default ones. + * @returns Tag set. + */ + private buildTags(tags: Tags) { + return { + config_id: this.context.configuration.activeLspConfig?.id ?? "disabled", + sorbet_extension_version: this.sorbetExtensionVersion, + ...tags, + }; + } + + private async initSorbetMetricsApi(): Promise { + let sorbetMetricsApi: Api; + try { + const api = await commands.executeCommand( + "sorbet.metrics.getExportedApi", + ); + if (api) { + this.context.log.info("Metrics-gathering initialized."); + sorbetMetricsApi = api as Api; + if (!sorbetMetricsApi.metricsEmitter.timing) { + this.context.log.info("Timer metrics disabled (unsupported API)."); + } + } else { + this.context.log.info("Metrics-gathering disabled (no API)"); + sorbetMetricsApi = NoOpApi.INSTANCE; + } + } catch (reason) { + sorbetMetricsApi = NoOpApi.INSTANCE; + const adjustedReason = + (reason)?.message === + "command 'sorbet.metrics.getExportedApi' not found" + ? "Define the 'sorbet.metrics.getExportedApi' command to enable metrics gathering" + : (reason).message; + + this.context.log.error( + `Metrics-gathering disabled (error): ${adjustedReason}`, + ); + } + return sorbetMetricsApi; + } + + /** + * Emit a count metric. + * @param metric Metric name. + * @param count Metric count. + * @param extraTags Tags to attach to metric. + */ + public async emitCountMetric( + metric: string, + count: number, + extraTags: Tags = {}, + ): Promise { + const api = await this.apiPromise; + if (!api) { + return; + } + + const fullName = `${METRIC_PREFIX}${metric}`; + const tags = this.buildTags(extraTags); + api.metricsEmitter.increment(fullName, count, tags); + } + + /** + * Emit a time metric. + * @param metric Metric name. + * @param time Time. + * @param extraTags Tags to attach to metric. + */ + public async emitTimingMetric( + metric: string, + time: number | Date, + extraTags: Tags = {}, + ): Promise { + const api = await this.apiPromise; + if (!api?.metricsEmitter.timing) { + // Ignore timers if metrics extension does not support them. + return; + } + + const fullName = `${METRIC_PREFIX}${metric}`; + const tags = this.buildTags(extraTags); + api.metricsEmitter.timing(fullName, time, tags); + } + + /** + * Set {@link API} instance to use. This is intended for tests only. + * @param api API instance + */ + public setSorbetMetricsApi(api: Api): void { + this.apiPromise = Promise.resolve(api); + } +} diff --git a/vscode_extension/src/sorbetExtensionContext.ts b/vscode_extension/src/sorbetExtensionContext.ts new file mode 100644 index 0000000000..f213933ddb --- /dev/null +++ b/vscode_extension/src/sorbetExtensionContext.ts @@ -0,0 +1,37 @@ +import { Disposable, ExtensionContext } from "vscode"; +import { DefaultSorbetWorkspaceContext, SorbetExtensionConfig } from "./config"; +import { OutputChannelLog } from "./log"; +import { MetricClient } from "./metricsClient"; +import { SorbetStatusProvider } from "./sorbetStatusProvider"; + +export class SorbetExtensionContext implements Disposable { + public readonly configuration: SorbetExtensionConfig; + private readonly disposable: Disposable; + public readonly extensionContext: ExtensionContext; + public readonly log: OutputChannelLog; + public readonly metrics: MetricClient; + public readonly statusProvider: SorbetStatusProvider; + + constructor(context: ExtensionContext) { + this.configuration = new SorbetExtensionConfig( + new DefaultSorbetWorkspaceContext(context), + ); + this.extensionContext = context; + this.log = new OutputChannelLog("Sorbet"); + this.metrics = new MetricClient(this); + this.statusProvider = new SorbetStatusProvider(this); + + this.disposable = Disposable.from( + this.configuration, + this.log, + this.statusProvider, + ); + } + + /** + * Dispose and free associated resources. + */ + public dispose() { + this.disposable.dispose(); + } +} diff --git a/vscode_extension/src/sorbetStatusBarEntry.ts b/vscode_extension/src/sorbetStatusBarEntry.ts new file mode 100644 index 0000000000..e275556d68 --- /dev/null +++ b/vscode_extension/src/sorbetStatusBarEntry.ts @@ -0,0 +1,139 @@ +import * as Spinner from "elegant-spinner"; +import { Disposable, StatusBarAlignment, StatusBarItem, window } from "vscode"; + +import { SHOW_ACTIONS_COMMAND_ID } from "./commandIds"; +import { SorbetExtensionContext } from "./sorbetExtensionContext"; +import { StatusChangedEvent } from "./sorbetStatusProvider"; +import { ShowOperationParams, ServerStatus, RestartReason } from "./types"; + +export class SorbetStatusBarEntry implements Disposable { + private readonly context: SorbetExtensionContext; + private readonly disposable: Disposable; + private operationStack: ShowOperationParams[]; + private serverStatus: ServerStatus; + private readonly spinner: () => string; + private spinnerTimer?: NodeJS.Timer; + private readonly statusBarItem: StatusBarItem; + + constructor(context: SorbetExtensionContext) { + this.context = context; + this.operationStack = []; + this.serverStatus = ServerStatus.DISABLED; + this.spinner = Spinner(); + this.statusBarItem = window.createStatusBarItem( + StatusBarAlignment.Left, + 10, + ); + this.statusBarItem.command = SHOW_ACTIONS_COMMAND_ID; + + this.disposable = Disposable.from( + this.context.configuration.onLspConfigChange(() => this.render()), + this.context.statusProvider.onStatusChanged((e) => + this.onServerStatusChanged(e), + ), + this.context.statusProvider.onShowOperation((params) => + this.onServerShowOperation(params), + ), + this.statusBarItem, + ); + + this.render(); + this.statusBarItem.show(); + } + + /** + * Dispose and free associated resources. + */ + public dispose() { + this.disposable.dispose(); + } + + private async onServerStatusChanged(e: StatusChangedEvent): Promise { + const isError = + this.serverStatus !== e.status && e.status === ServerStatus.ERROR; + this.serverStatus = e.status; + if (e.stopped) { + this.operationStack = []; + } + this.render(); + if (isError) { + await this.context.statusProvider.restartSorbet( + RestartReason.CRASH_EXT_ERROR, + ); + } + } + + private onServerShowOperation(p: ShowOperationParams) { + if (p.status === "end") { + this.operationStack = this.operationStack.filter( + (otherP) => otherP.operationName !== p.operationName, + ); + } else { + this.operationStack.push(p); + } + this.render(); + } + + private getSpinner() { + if (this.spinnerTimer) { + clearTimeout(this.spinnerTimer); + } + // Animate the spinner with setTimeout. + this.spinnerTimer = setTimeout(() => this.render(), 250); + return this.spinner(); + } + + private render() { + const numOperations = this.operationStack.length; + const { activeLspConfig } = this.context.configuration; + const sorbetName = activeLspConfig?.name ?? "Sorbet"; + + let text: string; + let tooltip: string; + // Errors should suppress operation animations / feedback. + if ( + activeLspConfig && + this.serverStatus !== ServerStatus.ERROR && + numOperations > 0 + ) { + const latestOp = this.operationStack[numOperations - 1]; + text = `${sorbetName}: ${latestOp.description} ${this.getSpinner()}`; + tooltip = latestOp.description; + } else { + switch (this.serverStatus) { + case ServerStatus.DISABLED: + text = `${sorbetName}: Disabled`; + tooltip = "The Sorbet server is disabled."; + break; + case ServerStatus.ERROR: + text = `${sorbetName}: Error`; + tooltip = "Click for remediation items."; + const { serverError } = this.context.statusProvider; + if (serverError) { + tooltip = `${serverError}\n${tooltip}`; + } + break; + case ServerStatus.INITIALIZING: + text = `${sorbetName}: Initializing ${this.getSpinner()}`; + tooltip = "The Sorbet server is initializing."; + break; + case ServerStatus.RESTARTING: + text = `${sorbetName}: Restarting ${this.getSpinner()}`; + tooltip = "The Sorbet server is restarting."; + break; + case ServerStatus.RUNNING: + text = `${sorbetName}: Idle`; + tooltip = "The Sorbet server is currently running."; + break; + default: + this.context.log.error(`Invalid ServerStatus: ${this.serverStatus}`); + text = ""; + tooltip = ""; + break; + } + } + + this.statusBarItem.text = text; + this.statusBarItem.tooltip = tooltip; + } +} diff --git a/vscode_extension/src/sorbetStatusProvider.ts b/vscode_extension/src/sorbetStatusProvider.ts new file mode 100644 index 0000000000..cb8311333e --- /dev/null +++ b/vscode_extension/src/sorbetStatusProvider.ts @@ -0,0 +1,185 @@ +import { Disposable, Event, EventEmitter } from "vscode"; +import { SorbetLanguageClient } from "./languageClient"; +import { SorbetExtensionContext } from "./sorbetExtensionContext"; +import { RestartReason, ServerStatus, ShowOperationParams } from "./types"; + +const MIN_TIME_BETWEEN_RETRIES_MS = 7000; + +export type StatusChangedEvent = { + status: ServerStatus; + stopped?: true; + error?: string; +}; + +export class SorbetStatusProvider implements Disposable { + private wrappedActiveLanguageClient?: SorbetLanguageClient; + private readonly context: SorbetExtensionContext; + private readonly disposables: Disposable[]; + /** Mutex for startSorbet. Prevents us from starting multiple processes at once. */ + private isStarting: boolean; + private lastSorbetRetryTime: number; + private readonly onShowOperationEmitter: EventEmitter; + private readonly onStatusChangedEmitter: EventEmitter; + + constructor(context: SorbetExtensionContext) { + this.context = context; + this.isStarting = false; + this.lastSorbetRetryTime = 0; + this.onShowOperationEmitter = new EventEmitter(); + this.onStatusChangedEmitter = new EventEmitter(); + + this.disposables = [ + this.onShowOperationEmitter, + this.onStatusChangedEmitter, + ]; + } + + /** + * Dispose and free associated resources. + */ + public dispose() { + Disposable.from(...this.disposables).dispose(); + } + + /** + * Current Sorbet client, if any. + */ + public get activeLanguageClient(): SorbetLanguageClient | undefined { + return this.wrappedActiveLanguageClient; + } + + private set activeLanguageClient(value: SorbetLanguageClient | undefined) { + if (this.wrappedActiveLanguageClient === value) { + return; + } + + // Clean-up existing client, if any. + if (this.wrappedActiveLanguageClient) { + this.wrappedActiveLanguageClient.dispose(); + const i = this.disposables.indexOf(this.wrappedActiveLanguageClient); + if (i !== -1) { + this.disposables.splice(i, 1); + } + } + + // Hook-up new client for clean-up, if any. + if (value) { + const i = this.disposables.indexOf(value); + if (i === -1) { + this.disposables.push(value); + } + } + + this.wrappedActiveLanguageClient = value; + + // State might have changed based on new client. + if (this.wrappedActiveLanguageClient) { + this.onStatusChangedEmitter.fire({ + status: this.wrappedActiveLanguageClient.status, + error: this.wrappedActiveLanguageClient.lastError, + }); + } + } + + /** + * Event raised on a {@link ShowOperationParams show-operation} event. + */ + public get onShowOperation(): Event { + return this.onShowOperationEmitter.event; + } + + /** + * Event raised on {@link ServerStatus status} changes. + */ + public get onStatusChanged(): Event { + return this.onStatusChangedEmitter.event; + } + + /** + * Restart Sorbet. + * @param reason Telemetry reason. + */ + public async restartSorbet(reason: RestartReason): Promise { + await this.stopSorbet(ServerStatus.RESTARTING); + // `reason` is an enum type with a small and finite number of values. + this.context.metrics.emitCountMetric(`restart.${reason}`, 1); + await this.startSorbet(); + } + + /** + * Error information, if {@link serverStatus} is {@link ServerStatus.ERROR} + */ + public get serverError(): string | undefined { + return this.activeLanguageClient?.lastError; + } + + /** + * Return current {@link ServerStatus server status}. + */ + public get serverStatus(): ServerStatus { + return this.activeLanguageClient?.status || ServerStatus.DISABLED; + } + + /** + * Start Sorbet. + */ + public async startSorbet(): Promise { + if (this.isStarting) { + return; + } + + // Debounce by MIN_TIME_BETWEEN_RETRIES_MS. Returns 0 if the calculated time to sleep is negative. + const sleepMS = + MIN_TIME_BETWEEN_RETRIES_MS - (Date.now() - this.lastSorbetRetryTime); + if (sleepMS > 0) { + // Wait timeToSleep ms. Use mutex, as this yields the event loop for future events. + this.context.log.debug( + `Waiting ${sleepMS.toFixed(0)}ms before restarting Sorbet…`, + ); + this.isStarting = true; + await new Promise((res) => setTimeout(res, sleepMS)); + this.isStarting = false; + } + this.lastSorbetRetryTime = Date.now(); + + // Create client + const newClient = new SorbetLanguageClient( + this.context, + (reason: RestartReason) => this.restartSorbet(reason), + ); + // Use property-setter to ensure proper setup. + this.activeLanguageClient = newClient; + + newClient.onStatusChange = (status: ServerStatus) => { + // Ignore event if this is not the current client (e.g. old client being shut down). + if (this.activeLanguageClient === newClient) { + this.onStatusChangedEmitter.fire({ + status, + error: newClient.lastError, + }); + } + }; + + // Wait for `ready` before accessing `languageClient`. + await newClient.languageClient.onReady(); + newClient.languageClient.onNotification( + "sorbet/showOperation", + (params: ShowOperationParams) => { + // Ignore event if this is not the current client (e.g. old client being shut down). + if (this.activeLanguageClient === newClient) { + this.onShowOperationEmitter.fire(params); + } + }, + ); + } + + /** + * Stop Sorbet. + * @param newStatus Status to report. + */ + public async stopSorbet(newStatus: ServerStatus): Promise { + // Use property-setter to ensure proper clean-up. + this.activeLanguageClient = undefined; + this.onStatusChangedEmitter.fire({ status: newStatus, stopped: true }); + } +} diff --git a/vscode_extension/src/test/commands/setLogLevel.test.ts b/vscode_extension/src/test/commands/setLogLevel.test.ts new file mode 100644 index 0000000000..787c998483 --- /dev/null +++ b/vscode_extension/src/test/commands/setLogLevel.test.ts @@ -0,0 +1,106 @@ +import * as vscode from "vscode"; +import * as assert from "assert"; +import * as path from "path"; +import * as sinon from "sinon"; + +import { LogLevelQuickPickItem, SetLogLevel } from "../../commands/setLogLevel"; +import { LogLevel, OutputChannelLog } from "../../log"; +import { SorbetExtensionContext } from "../../sorbetExtensionContext"; + +suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { + let testRestorables: { restore: () => void }[]; + + setup(() => { + testRestorables = []; + }); + + teardown(() => { + testRestorables.forEach((r) => r.restore()); + }); + + test("Shows dropdown when target-level argument is NOT provided", async () => { + const expectedLogLevel = LogLevel.Warning; + const createOutputChannelStub = sinon + .stub(vscode.window, "createOutputChannel") + .returns({ + appendLine(_value: string) {}, + }); + testRestorables.push(createOutputChannelStub); + + const showQuickPickSingleStub = sinon + .stub(vscode.window, "showQuickPick") + .resolves({ + label: LogLevel[expectedLogLevel], + level: expectedLogLevel, + }); + testRestorables.push(showQuickPickSingleStub); + + const log = new OutputChannelLog("Test", LogLevel.Info); + const context = { log }; + + const command = new SetLogLevel(context); + await assert.doesNotReject(command.execute()); + assert.strictEqual(log.level, expectedLogLevel); + + sinon.assert.calledWithExactly( + showQuickPickSingleStub, + >[ + { + level: LogLevel.Trace, + label: "Trace", + }, + { + level: LogLevel.Debug, + label: "Debug", + }, + { + level: LogLevel.Info, + label: "• Info", + }, + { + level: LogLevel.Warning, + label: "Warning", + }, + { + level: LogLevel.Error, + label: "Error", + }, + { + level: LogLevel.Critical, + label: "Critical", + }, + { + level: LogLevel.Off, + label: "Off", + }, + ], + { + placeHolder: "Select log level", + }, + ); + sinon.assert.calledOnce(createOutputChannelStub); + }); + + test("Shows no-dropdown when target-level argument is provided ", async () => { + const expectedLogLevel = LogLevel.Warning; + const createOutputChannelStub = sinon + .stub(vscode.window, "createOutputChannel") + .returns({ + appendLine(_value: string) {}, + }); + testRestorables.push(createOutputChannelStub); + + const showQuickPickSingleStub = sinon.stub(vscode.window, "showQuickPick"); + testRestorables.push(showQuickPickSingleStub); + + const log = new OutputChannelLog("Test", LogLevel.Info); + const context = { log }; + + const command = new SetLogLevel(context); + await assert.doesNotReject(command.execute(expectedLogLevel)); + assert.strictEqual(log.level, expectedLogLevel); + + sinon.assert.notCalled(showQuickPickSingleStub); + sinon.assert.calledOnce(createOutputChannelStub); + }); +}); diff --git a/vscode_extension/src/test/config.test.ts b/vscode_extension/src/test/config.test.ts index 04e4ae3167..3ece269e11 100644 --- a/vscode_extension/src/test/config.test.ts +++ b/vscode_extension/src/test/config.test.ts @@ -1,5 +1,4 @@ import * as assert from "assert"; -import { isEqual } from "lodash"; import * as sinon from "sinon"; import { EventEmitter, @@ -21,12 +20,17 @@ import { /** Imitate the WorkspaceConfiguration. */ class FakeWorkspaceConfiguration implements ISorbetWorkspaceContext { - public _emitter = new EventEmitter(); - public backingStore: Map; - public defaults: Map; - constructor(public properties: Iterable<[String, any]> = []) { - this.backingStore = new Map(properties); + public readonly backingStore: Map; + public readonly defaults: Map; + private readonly configurationChangeEmitter: EventEmitter< + ConfigurationChangeEvent + >; + constructor(properties: Iterable<[String, any]> = []) { + this.backingStore = new Map(properties); + this.configurationChangeEmitter = new EventEmitter< + ConfigurationChangeEvent + >(); const defaultProperties = extensions.getExtension( "sorbet.sorbet-vscode-extension", )!.packageJSON.contributes.configuration.properties; @@ -69,7 +73,7 @@ class FakeWorkspaceConfiguration implements ISorbetWorkspaceContext { } this.backingStore.set(section, value); return Promise.resolve( - this._emitter.fire({ + this.configurationChangeEmitter.fire({ affectsConfiguration: (s: string, _?: Uri) => { return section.startsWith(`${s}.`); }, @@ -78,7 +82,7 @@ class FakeWorkspaceConfiguration implements ISorbetWorkspaceContext { } get onDidChangeConfiguration() { - return this._emitter.event; + return this.configurationChangeEmitter.event; } workspaceFolders() { @@ -226,16 +230,6 @@ suite("SorbetLspConfig", () => { ); }); }); - test("using lodash.isEqual()", () => { - assert.notEqual(config1, config2, "Should not be identical"); - assert.ok( - isEqual(config1, config2), - `Should be deeply equal to ${config2}`, - ); - differentConfigs.forEach((c) => { - assert.ok(!isEqual(c, config1), `Should not be deeply equal to ${c}`); - }); - }); }); }); diff --git a/vscode_extension/src/test/LanguageClient.test.ts b/vscode_extension/src/test/languageClient.test.ts similarity index 94% rename from vscode_extension/src/test/LanguageClient.test.ts rename to vscode_extension/src/test/languageClient.test.ts index 7f32f17065..309ceb0f08 100644 --- a/vscode_extension/src/test/LanguageClient.test.ts +++ b/vscode_extension/src/test/languageClient.test.ts @@ -6,9 +6,9 @@ import { } from "vscode-languageclient/node"; import { RequestType } from "vscode-languageserver-protocol"; import * as assert from "assert"; -import { shimLanguageClient } from "../LanguageClient"; -import TestLanguageServerSpecialURIs from "./TestLanguageServerSpecialURIs"; -import { setSorbetMetricsApi, Tags, MetricsEmitter } from "../veneur"; +import { shimLanguageClient } from "../languageClient"; +import { TestLanguageServerSpecialURIs } from "./testLanguageServerSpecialURIs"; +import { MetricsEmitter, Tags } from "../metricsClient"; const enum MetricType { Increment, @@ -58,7 +58,7 @@ class RecordingMetricsEmitter implements MetricsEmitter { function createLanguageClient(): LanguageClient { // The server is implemented in node - const serverModule = require.resolve("./TestLanguageServer"); + const serverModule = require.resolve("./testLanguageServer"); // The debug options for the server const debugOptions = { execArgv: [] }; @@ -98,7 +98,6 @@ suite("LanguageClient", () => { suite("Metrics", () => { suiteSetup(() => { metricsEmitter = new RecordingMetricsEmitter(); - setSorbetMetricsApi({ metricsEmitter }); }); test("Shims language clients and records latency metrics", async () => { const client = createLanguageClient(); diff --git a/vscode_extension/src/test/log.test.ts b/vscode_extension/src/test/log.test.ts new file mode 100644 index 0000000000..27e1d1eba9 --- /dev/null +++ b/vscode_extension/src/test/log.test.ts @@ -0,0 +1,196 @@ +import * as vscode from "vscode"; +import * as assert from "assert"; +import * as path from "path"; +import * as sinon from "sinon"; + +import { + getLogLevelFromEnvironment, + getLogLevelFromString, + LogLevel, + OutputChannelLog, + VSCODE_SORBETEXT_LOG_LEVEL, +} from "../log"; + +suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { + let testRestorables: { restore: () => void }[]; + + setup(() => { + testRestorables = []; + }); + + teardown(() => { + testRestorables.forEach((r) => r.restore()); + }); + + test("getLogLevelFromEnvironment", () => { + const restorableValue = process.env[VSCODE_SORBETEXT_LOG_LEVEL]; + const restoreProcessEnv = { + restore: () => { + if (restorableValue !== undefined) { + process.env[VSCODE_SORBETEXT_LOG_LEVEL] = restorableValue; + } else { + delete process.env.VSCODE_PAYEXT_LOG_LEVEL; + } + }, + }; + testRestorables.push(restoreProcessEnv); + + assert.strictEqual( + LogLevel.Info, + getLogLevelFromEnvironment(), + "Defaults to LogLevel.Info when undefined", + ); + + process.env[VSCODE_SORBETEXT_LOG_LEVEL] = "Debug"; + assert.strictEqual( + LogLevel.Debug, + getLogLevelFromEnvironment(VSCODE_SORBETEXT_LOG_LEVEL, LogLevel.Debug), + "Defaults to provided default when undefined", + ); + + process.env[VSCODE_SORBETEXT_LOG_LEVEL] = "Not a LogLevel"; + assert.strictEqual( + LogLevel.Info, + getLogLevelFromEnvironment(), + "Defaults to LogLevel.Info when invalid", + ); + + process.env[VSCODE_SORBETEXT_LOG_LEVEL] = "Error"; + assert.strictEqual( + LogLevel.Error, + getLogLevelFromEnvironment(), + "Defaults to LogLevel.Error when invalid", + ); + }); + + test("getLogLevelFromString", () => { + // Literal conversion + assert.strictEqual(LogLevel.Critical, getLogLevelFromString("Critical")); + assert.strictEqual(LogLevel.Debug, getLogLevelFromString("Debug")); + assert.strictEqual(LogLevel.Error, getLogLevelFromString("Error")); + assert.strictEqual(LogLevel.Info, getLogLevelFromString("Info")); + assert.strictEqual(LogLevel.Off, getLogLevelFromString("Off")); + assert.strictEqual(LogLevel.Trace, getLogLevelFromString("Trace")); + assert.strictEqual(LogLevel.Warning, getLogLevelFromString("Warning")); + // Case insensitive + assert.strictEqual(LogLevel.Critical, getLogLevelFromString("CRITICAL")); + assert.strictEqual(LogLevel.Critical, getLogLevelFromString("critical")); + assert.strictEqual(LogLevel.Critical, getLogLevelFromString("cRiTiCal")); + // Invalid string + assert.strictEqual(undefined, getLogLevelFromString("Random Value")); + assert.strictEqual(undefined, getLogLevelFromString(" Critical ")); + }); + + test("OutputChannel is initialized correctly", () => { + const expectedName = "Test"; + + const createOutputChannelStub = sinon + .stub(vscode.window, "createOutputChannel") + .returns({ + appendLine(_value: string) {}, + }); + testRestorables.push(createOutputChannelStub); + + const log = new OutputChannelLog(expectedName); + assert.doesNotThrow(() => log.info("test message")); + + sinon.assert.calledWithExactly(createOutputChannelStub, expectedName); + }); + + test("OutputChannel.logLevel can be updated", () => { + const expectedLogLevel = LogLevel.Warning; + const createOutputChannelStub = sinon + .stub(vscode.window, "createOutputChannel") + .returns({ + appendLine(_value: string) {}, + }); + testRestorables.push(createOutputChannelStub); + + const log = new OutputChannelLog("Test", LogLevel.Info); + assert.strictEqual(log.level, LogLevel.Info, "Expected default state"); + + log.level = expectedLogLevel; + assert.strictEqual(log.level, expectedLogLevel, "Expected new state"); + + sinon.assert.calledOnce(createOutputChannelStub); + }); + + test("All log methods write", () => { + const logMessage = "Test log entry"; + let callCount = 0; + const createOutputChannelStub = sinon + .stub(vscode.window, "createOutputChannel") + .returns(({ + appendLine(value: string) { + assert.ok(value.endsWith(logMessage), `Found: ${value}`); + callCount++; + }, + })); + testRestorables.push(createOutputChannelStub); + + const log = new OutputChannelLog("Test", LogLevel.Trace); + log.error(logMessage); + assert.strictEqual(1, callCount); + log.info(logMessage); + assert.strictEqual(2, callCount); + log.warning(logMessage); + assert.strictEqual(3, callCount); + log.debug(logMessage); + assert.strictEqual(4, callCount); + log.trace(logMessage); + assert.strictEqual(5, callCount); + + sinon.assert.calledOnce(createOutputChannelStub); + }); + + test("Only log methods of appropriate level write", () => { + const logMessage = "Test log entry"; + let callCount = 0; + const createOutputChannelStub = sinon + .stub(vscode.window, "createOutputChannel") + .returns({ + appendLine(value: string) { + assert.ok(value.endsWith(logMessage), `Found: ${value}`); + callCount++; + }, + }); + testRestorables.push(createOutputChannelStub); + + const log = new OutputChannelLog("Test", LogLevel.Info); + log.debug(logMessage); + log.trace(logMessage); + assert.strictEqual( + 0, + callCount, + "No calls to OutputChannel.appendLine expected below LogLevel.Info", + ); + + log.info(logMessage); + assert.strictEqual(1, callCount); + sinon.assert.calledOnce(createOutputChannelStub); + }); + + test("Log.error handles message and error", () => { + const logMessage = "TestError"; + const logError = new Error("Test log entry"); + let callCount = 0; + const createOutputChannelStub = sinon + .stub(vscode.window, "createOutputChannel") + .returns({ + appendLine(value: string) { + assert.ok( + value.endsWith(`${logMessage} Error: ${logError.message}`), + `Found: ${value}`, + ); + callCount++; + }, + }); + testRestorables.push(createOutputChannelStub); + + const log = new OutputChannelLog("Test", LogLevel.Info); + log.error(logMessage, logError); + + assert.strictEqual(1, callCount); + sinon.assert.calledOnce(createOutputChannelStub); + }); +}); diff --git a/vscode_extension/src/test/TestLanguageServer.ts b/vscode_extension/src/test/testLanguageServer.ts similarity index 93% rename from vscode_extension/src/test/TestLanguageServer.ts rename to vscode_extension/src/test/testLanguageServer.ts index b32f573444..c181b1104b 100644 --- a/vscode_extension/src/test/TestLanguageServer.ts +++ b/vscode_extension/src/test/testLanguageServer.ts @@ -4,7 +4,7 @@ import { InitializeParams, TextDocumentSyncKind, } from "vscode-languageserver/node"; -import TestLanguageServerSpecialURIs from "./TestLanguageServerSpecialURIs"; +import { TestLanguageServerSpecialURIs } from "./testLanguageServerSpecialURIs"; // Create a connection for the server. The connection uses Node's IPC as a transport. // Also include all preview / proposed LSP features. diff --git a/vscode_extension/src/test/TestLanguageServerSpecialURIs.ts b/vscode_extension/src/test/testLanguageServerSpecialURIs.ts similarity index 51% rename from vscode_extension/src/test/TestLanguageServerSpecialURIs.ts rename to vscode_extension/src/test/testLanguageServerSpecialURIs.ts index 34dcd7eb6b..557b4be5e6 100644 --- a/vscode_extension/src/test/TestLanguageServerSpecialURIs.ts +++ b/vscode_extension/src/test/testLanguageServerSpecialURIs.ts @@ -1,7 +1,5 @@ -enum TestLanguageServerSpecialURIs { +export enum TestLanguageServerSpecialURIs { SUCCESS = "file:///success", FAILURE = "file:///failure", EXIT = "file:///exit", } - -export default TestLanguageServerSpecialURIs; diff --git a/vscode_extension/src/veneur.ts b/vscode_extension/src/veneur.ts deleted file mode 100644 index 66d479b954..0000000000 --- a/vscode_extension/src/veneur.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { OutputChannel, extensions, commands } from "vscode"; -import { SorbetExtensionConfig } from "./config"; - -export const METRIC_PREFIX = "ruby_typer.lsp.extension."; - -/** - * Exported by the `sorbet-internal.metrics` extension - */ -export type Tags = Readonly<{ [metric: string]: string }>; - -export interface MetricsEmitter { - /** Increments the given counter metric by the given count (default: 1 if unspecified). */ - increment(metricName: string, count?: number, tags?: Tags): Promise; - - /** Sets the given gauge metric to the given value. */ - gauge(metricName: string, value: number, tags?: Tags): Promise; - - /** - * Records a runtime for the specific metric. Is not present on older versions of metrics extension. - * TODO(jvilk): Make non-optional once new version is ~100% rolled out. - */ - timing?(metricName: string, value: number | Date, tags?: Tags): Promise; - - /** Emits any previously-unsent metrics. */ - flush(): Promise; -} - -export interface Api { - readonly metricsEmitter: MetricsEmitter; -} - -class NoOpMetricsEmitter implements MetricsEmitter { - async increment( - _metricName: string, - _count?: number, - _tags?: Tags, - ): Promise {} // eslint-disable-line no-empty-function - - async gauge( - _metricName: string, - _value: number, - _tags?: Tags, - ): Promise {} // eslint-disable-line no-empty-function - - async timing( - _metricName: string, - _value: number | Date, - _tags?: Tags, - ): Promise {} // eslint-disable-line no-empty-function - - async flush(): Promise {} // eslint-disable-line no-empty-function -} - -class NoOpApi implements Api { - readonly metricsEmitter: MetricsEmitter = new NoOpMetricsEmitter(); - static INSTANCE = new NoOpApi(); -} - -let sorbetMetricsApi: Api | undefined; -// Exported for tests. -export function setSorbetMetricsApi(api: Api): void { - sorbetMetricsApi = api; -} - -async function initSorbetMetricsApi(outputChannel: OutputChannel) { - return commands.executeCommand("sorbet.metrics.getExportedApi").then( - (api) => { - if (api) { - outputChannel.appendLine("Sorbet metrics-gathering initialized."); - sorbetMetricsApi = api as Api; - if (!sorbetMetricsApi.metricsEmitter.timing) { - outputChannel.appendLine( - "Note: Timer metrics will not be reported; metrics extension does not support the timer API.", - ); - } - } else { - sorbetMetricsApi = NoOpApi.INSTANCE; - outputChannel.appendLine( - `Sorbet metrics gathering disabled: unrecognized Api object: ${api}`, - ); - } - }, - (reason) => { - sorbetMetricsApi = NoOpApi.INSTANCE; - const adjustedReason = - reason.message === "command 'sorbet.metrics.getExportedApi' not found" - ? "Define the 'sorbet.metrics.getExportedApi' command to enable metrics gathering" - : reason.message; - outputChannel.appendLine( - `Sorbet metrics gathering disabled: ${adjustedReason}`, - ); - }, - ); -} - -const sorbetExtension = extensions.getExtension("sorbet-vscode-extension"); -const sorbetExtensionVersion = - (sorbetExtension && `${sorbetExtension.packageJSON.version}`) || "unknown"; - -/** - * Emit a metric via Veneur. - */ -export function emitCountMetric( - sorbetExtensionConfig: SorbetExtensionConfig, - _outputChannel: OutputChannel, - metric: string, - count: number, -) { - const { activeLspConfig } = sorbetExtensionConfig; - const fullName = `${METRIC_PREFIX}${metric}`; - const tags = { - config_id: activeLspConfig ? activeLspConfig.id : "disabled", - sorbet_extension_version: sorbetExtensionVersion, - }; - if (sorbetMetricsApi) { - sorbetMetricsApi.metricsEmitter.increment(fullName, count, tags); - return; - } - initSorbetMetricsApi(_outputChannel).then(() => { - if (sorbetMetricsApi) { - emitCountMetric(sorbetExtensionConfig, _outputChannel, metric, count); - } - }); -} - -export function emitTimingMetric( - sorbetExtensionConfig: SorbetExtensionConfig, - _outputChannel: OutputChannel, - metric: string, - time: number | Date, - extraTags: Tags = {}, -) { - const { activeLspConfig } = sorbetExtensionConfig; - const fullName = `${METRIC_PREFIX}${metric}`; - const tags = { - config_id: activeLspConfig ? activeLspConfig.id : "disabled", - sorbet_extension_version: sorbetExtensionVersion, - ...extraTags, - }; - if (sorbetMetricsApi) { - // Ignore timers if metrics extension does not support them. - if (sorbetMetricsApi.metricsEmitter.timing) { - sorbetMetricsApi.metricsEmitter.timing(fullName, time, tags); - } - return; - } - initSorbetMetricsApi(_outputChannel).then(() => { - if (sorbetMetricsApi) { - emitTimingMetric(sorbetExtensionConfig, _outputChannel, metric, time); - } - }); -} diff --git a/vscode_extension/yarn.lock b/vscode_extension/yarn.lock index 9bbfdffc03..e6a9227638 100644 --- a/vscode_extension/yarn.lock +++ b/vscode_extension/yarn.lock @@ -141,11 +141,6 @@ "@types/minimatch" "*" "@types/node" "*" -"@types/lodash@^4.14.144": - version "4.14.149" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.149.tgz#1342d63d948c6062838fbf961012f74d4e638440" - integrity "sha1-E0LWPZSMYGKDj7+WEBL3TU5jhEA= sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==" - "@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" @@ -1597,7 +1592,7 @@ lodash.truncate@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= -lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.21: +lodash@^4.17.14, lodash@^4.17.15: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -1676,20 +1671,27 @@ mime@^1.3.4: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -minimatch@3.0.4, minimatch@^3.0.3: +minimatch@3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM= sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==" dependencies: brace-expansion "^1.1.7" -minimatch@^3.0.4: +minimatch@^3.0.3, minimatch@^3.0.4: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" +minimatch@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.5.tgz#4da8f1290ee0f0f8e83d60ca69f8f134068604a3" + integrity sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw== + dependencies: + brace-expansion "^1.1.7" + minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" diff --git a/website/blog/2022-01-06-open-sourcing-sorbet-vscode.md b/website/blog/2022-01-06-open-sourcing-sorbet-vscode.md index 7c99285e27..361334dcae 100644 --- a/website/blog/2022-01-06-open-sourcing-sorbet-vscode.md +++ b/website/blog/2022-01-06-open-sourcing-sorbet-vscode.md @@ -22,8 +22,10 @@ Today’s release includes: - The [pre-built extension](https://marketplace.visualstudio.com/items?itemName=sorbet.sorbet-vscode-extension) on the Visual Studio Marketplace -- The [source code](https://github.com/sorbet/sorbet/issues/new/choose) for the - extension, located in the `vscode_extension/` folder of the Sorbet repo +- The + [source code](https://github.com/sorbet/sorbet/tree/master/vscode_extension) + for the extension, located in the `vscode_extension/` folder of the Sorbet + repo - Full [installation and usage instructions](https://sorbet.org/docs/vscode) in the Sorbet docs diff --git a/website/docs/abstract.md b/website/docs/abstract.md index d4514bc1a3..da214ee390 100644 --- a/website/docs/abstract.md +++ b/website/docs/abstract.md @@ -81,6 +81,47 @@ There are some additional stipulations on the use of `abstract!` and singleton methods. - `abstract!` classes cannot be instantiated (will raise at runtime). +## `overridable`: Providing default implementations of methods + +Certain abstract classes or interfaces want to provide methods that provide a +reasonable default implementation of a method, allowing individual children to +override the method with a more specific implementation. + +This is done with `overridable`: + +```ruby +module Countable + extend T::Helpers + + # 1: `abstract!` instead of `interface!` + abstract! + + sig { abstract.returns(T.nilable(Integer)) } + def to_count; end + + # 2: Use `overridable` to provide default implementation of `to_count!` + sig { overridable.returns(Integer) } + def to_count! + T.must(self.to_count) + end +end +``` + +As the example shows, there are two main steps: + +1. If the module is not already `abstract!` (i.e., if it's an `interface!`), + change it to use `abstract!`. Modules declared with `interface!` are + constrained to _only_ have abstract methods, which prevents adding methods + with a default implementation. + +2. Use `overridable` to declare the default implementation of a method. Using + `overridable` opts the method into static + [override checking](override-checking.md), which will ensure that children + define a type-compatible override. + +Note: if you want to provide functionality in an abstract class or module that +**must not** be possible to override in a child, use a [final method](final.md). + ## Abstract singleton methods `abstract` singleton methods on a module are not allowed, as there's no way to diff --git a/website/docs/adopting.md b/website/docs/adopting.md index 559d640159..6d9e797b28 100644 --- a/website/docs/adopting.md +++ b/website/docs/adopting.md @@ -155,7 +155,7 @@ people encounter at this step: types can be used in place of which other types. [rand-include]: - https://sorbet.run/#module%20A%3B%20end%0Amodule%20B%3B%20end%0A%20%20%0Adef%20x%0A%20%20rand.round%20%3D%3D%200%20%3F%20A%20%3A%20B%0Aend%0A%20%20%0Aclass%20Main%0A%20%20include%20x%0Aend + https://sorbet.run/#%23%20typed%3A%20true%0Amodule%20A%3B%20end%0Amodule%20B%3B%20end%0A%20%20%0Adef%20x%0A%20%20rand.round%20%3D%3D%200%20%3F%20A%20%3A%20B%0Aend%0A%20%20%0Aclass%20Main%0A%20%20include%20x%0Aend To solve points (3) and (4), Sorbet uses [RBI files](rbi.md). We mentioned RBI files before when we introduced `tapioca init`. RBI files are purely annotations diff --git a/website/docs/anything.md b/website/docs/anything.md new file mode 100644 index 0000000000..d8919ce63b --- /dev/null +++ b/website/docs/anything.md @@ -0,0 +1,222 @@ +--- +id: anything +title: T.anything +--- + +The type `T.anything` is a type that is a supertype of all other types in +Sorbet. In this sense, it is the "top" type of Sorbet's type system. + +```ruby +sig {params(x: T.anything).returns(T.anything)} +def example(x) + x.nil? # error: `nil?` doesn't exist on `T.anything` + x +end + +example(0) # ok +example('') # ok +``` + +In this `example` method the parameter `x` has type `T.anything`, Sorbet lets it +be called with anything. However, since Sorbet knows nothing about what methods +exist on `T.anything`, it rejects all methods calls on it (including `.nil?` as +a seen here). + +## Doing something with `T.anything` + +`T.anything` requires being explicitly downcast before it's possible to do +anything meaningful with a value of such a type: + +```ruby +sig {params(x: T.anything).void} +def print_if_even(x) + x.even? # error: Don't know whether `x` is an `Integer` + + # option 1: safe downcast + case x + when Integer + if x.even? + puts("it's even") + else + puts("it's not even") + end + else + # ... handle case when not an `Integer` ... + end + + # option 2: unchecked downcast + y = T.cast(x, Integer) # will raise at runtime if not an `Integer` + if y.even? + puts("it's even") + else + puts("it's not even") + end +end +``` + +In option 1, we use `case` to check whether `x` is an `Integer`, which makes it +easy to handle the case when `x` is not an `Integer`, too. Note that we have to +use `case` and not `is_a?`, because `is_a?` is a method, and `T.anything` does +not respond to any methods. + +In option 2, we use `T.cast` to do a [runtime-only cast](type-assertions.md) to +raise an exception if `x` is not an `Integer` at runtime. + +Viewed like this, `T.anything` is a kind of forcing mechanism to require that +consumers of some otherwise "untyped" interface do runtime type checks to verify +that the type is what they expect. + +## `T.anything` vs `T.untyped` + +`T.anything` is not the same as `T.untyped`: + +- `T.anything` is a supertype of all other types, **but** is not a subtype of + any other type (except itself). + +- `T.untyped` is a super type of all other types, **and** is a subtype of all + other types (which is a contradiction that lies at the core of a + [gradual type system](gradual.md)). + +In simpler terms, Sorbet essentially assumes that a `T.untyped` value is being +used correctly. But for `T.anything`, Sorbet does not allow treating it as if it +were a specific type without some sort of runtime type check or cast. + +To drive the difference home: + +```ruby +sig {params(x: Integer).void} +def takes_integer(x); end + +sig do + params( + something_untyped: T.untyped, + could_be_anything: T.anything, + ) + .void +end +def example(could_be_anything, something_untyped) + # OK, because `T.untyped` is a subtype of everything + takes_integer(something_untyped) + + # NOT OK, because `T.anything` is only a subtype of `T.anything`, + # not `Integer` nor anything else + takes_integer(could_be_anything) +end +``` + +## `T.anything` vs `BasicObject` + +[`BasicObject`] is somewhat similar to `T.anything`. In Ruby, `BasicObject` is +the parent class of all classes. But `T.anything` is an even wider type than +`BasicObject`. + +[`basicobject`]: class-types.md + +The distinction is subtle but important. For example, maybe we want to build an +[interface](abstract.md) that only exposes a single method: + +```ruby +module IFoo + extend T::Helpers + abstract! + + sig {abstract.void} + def foo; end +end + +sig {params(x: IFoo, y: IFoo).void} +def example(x, y) + x.foo # obviously okay + + x == y # should this be okay? (spoiler: it's not) +end +``` + +In this example: it's totally fine to call `x.foo`, because our `IFoo` interface +exposes a `foo` method. But should the call to `x == y` be allowed? + +The `==` method isn't in our interface. Technically speaking, `BasicObject` +defines `==` for all objects, but it's not necessarily the case that it makes +sense to compare all things that implement the `IFoo` interface. As the author +of this interface, we might actually **want** to have Sorbet tell us when we're +calling a method that's not in the interface. + +For this reason, Sorbet does not treat `IFoo` as a subtype of `BasicObject`. +Programmers are free to build precisely the interface they'd like to expose, and +never have to worry about "hiding" the methods from `BasicObject` that they +don't want to expose. + +This is why `T.anything` is useful: there is still _some_ type to write down in +cases where truly passing in anything or returning anything is fine. + +## `T.anything` vs `T.type_parameter(:U)` + +Another common way to declare that a method "accepts anything" is to use a +[generic method](generics.md): + +```ruby +sig {params(x: T.anything).void} +def takes_anything(x) + takes_anything_generic(x) # okay +end + +sig do + type_parameters(:U) + .params(x: T.type_parameter(:U)) + .void +end +def takes_anything_generic(x) + takes_anything(x) # okay +end +``` + +There are some subtle differences between these approaches, but overall they're +quite similar. In fact, the body of `takes_anything_generic` is allowed to pass +`x`, which has type `T.type_parameter(:U)` to `takes_anything`. The opposite +calling direction works as well. + +So how are they different? Whenever using `T.type_parameter(:U)` in a method +signature, **all** occurrences of the type have to agree. In a method like the +identify function, that means that the output has to be verbatim something that +was provided as input: + +```ruby +sig {params(x: T.anything).returns(T.anything)} +def f(x) + # ... +end + +sig do + type_parameters(:U) + .params(x: T.type_parameter(:U)) + .returns(T.type_parameter(:U)) +end +def identity(x) + res = f(x) + return res # error! +end +``` + +The signature for `f` says that it takes `T.anything` and returns `T.anything`, +but it is not required that the thing it returns is at all related to the thing +it took as input. + +Meanwhile, the signature for `identity` says that it returns exactly what it was +given as input. As such, it's an error in the snippet above to have `identity` +return `f(x)` instead of simply `x`. + +But for methods that only mention a given generic type parameter once (like our +`takes_anything` and `takes_anything_generic` methods above), `T.anything` and +`T.type_parameter(:U)` are nearly indistinguishable. + +## `T.anything` and RBIs + +Historically, Sorbet has favored being easy to adopt over avoiding `T.untyped`. +This means that certain methods, for example `JSON.parse`, have been declared to +return `T.untyped` instead of `T.anything` (or something more specific). + +While we're not opposed to using `T.anything` in more places in RBI files, in +each case we will judge it's value against how costly it would be to adopt the +RBI change. See +[the FAQ](faq.md#it-looks-like-sorbets-types-for-the-stdlib-are-wrong) for more +information about contributing RBI improvements. diff --git a/website/docs/attached-class.md b/website/docs/attached-class.md index f4dfdc918f..06f3ff02f0 100644 --- a/website/docs/attached-class.md +++ b/website/docs/attached-class.md @@ -175,6 +175,17 @@ class Parent end ``` +As a final note: none of these problems would happen if `consume` were private: + +- The bad call to `Child.consume(Parent.new)` would not be allowed, because + `consume` would be private. +- The bad call to `A.consume_parent(Child)` would not be allowed because the + body of `consume_parent` contains `cls.consume`, which is a non-private call + to a private method. + +As such, Sorbet allows `T.attached_class` to appear in input (`:in`) positions +of private methods. + ## `T.attached_class` common problems One common problem people encounter when using `T.attached_class` looks @@ -211,3 +222,134 @@ type suggests to the caller that `Child.make` will return a `Child` instance Since `Parent.new` is not an instance of `Child` or any other potential subclasses of `Parent`, Sorbet must reject the code at `(1)`. + +## `has_attached_class!`: `T.attached_class` in module instance methods + +Some modules are only ever eventually mixed into a **class** with `extend` (not +`include`), meaning that any methods that module defines will eventually be +called like singleton class methods. + +These modules usually want to be able to call `new` to instantiate an instance +of the class that the module is `extend`'d into. That's a problem because +normally constructor methods like that would have a return type of +`T.attached_class`, but instance methods cannot mention `T.attached_class`. + +To allow instance methods in such modules to use `T.attached_class`, Sorbet +provides the `has_attached_class!` annotation: + +```ruby +module FinderMethods + extend T::Sig + extend T::Generic + abstract! + + has_attached_class! + + sig {abstract.returns(T.attached_class)} + def new; end + + sig {params(id: String).returns(T.attached_class)} + def find(id) + self.new + end +end + +class ParentModel + extend T::Sig + extend FinderMethods +end + +class ChildModel < ParentModel +end + +parent = ParentModel.find('pa_123') +T.reveal_type(parent) # => `ParentModel` +child = ChildModel.find('ch_123') +T.reveal_type(child) # => `ChildModel` +``` + +Some things to note: + +- The `has_attached_class!` method is exposed as a method in `T::Generic`, + because using `has_attached_class!` implicitly makes the module into a + [generic module](generics.md). This is why modules are not allowed to use + `T.attached_class` by default. More on this in a moment. + +- We've declared `new` as an abstract method. This method is automatically + detected to be implemented when `extend`'d into a class (because all classes + inherit a concrete `self.new` method). + + This abstract `new` method allows calling `new` in the `find` method. If this + `find` method had been in an [RBI file](rbi.md) (not in a source file), then + the abstract `new` declaration would not have been required, because there + would be no method bodies to type check. + +- The `FinderMethods` module is `extended` into `ParentModel`. Had this been + `include FinderMethods`, Sorbet would have reported an error saying that + `FinderMethods` can only be extended into classes. + +- The two calls to `find` at the bottom of the snippet reveal that `find`'s type + changes based on the type of the method call's receiver. Basically: `find` on + a `ChildModel` will be a `ChildModel`. + +### Generics and `has_attached_class!` + +We mentioned above that using `has_attached_class!` in a module makes the module +into a generic module. The mental model is to think of `has_attached_class!` as +syntactic sugar for putting a `type_member` with an unknown name into the +module. This `type_member`, having no explicit name, can then be referenced +using `T.attached_class`. + +What this means is that it's possible to abstract over the attached class of an +`has_attached_class!` module, the same as any other generic interface: + +```ruby +sig do + type_parameters(:U) + .params( + findable: FinderMethods[T.type_parameter(:U)] + id: String + ) + .returns(T.type_parameter(:U)) +end +def find_and_log(findable, id) + instance = findable.find(id) + puts("Found #{instance}") + instance +end + +parent = find_and_log(ParentModel, 'pa_123') +T.reveal_type(parent) # => `ParentModel` +child = find_and_log(ChildModel, 'pa_123') +T.reveal_type(child) # => `ChildModel` +``` + +Note how we've annotated the `findable` parameter as `FinderMethods[...]`, +indicating that we're using the `FinderMethods` module generically. In fact, +supplying a type argument to the `FinderMethods` is now **required**: it's not +possible to reference `FinderMethods` in a type position without providing a +type annotation. If you truly must ignore this, supply a type argument like +`BasicObject` or `T.untyped` (or accept the autocorrect on the error, which will +insert `T.untyped` by default). + +As a generic, `has_attached_class!` takes the same arguments that `type_member` +takes for things like variance and bounds: + +```ruby +# Declares a covariant type member: +has_attached_class!(:out) + +# Places a bound on the type member: +has_attached_class! { {upper: SomeInterface} } + +# Altogether: +has_attached_class!(:out) { {upper: SomeInterface} } +``` + +(Note that this type member cannot be declared contravariant, as that would make +it impossible to mix this module into a class.) + +> Note: you may also find this external blog post useful, which discusses +> similar topics: +> +> [Typing klass.new in Ruby with Sorbet →](https://blog.jez.io/typing-klass-new/) diff --git a/website/docs/class-of.md b/website/docs/class-of.md index 919b29fb56..7f7a090f5c 100644 --- a/website/docs/class-of.md +++ b/website/docs/class-of.md @@ -4,21 +4,34 @@ title: Types for Class Objects via T.class_of sidebar_label: T.class_of --- -Classes are also values in Ruby. Sorbet uses `T.class_of(...)` to describe the -types of those class objects. +Classes are also values in Ruby. Sorbet has two ways to describe the type of +these class objects: `T.class_of(...)` and `T::Class[...]`. ```ruby -T.class_of(Integer) +# The type to use in most circumstances: +T.class_of(MyClass) + +# Another type that has certain specific use cases +# (discussed below) +T::Class[MyClass] ``` -The difference between `MyClass` and `T.class_of(MyClass)` can be confusing. -Here are some examples to make it less confusing: +Prefer `T.class_of(...)` in most cases: it's simpler and leads to fewer +surprises. `T::Class[...]` is better for some very specific use cases, discussed +below. (These specific cases are less common, which is why we recommend using +`T.class_of` to those who don't yet know which to pick.) + +## What is a `T.class_of` type? -| These expressions... | ...have these types | -| -------------------- | --------------------- | -| `0`, `1`, `2 + 2` | `Integer` | -| `Integer` | `T.class_of(Integer)` | -| `42.class` | `T.class_of(Integer)` | +`T.class_of` is used to refer to the type of a class object itself, not values +of that class. This difference can be confusing, so here are some examples to +make it less confusing: + +| This expression... | ...has this type | +| ------------------ | --------------------- | +| `0`, `1`, `2 + 2` | `Integer` | +| `Integer` | `T.class_of(Integer)` | +| `42.class` | `T.class_of(Integer)` | Here's a playground link to confirm these types: @@ -38,8 +51,8 @@ T.let(42.class, T.class_of(Integer)) ## `T.class_of` and inheritance -As with [Class Types](class-types.md#inheritance), `T.class_of` types work with -inheritance: +As with plain [Class Types](class-types.md#inheritance), `T.class_of` types +respect inheritance: ```ruby # typed: true @@ -61,21 +74,40 @@ example(Child) # ok → View on sorbet.run -The most surprising feature of `T.class_of` comes from not understanding -inheritance in Ruby, especially with `include` or `extend` plus modules. +In this example, the `Child` class object passed to the `example` method on the +last line has type `T.class_of(Child)`. The `example` takes +`T.class_of(Parent)`. When one class inherits another, it's singleton class also +inherits the other class's singleton class: -See below for a common gotcha. +```ruby +# On the class itself, Child < Parent +Child.ancestors +# => [Child, Parent, Grandparent, Object, Kernel, BasicObject] + +# On the singleton class, # < # +Child.singleton_class.ancestors +# => [#, #, #, #, + #, Class, Module, Object, Kernel, BasicObject] +``` + +Importantly, this only happens for classes, not modules: the singleton class of +a module is _never_ the ancestor of some other class. See the next section for +more. ## `T.class_of` and modules -**TL;DR**: `T.class_of` has some unintuitive behavior with modules (as opposed -to classes). Consider either using an abstract class or using -`T.all(Class, MyInterface::ClassMethods)` instead of `T.class_of(MyInterface)`. +Usually when people write `T.class_of(MyInterface)`, what they actually want is +either: + +- To rewrite the code to use abstract classes instead of interfaces, and then + use `T.class_of(MyAbstractClass)`, or +- To use a type like `T.all(T::Class[MyInterface], MyInterface::ClassMethods)` -To showcase the problem and solutions, let’s walk through a running example. The -full code for this example is available here: +To showcase why `T.class_of(MyInterface)` is usually a problem and why these two +are better solutions, let's walk through an example. The full code for this +example is available here: - + → View on sorbet.run @@ -98,10 +130,11 @@ example1(MyClass) # ok `MyClass` declares a class which has an instance method and a class method. The `T.class_of(MyClass)` annotation allows `example1` to call both those methods. -None of this is too surprising. +None of this is surprising. Now imagine that we have a lot of these classes and we want to factor out an -interface. The straightforward way to do this uses +interface. The straightforward way to factor out an interface that defines both +instance and singleton class methods uses [`mixes_in_class_methods`](abstract#interfaces-and-the-included-hook), like this: @@ -124,7 +157,7 @@ end This will make `some_instance_method` and `some_class_method` available on `MyClass`, just like before. But if we try to replace `T.class_of(MyClass)` with -`T.class_of(MyInterface)`, it doesn’t work: +`T.class_of(MyInterface)`, it doesn't work: ```ruby sig {params(x: T.class_of(MyInterface)).void} # ← sig has changed @@ -137,15 +170,22 @@ example2(MyClass) # error: Expected `T.class_of(MyInterface)` # but found `T.class_of(MyClass)` ``` -**These errors are correct**, and we can verify them in the Ruby REPL. First, -let's explain the error on the last line above: +**These errors are correct**. Conceptually, `T.class_of(MyInterface)` represents +the type of the `MyInterface` class object _itself_, not "any class object whose +instances implement `MyInterface`." We can verify these errors are correct in +the repl. + +First, we can explain the error on the call to `example2` by looking at +ancestors: ``` ❯ MyClass.singleton_class.ancestors -=> [#, MyInterface::ClassMethods, #, T::Private::Methods::MethodHooks, #, Class, Module, T::Sig, Object, Kernel, BasicObject] +=> [#, MyInterface::ClassMethods, + #, T::Private::Methods::MethodHooks, #, + Class, Module, T::Sig, Object, Kernel, BasicObject] ``` -The first two ancestors of the `MyClass` object are itself and +The first two ancestors of the `MyClass` singleton class are itself and `MyInterface::ClassMethods`. But notably, `#` **does not** appear in this list, so Sorbet is correct to say that `MyClass` does not have type `T.class_of(MyInterface)`. This is because neither `include` nor `extend` @@ -155,49 +195,210 @@ Next, let's explain the other two errors: ``` ❯ MyInterface.singleton_class.ancestors -=> [#, T::Private::MixesInClassMethods, T::Helpers, Module, T::Sig, Object, Kernel, BasicObject] +=> [#, + T::Private::MixesInClassMethods, T::Helpers, Module, T::Sig, Object, + Kernel, BasicObject] ``` -For the `MyInterface` class object, we see that its only ancestor is itself +For the `MyInterface` singleton class, we see that its only ancestor is itself (ignoring common ancestors like `Object`). Notably, **none** of the classes in this list define either a method called `new` (because `Class` is not there) nor `some_class_method` (because `MyInterface::ClassMethods` is not there). -While these errors are technically correct, **we want to be able to type this -code**. There are two options: +While these errors are technically correct, we want to be able to type this +code. There are two options: 1. Use an abstract class instead of an interface. + If this option is available, it's likely the most straightforward. If we + change `MyInterface` to `MyAbstractClass`, all our problems vanish. Sometimes this is not possible, because the class in question already has a - superclass that can't be changed. However, if this option is available, it's - likely the most straightforward. If we change `MyInterface` to - `MyAbstractClass`, all our problems vanish. - -2. Use `T.all(Class, MyInterface::ClassMethods)`. + superclass that can't be changed. - For our example this is only a partial solution, but in many cases it is - good enough. +2. Use `T.all(T::Class[MyInterface], MyInterface::ClassMethods)`. Specifically, option (2) looks like this: ```ruby -sig {params(x: T.all(Class, MyInterface::ClassMethods)).void} +sig {params(x: T.all(T::Class[MyInterface], MyInterface::ClassMethods)).void} def example3(x) - x.new.some_instance_method # error: `some_instance_method` does not exist + x.new.some_instance_method # ok x.some_class_method # ok end -example3(MyClass) # ok +example3(MyClass) # OK ``` -We’re down to only one error now. The error is still technically correct: since -we’re using `Class` instead of `T.class_of(...)`, Sorbet has no way to know what -the instance type created by `x.new` will be (it could be anything), so it -treats the type as `Object`, causing `some_instance_method` to not be found. -However, both the top-level call site to `example3` and the call to -`x.some_class_method` now typecheck successfully. In cases where we don't -actually need to use instance methods from `MyInterface`, this may be an -acceptable workaround. - -> A future feature of Sorbet might be able to improve this workaround. See -> https://github.com/sorbet/sorbet/issues/62. +We discuss `T::Class` more in the next section. To break down that large type: + +- `T.all` is an [Intersection Type](intersection-types.md), which says that `x` + has both the type `T::Class[MyInterface]` and `MyInterface::ClassMethods`. + It's allowed to call all the methods defined on those types individually. + +- `T::Class[MyInterface]` is a type that represents "any class object which, + when instantiated, creates instances that at least have type `MyInterface`." + Other than that, it says nothing about what singleton class methods the class + object has, which means it only assumes those that are defined on `::Class` in + the Ruby standard library (basically, just `.new` and `.name`). But Sorbet is + smart enough to know that objects created by calling `new` have type + `MyInterface`, and thus that `some_instance_method` exists. + +- `MyInterface::ClassMethods` + + This module holds all of the interface's class methods, including + `some_class_methods`. + +## `T::Class` vs `T.class_of` + +`T::Class` was designed to model some mismatches between how people think they +can use `T.class_of` and how `T.class_of` actually works. `T::Class` is powered +by Sorbet's support for [generic classes](generics.md), and is therefore a good +choice for writing code that abstracts over over class objects. + +What are these mismatches? `T.class_of(...)` is, simply, a type representing the +singleton class of `A`, matching how singleton classes work in Ruby as closely +as possible. However: + +- Arbitrary types don't necessarily have singleton classes: for example, + `T.class_of(T.noreturn)` is not a valid type, and neither is + `T.class_of(T.any(A, B))`. +- As we saw in the previous section, `T.class_of(MyInterface)` **does not mean** + "any class object which, when instantiated, creates instances that at least + have type `MyInterface`." + +Sorbet provides `T::Class` to relax these restrictions. Like other +`T::`-prefixed types, this is a [typed wrapper](stdlib-generics.md) for the +`::Class` class defined in the Ruby standard library. It's also a +[generic class](generics.md), which means it can be given an arbitrary type, +instead of only classes. And finally, the generic type parameter on `T::Class` +uses the same internal mechanism as Sorbet's +[`T.attached_class` type](attached-class.md), which represents "an instance of +the current class." + +Combined, these features allow `T::Class[...]` to model some common Ruby +patterns. For example: + +```ruby +sig do + type_parameters(:Instance) + .params(klass: T::Class[T.type_parameter(:Instance)]) + .returns(T.type_parameter(:Instance)) +end +def instantiate_class(klass) + instance = klass.new + puts("Instantiated: #{instance}") + instance +end + +class A; end +class B; end + +# converts T.class_of(A) -> A +a = instantiate_class(A) + +# converts T.class_of(B) -> B +b = instantiate_class(B) +``` + +The example above uses [a generic method](generics.md#generic-methods) to take +any class object, instantiate it, and understand that the return value's type is +the [attached class](attached-class.md) of the class object that was passed in. +Calling `instantiate_class(A)` takes a value of type `T.class_of(A)` and +produces a value of type `A`. `T::Class[T.type_parameter(:U)]` is a type we can +actually write because `T::Class` is a full-fledged generic class. By contrast, +we can't write `T.class_of(T.type_parameter(:U))`,because an arbitrary type like +`T.type_parameter(:U)` might not have a singleton class. + +Another example: + +```ruby +module AbstractCommand + extend T::Helpers + interface! + sig {abstract.void} + def run; end +end + +class MyCommand + include AbstractCommand + + sig {override.void} + def run; puts("Hello, world!"); end +end + +sig {params(command_klass: T::Class[AbstractCommand]).void} +def run_command(command_klass) + # (1) Instantiate some command class + command = command_klass.new + T.reveal_type(command) # => AbstractCommand + # (2) Run the command + command.run +end + +run_command(MyCommand) +``` + +In this example, we use `T::Class` to place a constraint on the class object's +attached class. The `run_command` method takes class objects, but only those +whose attached classes implement the `AbstractCommand` interface. At point (1) +we use the class object to instantiate `command_class`, and Sorbet understands +that the resulting value has type `AbstractCommand`. This allows point (2) to +type check, because Sorbet will know that the `.run` method exists. + +### Why have both `T.class_of` and `T::Class`? + +There are some things that are only possible to represent with `T.class_of`, and +some things that are only possible to represent with `T::Class`. + +- `T::Class` is generic in its attached class. It can be applied to an arbitrary + type, which means that things like `T::Class[T.any(A, B)]` and + `T::Class[MyInterface]` work. + + By contrast, it's simply a syntax error to write `T.class_of(T.any(A, B))` + (because this doesn't resolve to a single attached class), and + `T.class_of(MyInterface)` means something different from what people might + otherwise expect it to mean. + +- `T.class_of` knows what methods are on the singleton class of a class. By + contrast, given this: + + ```ruby + class MyClass + def self.foo; end + end + ``` + + The type `T::Class[MyClass]` doesn't represent what singleton class methods + exist on that class object, only that the associated instance type is. But + `T.class_of(MyClass)` represents both what singleton class methods exist, and + also that creating an instance of this class will have type `MyClass`. + +So these two types are similar, but each has functionality unique to itself. + +The fact that the names are so similar is an unfortunate consequence of history. +It might have been better to use syntax like `T.singleton_class(A)` (or maybe +even `A.singleton_class`) if we could have anticipated that we would eventually +want to build `T::Class` one day. + +### `T::Class` vs `Class` + +In old versions of Sorbet, the `::Class` class in the Ruby standard library was +not generic. In versions of Sorbet that support `T::Class`, `::Class` became +generic. Sorbet requires that generic classes in type annotations not be +bare--they must be applied to a type argument. + +For more information, see +[this section in the docs](stdlib-generics.md#generic-class-without-type-arguments). + +The difference between `T::Class` and `Class` is the same as the difference +between `T::Array` and `Array`. `T::Class` and `Class` represent the same class +definition in the standard library, but `T::Class` allows passing type arguments +to the generic type parameters defined in `Class`. This error is only reported +at [`# typed: strict` or higher](static.md). At lower levels, Sorbet implicitly +assumes that a bare type annotation like `Class` is the same as +`T::Class[T.anything]`. (See [`T.anything`](anything.md).) + +Feel free to replace `Class` with `T::Class[T.anything]` in type annotations +where nothing is known about the class object. If there's an obvious more +specific type, feel free to narrow `T.anything` to whatever the more specific +type is. diff --git a/website/docs/class-types.md b/website/docs/class-types.md index 6e6e9d3a3f..1e3f289511 100644 --- a/website/docs/class-types.md +++ b/website/docs/class-types.md @@ -121,7 +121,7 @@ def takes_object(x); end sig {params(x: BasicObject).void} def takes_basic_object(x); end -# The one error is because an instance +# The one error is because an instance of BasicObject is not an instance of Object takes_object(Object.new) # ok takes_object(BasicObject.new) # error takes_basic_object(Object.new) # ok diff --git a/website/docs/error-reference.md b/website/docs/error-reference.md index e3c46f8922..d3d064e96e 100644 --- a/website/docs/error-reference.md +++ b/website/docs/error-reference.md @@ -531,6 +531,17 @@ generated setter method will then be given an invalid name ending with `==`. `T.nilable(T.untyped)` is just `T.untyped`, because `nil` is a valid value of type `T.untyped` (along with all other values). +## 3513 + +This error code is from an old Sorbet version. It's equivalent to error 4023: + +[→ 4023](#4023) + +## 3514 + +The `has_attached_class!` annotation cannot be given a contravariant `:in` +annotation because `T.attached_class` is only allowed in output positions. + ## 3702 > This error is specific to Stripe's custom `--stripe-packages` mode. If you are @@ -586,10 +597,10 @@ imports. > See [go/pbal](http://go/pbal) for more details. -`autoloader_compatibility` declarations must take a single String argument, -specifically either `legacy` or `strict`. These declarations annotate a package -as compatible for path-based autoloading and are used by our Ruby code loading -pipeline. +`autoloader_compatibility` declarations must take a single String argument. The +only allowed value is `legacy`, otherwise the declaration cannot be present. +These declarations annotate a package as incompatible for path-based autoloading +and are used by our Ruby code loading pipeline. ## 3707 @@ -790,6 +801,34 @@ class A::B < PackageSpec end ``` +Additional signatures of error 3721 include: + +- A package exporting a constant only defined in .rbi files. RBI files are shims + to enable typechecking in places where Ruby metaprogramming prevents Sorbet + from statically interpreting the behavior of a class or module. Generally, + these files declare additional methods on classes defined in Ruby source + files, and should not define any new constants. However, there are some rare + exceptions where these files can define net-new constants. For these cases, we + enforce that these constants cannot be exported. +- A package exporting an enum value: + +```ruby +module MyPackage + class A < T::Enum + enums do + Val1 = new + Val2 = new + end + end +end + +# -- my_package/__package.rb -- + +class MyPackage < PackageSpec + export A::Val1 # not allowed, instead the full enum should be exported with `export A` +end +``` + ## 3722 > This error is specific to Stripe's custom `--stripe-packages` mode. If you are @@ -816,6 +855,23 @@ class A::B < PackageSpec end ``` +## 3723 + +> This error is specific to Stripe's custom `--stripe-packages` mode. If you are +> at Stripe, please see [go/modularity](http://go/modularity) for more. + +The `--stripe-packages` mode allows packages to explicitly enumerate which other +packages are allowed to import them by using the `visible_to` directive. If a +package uses one or more `visible_to` lines, and is imported by a package _not_ +referenced by a `visible_to` line, then Sorbet will report an error pointing to +that import. + +Often, if you're running across this error, it means that you're trying to rely +on an implementation detail that was deliberately made private. However, if +you're sure that it should be okay to import this package, then you can add an +additional `visible_to` directive in order to allow the import you're trying to +add. + ## 4001 Sorbet parses the syntax of `include` and `extend` declarations, even in @@ -827,8 +883,14 @@ are reported when encountered. ## 4002 -Sorbet requires that every `include` references a constant literal. For example, -this is an error, even in `# typed: false` files: +Sorbet requires seeing the complete inheritance hierarchy in a codebase. To do +this, it must be able to statically resolve a class's superclass and any mixins, +declared with `include` or `extend`. + +To make this possible, Sorbet requires that every superclass, `include`, and +`extend` references a constant literal. It's not possible to use an arbitrary +expression (like a method call that produces a class or module) as an ancestor. +This restriction holds even in `# typed: false` files. ```ruby module A; end @@ -843,14 +905,17 @@ class C end ``` -Non-constant literals make it hard to impossible to determine the complete -inheritance hierarchy in a codebase. Sorbet must know the complete inheritance -hierarchy of a codebase in order to check that a variable is a valid instance of -a type. +(For some intuition why this restriction is in place: Sorbet requires resolving +the inheritance hierarchy before it can run inference. Inference is when it +assigns types to every expression in the codebase. Therefore inheritance +resolution cannot depend on inference, as otherwise there would be a logical +cycle in the order Sorbet has to type check a codebase. Similar restrictions +appear throughout Sorbet: see [Why type annotations?](why-type-annotations.md) +for more examples.) -It is possible to silence this error with `T.unsafe`, but it should be done with -**utmost caution**, as Sorbet will not consider the include and provide a less -accurate analysis: +For module mixins, it is possible to silence this error with `T.unsafe`, but it +should be done with **utmost caution**, as Sorbet will not consider the include +and provide a less accurate analysis: ```ruby module A; end @@ -877,6 +942,8 @@ T.let(C, A) # error: Argument does not have asserted type `A` T.let(C, B) # error: Argument does not have asserted type `B` ``` +There is no such workaround for superclasses. + ## 4003 Sorbet parses the syntax of `include` and `extend` declarations, even in @@ -1208,6 +1275,11 @@ assignment and a class definition for a given constant, you can either: file to declare anything that can't be factored out of the ignored file but should still be visible to Sorbet). +## 4023 + +The `has_attached_class!` annotation is only allowed in a Ruby `module`, not a +Ruby `class`. For more, see the docs: [`T.attached_class`](attached-class.md). + ## 5001 Sorbet cannot resolve references to dynamic constants. The common case occurs @@ -1543,6 +1615,10 @@ requires that the variance on parent and child classes matches. ## 5016 +> Note: more recent versions of Sorbet have eliminated this error--it is now +> possible to define generic classes with covariant and contravariant type +> members. + Sorbet does not allow classes to be covariant nor contravariant. **Why?** The design of generic classes and interfaces in Sorbet was heavily @@ -1677,7 +1753,7 @@ Some modules require specific functionality in the receiving class to work. For example `Enumerable` needs a `each` method in the target class. Failing example in -[sorbet.run](https://sorbet.run/#class%20Example%0A%20%20include%20Enumerable%0Aend): +[sorbet.run](https://sorbet.run/#%23%20typed%3A%20true%0A%0Aclass%20Example%0A%20%20include%20Enumerable%0Aend): ``` class Example @@ -1689,7 +1765,7 @@ To fix this, implement the required abstract methods in your class to provide the required functionality. Passing example in -[sorbet.run](): +[sorbet.run](https://sorbet.run/#%23%20typed%3A%20true%0A%0Aclass%20Example%0A%20%20include%20Enumerable%0A%0A%20%20def%20each%28%26blk%29%0A%0A%20%20end%0Aend): ``` class Example @@ -1758,6 +1834,71 @@ def foo; [0]; end For more information, see [Arrays, Hashes, and Generics in the Standard Library](stdlib-generics.md). +## 5027 + +> This error is opt-in, behind the `--check-out-of-order-constant-references` +> flag. +> +> Sorbet does not check this by default because certain codebases make clever +> usage of Ruby's `autoload` mechanism to allow all constants to be referenced +> before their definitions. + +This error fires when a constant is referenced before it is defined. + +```ruby +puts X # error: `X` referenced before it is defined +X = 1 +``` + +```ruby +module Foo + A = X + # ^ error: `Foo::X` referenced before it is defined + class X; end +end +``` + +Generally, Sorbet is not opinionated about definition-reference ordering. It +assumes files are required in the correct order or at the correct times to +ensure that definitions are available before they're referenced. + +However, if a constant is defined in a single file, Sorbet can detect when it's +been referenced in that file ahead of its definition (because in the single-file +case, it doesn't matter whether or in what order any require statements happen). +There are some limitations: + +### Load-time scope must be established definitively + +Sorbet has to prove definitively that a given constant is accessed out-of-order +at load time. It cannot track accesses across function calls or blocks, meaning +that the following code, while technically unloadable, will not throw a Sorbet +error. + +```ruby +module Foo + def bar(&blk) + yield + end + + bar do + A = X # this will not report an error + end + + class X; end +``` + +### Symbols have to be guaranteed to exist only in one file + +In the above example, if `Foo::X` is also declared in another file, the error +will not fire. In such cases, the other file that defines `X` may get required +first, so Sorbet cannot prove that there will be a problem referencing `X` in +this file. + +Ways to fix the error include: + +- Re-ordering the constant access below the declaration. +- In the case of classes, adding an empty pre-declaration before the access. + ## 5028 In `# typed: strict` files, Sorbet requires that all constants are annotated @@ -2150,9 +2291,16 @@ the restriction of only being able to use `Elem` in **out positions**. See [Input and output positions](generics.md#input-and-output-positions) for more information. -Recall that only modules (not classes) may have covariant and contravariant type -members—classes are limited to only invariant type members. For more, see the -docs for error code [5016](#5016). +The ways to fix this error include: + +- Make the type invariant by removing the `:in` or `:out` annotation on the + type. (This comes with the normal restrictions on invariant type members.) +- Mark the method in question `private`. (This comes with the normal + restrictions on `private` methods.) + +If neither of these works, you'll have to reconsider whether it's possible to +statically type the code in question, and how best to rewrite the code so that +it can be typed statically. > **Note** that `T.attached_class` is actually modeled as a covariant (`:out`) > `type_template` defined automatically on all singleton classes, which means @@ -2532,9 +2680,9 @@ module A end ``` -The definition B::C is ambiguous. In Ruby's runtime, it resolves to B::C (and -not A::B::C). However, things are different in the presence of a pre-declared -filler namespace like below: +The definition `B::C` is ambiguous. In Ruby's runtime, it resolves to `B::C` +(and not `A::B::C`). However, things are different in the presence of a +pre-declared filler namespace like below: ```ruby # typed: true @@ -2551,12 +2699,12 @@ module A end ``` -In this case, the definition resolves to A::B::C in Ruby's runtime. +In this case, the definition resolves to `A::B::C` in Ruby's runtime. By default, Sorbet assumes the presence of filler namespaces while typechecking, regardless of whether they are explicitly predeclared like in the second -example. This means that in Sorbet's view, the definition resolves to A::B::C in -either case. +example. This means that in Sorbet's view, the definition resolves to `A::B::C` +in either case. In Stripe's codebase, this is generally not a problem at runtime, as we use Sorbet's own autoloader generation to pre-declare filler namespaces, keeping the @@ -2701,6 +2849,43 @@ class MyClass < AbstractSerializable end ``` +## 5073 + +Abstract classes cannot be instantiated by definition. See +[Abstract Classes and Interfaces](abstract.md) for more information. + +```ruby +class Abstract + extend T::Sig + extend T::Helpers + abstract! + + sig {abstract.void} + def foo; end +end + +Abstract.new # error: Attempt to instantiate abstract class `Abstract` +``` + +To fix this error, there are some options: + +- If the class which is marked `abstract!` does not actually have any `abstract` + methods, simply remove `abstract!` from the class definition to fix the error. +- If the class _does_ have `abstract` methods, find some concrete subclass to + call `new` on instead. If the call to `new` is in a test file, you may wish to + make a new, test-only subclass of the abstract class. (Depending on the + specifics of the test, it may even be possible to simply define all the + abstract methods to simply `raise`, so that other aspects of the parent class + can be tested.) + +## 5074 + +A module marked `has_attached_class!` can only be mixed into a class with +`extend`, or a module with `include`. When mixing a `has_attached_class!` module +into another module, both modules must declare `has_attached_class!`. + +For more information, see the docs for [`T.attached_class`](attached-class.md). + ## 6001 Certain Ruby keywords like `break`, `next`, and `retry` can only be used inside @@ -3298,14 +3483,21 @@ See also: [5028](#5028), [6002](#6002), [7028](#7028), [7043](#7043). ## 7018 -At `typed: strong`, Sorbet no longer allows `T.untyped` as the intermediate -result of any method call. This effectively means that Sorbet knew the type -statically for 100% of calls within a file. This sigil is rarely used—usually -the only files that are `# typed: strong` are RBI files and files with empty -class definitions. Most Ruby files that actually do interesting things will have -errors in `# typed: strong`. Support for `typed: strong` files is minimal, as -Sorbet changes regularly and new features often bring new `T.untyped` -intermediate values. +At `# typed: strong`, Sorbet no longer allows using `T.untyped` values. To fix +errors of this class, add type annotations to the code until Sorbet has enough +context to know the static type of a value. Usually this means adding +[method signatures](sigs.md) or [type assertions](type-assertions.md) to declare +types to Sorbet that it couldn't infer. + +**Note**: this strictness level should be considered a beta feature: the errors +at this level are still being developed. Most Ruby files that actually do +interesting things will have errors in `# typed: strong`. As such, an +alternative solution to fixing these errors is simply to downgrade the file to +`# typed: strict` or below, which will silence all these `T.untyped` errors. + +For more information on `# typed: strong`, strategies for dealing with errors +that arise from using `T.untyped`, and current known limitations, see the docs +for [`# typed: strong`](strong.md). ## 7019 @@ -3471,9 +3663,10 @@ def get_value(input) end ``` -Since generic types are erased at runtime, this construct would never work when -the program executed. Replace the generic type `T::Array[Integer]` by the erased -type `Array` so the runtime behavior is correct: +Since [generic types are erased](generics.md#generics-and-runtime-checks) at +runtime, this construct would never work when the program executed. Replace the +generic type `T::Array[Integer]` by the erased type `Array` so the runtime +behavior is correct: ```ruby def get_value(input) @@ -3897,6 +4090,55 @@ arr = T::Array[NilClass].new T.unsafe(arr).dig(0, 0) ``` +## 7045 + +Sorbet sometimes assumes an expression has a certain type—even when it has no +guarantee whether that's the case—because the assumption will be correct almost +all the time and assuming the type means not having to given an explicit type +annotation. + +This error is reported when those assumptions are wrong. Rather than go back and +attempt to invalidate the assumption by redoing work it already did (but this +time under the correct assumptions), it reports an error asking the user to +provide an explicit type annotation so that no assumption is necessary in the +first place. This enables Sorbet to finish type checking quickly on large +codebases. + +To fix this error, provide an explicit annotation (or simply accept the +[autocorrect suggestion](cli.md#accepting-autocorrect-suggestions)). + +For more information, read +[Why does Sorbet sometimes need type annotations?](why-type-annotations.md). + +## 7046 + +For a limited number of types, Sorbet checks whether it looks like a call to +`==` is out of place. Currently, Sorbet only does these checks when the left +operand of `==` is: + +- `Symbol` +- `String` + +Sorbet is unable to apply these checks for all types, because `==` can be +overridden in arbitrary ways, including to allow for implicit conversion between +unrelated types. This means that Sorbet will sometimes miss reporting this error +in places where we would like it to, and can't be changed to report an error +without breaking valid code. + +To fix this error, ensure that the left and right operands' types match before +doing the comparison. For example, try converting `String`s to `Symbol`s with +`to_sym` (or vice versa with `to_s`). + +## 7047 + +This error code is an implementation detail of Sorbet's "highlight untyped in +editor" mode. It indicates that the given piece of code has type +[`T.untyped`](untyped.md). Untyped code can be dangerous, because it circumvents +the guarantees of the type system. + +This feature is opt-in. See [VS Code](vscode.md) for instructions on how to turn +it on. + [report an issue]: https://github.com/sorbet/sorbet/issues diff --git a/website/docs/flow-sensitive.md b/website/docs/flow-sensitive.md index 4807dd43f4..7166b3c469 100644 --- a/website/docs/flow-sensitive.md +++ b/website/docs/flow-sensitive.md @@ -126,7 +126,7 @@ because knowing that the method `foo` exists says nothing about what parameters that method expects, what their types are, or what the return type of that function is. -It's possible that someday that Sorbet could support a limited form of +It's possible that someday Sorbet could support a limited form of `x.respond_to?(:foo)` when one of the component types of `x` is a type which has a known method called `foo`. There is more information [in this issue](https://github.com/sorbet/sorbet/issues/3469), which details the diff --git a/website/docs/from-typescript.md b/website/docs/from-typescript.md index 05eedf4048..3ab98d8c79 100644 --- a/website/docs/from-typescript.md +++ b/website/docs/from-typescript.md @@ -325,10 +325,10 @@ end unknown - BasicObject + T.anything - See Class Types for more. + See T.anything for more. diff --git a/website/docs/generics.md b/website/docs/generics.md index 4956b72f05..8c58b5d5d7 100644 --- a/website/docs/generics.md +++ b/website/docs/generics.md @@ -285,8 +285,8 @@ motivates why type systems (Sorbet included) place such emphasis on variance. ### Invariance -(_For convenience throughout these docs, we use the annotation `<:` to claim -that one type is a subtype of another type._) +(_For convenience throughout these docs, we use the annotation `A <: B` to claim +that `A` is a subtype of `B`._) By default, `type_member`'s and `type_template`'s are invariant. Here is an example of what that means: @@ -322,11 +322,6 @@ contravariant ones, may be used in **both** input and output positions within method signatures. This nuance is explained in more detail in the next sections about covariance and contravariance. -> **Note**: all `type_member`'s and `type_template`'s in a Ruby `class` must be -> invariant. Only `type_member`'s in a Ruby `module` are allowed to be covariant -> or contravariant. See the docs for error code [5016](error-reference.md#5016) -> for more information. - ### Covariance (`:out`) Covariant type variables preserve the subtyping relationship. Specifically, if @@ -638,10 +633,9 @@ T.proc.params(arg0: Integer).returns(String) ``` In fact, Sorbet uses exactly this trick. The `T.proc` syntax that Sorbet uses to -model to model [procs and lambdas](procs.md) is just syntactic sugar for -something that looks like the `Fn` type above (there are some gotchas around -functions that take zero parameters or more than one parameter, but the concept -is the same). +model [procs and lambdas](procs.md) is just syntactic sugar for something that +looks like the `Fn` type above (there are some gotchas around functions that +take zero parameters or more than one parameter, but the concept is the same). Another intuition which may help knowing which positions are input and output positions: treat function return types as `1` and function parameters as `-1`. @@ -671,11 +665,27 @@ in the input position of an output position, so they're both in input positions (`-1 × +1 = -1`). `F` is in the output position of an output position, so it's also in output position (`+1 × +1 = +1`). +#### Variance positions and `private` + +A special case is provided for `private` methods and instance variables: Sorbet +does not check generic types for their variance position in private methods and +instance variables. + +If you need to allow, for example, a covariant generic type to appear in the +input of a method, that method must be `private`. Note that Ruby treats the +`initialize` method as `private` even if it is not defined as `private` +explicitly, which is what allows accepting arguments typed with covariant type +members in a constructor. + +We can't really explain why this special case is carved out except by answering +"why does tracking variance matter?" + ### Why does tracking variance matter? To get a sense for why Sorbet places constraints on where covariant and contravariant type members can appear within signatures, consider this example, -which continues the example from the [covariance section](#covariance) above: +which continues the example from the [covariance section](#covariance-out) +above: ```ruby int_box = Box[Integer].new(value: 0) @@ -714,6 +724,17 @@ This is what variance checks buy in a type system: they prevent abstractions from being misused in ways that would otherwise compromise the integrity of the type checker's predictions. +As for `private` methods and instance variables (which don't have to respect +variance positions), the short answer is that the general pattern above for how +to produce a contradiction doesn't apply, and it's not possible to construct any +other examples which would cause problems. The example above relied on being +able to explicitly widen the type of the receiver of a method. Since it's not +possible to widen the type of `self`, that class of bug doesn't apply. + +_(As a technicality, this is not quite true because of `T.bind`. Sorbet ignores +this technicality because `T.bind` is itself already an escape hatch to get out +of the type system's checks, like `T.cast`.)_ + ## A `type_template` example So far, the discussion in this guide has focused on `type_member`'s, which tend @@ -807,7 +828,7 @@ The `fixed` annotation in the [example above](#type_templates-and-bounds) places bounds to a `type_member` or `type_template`: - `upper`: Places an upper bound on types that can be applied to a given type - member. Only that are subtypes of that upper bound are valid. + member. Only subtypes of that upper bound are valid. - `lower`: The opposite—places a lower bound, thus requiring only supertypes of that bound. @@ -1003,9 +1024,9 @@ by inventing an entirely new value. ### Shortcomings of generic methods Most commonly, when there is something wrong with Sorbet's support for generic -methods, the error message mentions something about ``, or something about -unreachable code. Whenever you see `` in an error message, one of two -things is happening: +methods, the error message mentions something about `T.anything`, or something +about unreachable code. Whenever you see `T.anything` in an error message +relating to a generic method, one of two things is happening: - There is a valid error, because the method's input type was not properly constrained. Double check the previous section on diff --git a/website/docs/highlight-untyped.md b/website/docs/highlight-untyped.md new file mode 100644 index 0000000000..551a62a22c --- /dev/null +++ b/website/docs/highlight-untyped.md @@ -0,0 +1,157 @@ +--- +id: highlight-untyped +title: Highlighting untyped code +sidebar_label: Highlighting untyped +--- + +> **Note**: This feature is in beta. Please give us feedback! + +Sorbet can highlight regions of [untyped] Ruby code in editors. Here's what it +looks like:[^1] + +[untyped]: untyped.md + +![](/img/highlight-untyped.png) + +[^1]: + This screenshot uses the [Error Lens] VS Code extension to display + diagnostic titles inline. + +[error lens]: + https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens + +VS Code renders these untyped code highlights with a blue squiggly underline. +Other language clients may present them differently, depending on how they +render diagnostics with an [Information] severity. + +[information]: + https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#diagnosticSeverity + +## Toggling untyped highlights + +**Note**: at the moment, toggling untyped highlights incurs a full restart of +Sorbet. This limitation should be removed in the future. + +### In VS Code + +1. Install version 0.3.19 (or later) of the + [Sorbet VS Code extension](vscode.md). + +2. Open a Ruby file, and run the `Sorbet: Toggle highlighting untyped code` + command from the command pallet (accessed via ⇧⌘P on macOS, or ⌃⇧P on Windows + and Linux). + +This setting should persist through restarts of VS Code. + +### In other LSP clients + +This feature relies on the `initializationOptions` parameter to the `initialize` +request that starts +[every language server protocol session](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize). + +To enable this feature, ensure that the `initializationOptions` includes + +```json +"highlightUntyped": true +``` + +as a key-value pair when launching the Sorbet language server in your preferred +client. + +For example, in Neovim, this setting can be provided via the [`init_options` +argument] to the `vim.lsp.start_client()` function. + +[`init_options` argument]: + https://neovim.io/doc/user/lsp.html#:~:text=initializationOptions + +## Notes + +- This feature is not enabled in `# typed: false` files. A `# typed: false` file + is essentially a file where the entire contents would need to be underlined. + Sorbet does not actually underline the entire content of such a file, as it + would be too noisy. + +- This feature will only display diagnostics for **open files**. VS Code scales + poorly when language servers report a very large number number of diagnostics, + and using this feature adds a non-trivial slowdown to Sorbet itself. + +- These blue-underline diagnostics are **not type errors**. In this mode, type + errors continue to be reported exclusively with red squiggles (at least in VS + Code). Files that contain regions of code flagged by this editor mode will + still type check without error according to `srb tc` at the command line. + +- The diagnostics reported in this mode can be converted into proper errors + (i.e., red-underline diagnostics) by marking the file `# typed: strong`. For + more information, see [the docs for `# typed: strong`](strong.md). + +- It may be helpful to use the filtering mechanism built into VS Code's + "Problems" pane to filter these errors. A filter of `!7047` should hide these + in the problems pane. ([7047](error-reference.md#7047) is the code for this + diagnostic, and the `!` negates the filter.) + +## How can I avoid using `T.untyped`? + +→ [How can I avoid using `T.untyped`](strong.md#how-can-i-avoid-using-tuntyped) + +## Why is this feature in beta? + +This feature is currently under development, and we'd love your feedback! + +Some known limitations: + +- Sometimes, the region Sorbet highlights does not precisely match the code + which is untyped. + + For example, a previous version of this feature accidentally highlighted an + entire block (from `do` until `end`) when the value returned from the block + was `T.untyped`. This was fixed by reporting exactly the block's return value. + + Please report cases like these! + + You can use [this Sorbet playground] as a starting point to report issues with + this feature. + +- Not all sources of `T.untyped` are accounted for. + + As we further develop this feature, **more sources of untyped will start being + reported**. Feel free to report instances where it would be nice for Sorbet to + highlight a region of untyped code, but know that we have less time to focus + on these sorts of improvements at the moment. + + Importantly, things with type `T::Array[T.untyped]` or similar, where + `T.untyped` appears in the middle of the type somewhere, are counted as typed. + Code is only highlighted if it is entirely `T.untyped`. + + Also, untyped method parameters are not highlighted. For example: + + ```ruby + sig { params(arg0: T.untyped).void } + def foo(arg0); end + foo(0) + ``` + + Sorbet does not highlight anything about the call to `foo`, even though the + `arg0` argument is untyped. + +- Sometimes, the source of untyped comes from RBI files that do not yet have + types. + + In this case, please help by contributing better types!\ + See [this FAQ entry] for help contributing RBI improvements. + +- Sometimes, the source of the untyped is Sorbet itself. + + Certain features of Ruby are hard to statically type and have historically + been supported on a "best effort" basis by marking them `T.untyped`. + + Feel free to browse our [Untyped code milestone] to see if it looks like + someone has already reported a bug. Otherwise, use [this Sorbet playground] to + craft a test case and report it to us. + +Again, to browse the most up-to-date known limitations with this feature, check +the [Untyped code milestone] for the Sorbet project. + +[this sorbet playground]: + https://sorbet.run/#%23%20typed%3A%20strong%0A%23%20To%20report%20an%20issue%2C%20click%20%22Examples%20%E2%98%B0%20%3E%20Create%20issue%20with%20example%22%0A%0AT.unsafe%28nil%29.foo +[this faq entry]: faq#it-looks-like-sorbets-types-for-the-stdlib-are-wrong +[untyped code milestone]: https://github.com/sorbet/sorbet/milestone/20 diff --git a/website/docs/intersection-types.md b/website/docs/intersection-types.md index b56eae48a1..7340256bb2 100644 --- a/website/docs/intersection-types.md +++ b/website/docs/intersection-types.md @@ -146,15 +146,6 @@ class FooParent; end class FooChild < FooParent; end class Bar; end -module ImmutableBox - extend T::Generic - Elem = type_member(:out) -end -class MutableBox - extend T::Generic - Elem = type_member -end - sig {params(xs: T::Array[T.all(A, B)]).void} def example1(xs) # Since A and B are unrelated classes, Sorbet notices that @@ -186,7 +177,7 @@ def example3(x) end ``` -[→ View on sorbet.run](https://sorbet.run/#%23%20typed%3A%20true%0Aextend%20T%3A%3ASig%0A%0Aclass%20A%3B%20end%0Aclass%20B%3B%20end%0A%0Amodule%20M%3B%20end%0A%0Aclass%20FooParent%3B%20end%0Aclass%20FooChild%20%3C%20FooParent%3B%20end%0Aclass%20Bar%3B%20end%0A%0Amodule%20ImmutableBox%0A%20%20extend%20T%3A%3AGeneric%0A%20%20Elem%20%3D%20type_member%28%3Aout%29%0Aend%0Aclass%20MutableBox%0A%20%20extend%20T%3A%3AGeneric%0A%20%20Elem%20%3D%20type_member%0Aend%0A%0Asig%20%7Bparams%28xs%3A%20T%3A%3AArray%5BT.all%28A%2C%20B%29%5D%29.void%7D%0Adef%20example1%28xs%29%0A%20%20%23%20Since%20A%20and%20B%20are%20unrelated%20classes%2C%20Sorbet%20notices%20that%0A%20%20%23%20there%20are%20no%20values%20that%20satisfy%20%60T.all%28A%2C%20B%29%60%2C%20and%20thus%0A%20%20%23%20collapses%20the%20type%20to%20%60T.noreturn%60%0A%20%20T.reveal_type%28xs%29%20%23%20%3D%3E%20T%3A%3AArray%5BT.noreturn%5D%0Aend%0A%0Asig%20%7Bparams%28x%3A%20T.all%28A%2C%20M%29%29.void%7D%0Adef%20example2%28x%29%0A%20%20%23%20Even%20though%20A%20and%20M%20are%20unrelated%2C%20because%20M%20is%20a%20module%0A%20%20%23%20%28not%20a%20class%29%20the%20type%20does%20not%20collapse.%20Why%3F%20There%20might%0A%20%20%23%20be%20some%20subclasses%20of%20A%20that%20include%20M%2C%20and%20some%20that%20don't.%0A%20%20%23%20%0A%20%20%23%20In%20this%20example%2C%20A%20has%20no%20subclasses.%20If%20we%20explicitly%0A%20%20%23%20declare%20to%20Sorbet%20that%20A%20has%20no%20subclasses%20with%20%60final!%60%2C%0A%20%20%23%20it%20would%20collapse%20the%20type.%0A%20%20T.reveal_type%28x%29%20%23%20%3D%3E%20T.all%28A%2C%20M%29%0Aend%0A%0Asig%20%7Bparams%28x%3A%20T.all%28FooParent%2C%20T.any%28FooChild%2C%20Bar%29%29%29.void%7D%0Adef%20example3%28x%29%0A%20%20%23%20Sorbet%20is%20smart%20enough%20to%20distribute%20over%20union%20types%3A%0A%20%20%23%20%20%20%20T.all%28FooParent%2C%20T.any%28FooChild%2C%20Bar%29%29%0A%20%20%23%20%3D%3E%20T.any%28T.all%28FooParent%2C%20FooChild%29%2C%20T.all%28FooParent%2C%20Bar%29%29%0A%20%20%23%20%3D%3E%20T.any%28FooChild%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2C%20T.noreturn%20%20%20%20%20%20%20%20%20%20%20%29%0A%20%20%23%20%3D%3E%20FooChild%0A%20%20T.reveal_type%28x%29%20%23%20%3D%3E%20FooChild%0Aend) +[→ View on sorbet.run](https://sorbet.run/#%23%20typed%3A%20true%0Aextend%20T%3A%3ASig%0A%0Aclass%20A%3B%20end%0Aclass%20B%3B%20end%0A%0Amodule%20M%3B%20end%0A%0Aclass%20FooParent%3B%20end%0Aclass%20FooChild%20%3C%20FooParent%3B%20end%0Aclass%20Bar%3B%20end%0A%0Asig%20%7Bparams%28xs%3A%20T%3A%3AArray%5BT.all%28A%2C%20B%29%5D%29.void%7D%0Adef%20example1%28xs%29%0A%20%20%23%20Since%20A%20and%20B%20are%20unrelated%20classes%2C%20Sorbet%20notices%20that%0A%20%20%23%20there%20are%20no%20values%20that%20satisfy%20%60T.all%28A%2C%20B%29%60%2C%20and%20thus%0A%20%20%23%20collapses%20the%20type%20to%20%60T.noreturn%60%0A%20%20T.reveal_type%28xs%29%20%23%20%3D%3E%20T%3A%3AArray%5BT.noreturn%5D%0Aend%0A%0Asig%20%7Bparams%28x%3A%20T.all%28A%2C%20M%29%29.void%7D%0Adef%20example2%28x%29%0A%20%20%23%20Even%20though%20A%20and%20M%20are%20unrelated%2C%20because%20M%20is%20a%20module%0A%20%20%23%20%28not%20a%20class%29%20the%20type%20does%20not%20collapse.%20Why%3F%20There%20might%0A%20%20%23%20be%20some%20subclasses%20of%20A%20that%20include%20M%2C%20and%20some%20that%20don't.%0A%20%20%23%20%0A%20%20%23%20In%20this%20example%2C%20A%20has%20no%20subclasses.%20If%20we%20explicitly%0A%20%20%23%20declare%20to%20Sorbet%20that%20A%20has%20no%20subclasses%20with%20%60final!%60%2C%0A%20%20%23%20it%20would%20collapse%20the%20type.%0A%20%20T.reveal_type%28x%29%20%23%20%3D%3E%20T.all%28A%2C%20M%29%0Aend%0A%0Asig%20%7Bparams%28x%3A%20T.all%28FooParent%2C%20T.any%28FooChild%2C%20Bar%29%29%29.void%7D%0Adef%20example3%28x%29%0A%20%20%23%20Sorbet%20is%20smart%20enough%20to%20distribute%20over%20union%20types%3A%0A%20%20%23%20%20%20%20T.all%28FooParent%2C%20T.any%28FooChild%2C%20Bar%29%29%0A%20%20%23%20%3D%3E%20T.any%28T.all%28FooParent%2C%20FooChild%29%2C%20T.all%28FooParent%2C%20Bar%29%29%0A%20%20%23%20%3D%3E%20T.any%28FooChild%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2C%20T.noreturn%20%20%20%20%20%20%20%20%20%20%20%29%0A%20%20%23%20%3D%3E%20FooChild%0A%20%20T.reveal_type%28x%29%20%23%20%3D%3E%20FooChild%0Aend%29) All the examples above use normal, non-generic classes and modules, however the same principles govern when intersection types involving generic classes and diff --git a/website/docs/metrics.md b/website/docs/metrics.md index 3eafdafe5e..f5434921f9 100644 --- a/website/docs/metrics.md +++ b/website/docs/metrics.md @@ -258,6 +258,16 @@ different phases of adopting Sorbet in a codebase. method definitions in that file. The [`rubocop-sorbet`] gem has a number of Rubocop rules for enforcing various Sorbet conventions. +4. Rarely, in codebases that have managed to make heavy use of + `# typed: strict`, it can make sense to use `# typed: strong`. At this + level, Sorbet prevents using `T.untyped` in the file. We recommend that this + level be used sparingly, only in files where 100% type coverage is essential + (for example, possibly in the most error-prone parts of a codebase). By no + means does it have to be a long term goal to achieve a high degree of + `# typed: strong` files in a codebase. For example, Stripe's codebase of + [over 150,000 files](https://stripe.com/blog/sorbet-stripes-type-checker-for-ruby) + has only a couple dozen `# typed: strong` files. + [ask sorbet what it thinks]: https://github.com/sorbet/sorbet/blob/master/docs/suggest-sig.md [`rubocop-sorbet`]: https://github.com/Shopify/rubocop-sorbet diff --git a/website/docs/nilable-types.md b/website/docs/nilable-types.md index 684abdd51c..b42c35fa58 100644 --- a/website/docs/nilable-types.md +++ b/website/docs/nilable-types.md @@ -53,7 +53,7 @@ def foo(x) end ``` - + → View on sorbet.run @@ -201,7 +201,7 @@ def bar(x) end ``` - + → View on sorbet.run diff --git a/website/docs/override-checking.md b/website/docs/override-checking.md index 8ce6d195a3..a6d056bfd3 100644 --- a/website/docs/override-checking.md +++ b/website/docs/override-checking.md @@ -22,13 +22,13 @@ while ❌ means "this is an error". > Below, `standard` (for the child or parent) means "has a `sig`, but has none > of the special modifiers." -| ↓Parent \ Child → | no sig | `standard` | `override` | -| ----------------- | :----: | :--------: | :--------: | -| no sig | ✅ | ✅ | ✅ | -| `standard` | ✅ | ✅ | ❌ | -| `overridable` | ✅ | ❌ | ✅ | -| `override` | ✅ | ❌ | ✅ | -| `abstract` | ✅ | ❌ | ✅ | +| ↓Parent \ Child → | no sig | `standard` | `override` | `abstract` | +| ----------------- | :----: | :--------: | :--------: | :--------: | +| no sig | ✅ | ✅ | ✅ | ✅\* | +| `standard` | ✅ | ✅ | ❌ | ✅\* | +| `overridable` | ✅ | ❌ | ✅ | ✅\* | +| `override` | ✅ | ❌ | ✅ | ✅\* | +| `abstract` | ✅ | ❌ | ✅ | ✅ | Some other things are checked that don't fit into the above table: @@ -41,6 +41,12 @@ Note that the **absence** of `abstract` or `overridable` does **not** mean that a method is never overridden. To declare that a method can never be overridden, look into [final methods](final.md). +> **\***: in the future, Sorbet may stop allowing `abstract` on child methods to +> override non-`abstract` parent methods. Currently, this is a no-op: grandchild +> classes are **not** required to provide a further implementation of the +> `abstract` method in this case, as the concrete parent implementation will be +> run at runtime. + ## A note on variance When overriding a method, the override must accept at least all the same things diff --git a/website/docs/runtime.md b/website/docs/runtime.md index f8abacac6c..3782ab2bcd 100644 --- a/website/docs/runtime.md +++ b/website/docs/runtime.md @@ -271,6 +271,10 @@ default checked level can also be configured. For example: T::Configuration.default_checked_level = :tests ``` +This can also be set via the `SORBET_RUNTIME_DEFAULT_CHECKED_LEVEL` environment +variable, see [Environment Variables](tconfiguration.md#environment-variables) +for more. + Writing this will make it so that any sig which does not have a `.checked(...)` call in it will behave as if the user had written `.checked(:tests)`. To prevent accidental misuse, `sorbet-runtime` will require that this setting is changed @@ -284,6 +288,10 @@ point is a test, run: T::Configuration.enable_checking_for_sigs_marked_checked_tests ``` +This can also be set via the `SORBET_RUNTIME_ENABLE_CHECKING_IN_TESTS` +environment variable, see +[Environment Variables](tconfiguration.md#environment-variables) for more. + For example, this should probably be placed as the first line of any `rake test` target, as well as any other entry point to a project's tests. If this line is absent, `.checked(:tests)` sigs behave as if they had been `.checked(:never)`. @@ -321,6 +329,17 @@ class Foo end ``` +## T.let, T.cast, T.must, T.bind + +Type assertions like `T.let`, `T.cast`, `T.must`, and `T.bind` are normally +checked at runtime, just like `sig` annotations on methods, unless runtime +checks have been disabled. + +Unlike method signatures, type assertions _always_ have a performance cost, +whether or not they are checked at runtime. See +[Type Assertions](type-assertions.md) for tips on patterns that reduce or avoid +this cost. + ## What's next? - [Signatures](sigs.md) diff --git a/website/docs/sig-suggestion.md b/website/docs/sig-suggestion.md new file mode 100644 index 0000000000..e86ef14bfc --- /dev/null +++ b/website/docs/sig-suggestion.md @@ -0,0 +1,101 @@ +--- +id: sig-suggestion +title: Automatically suggesting method signatures +sidebar_label: Suggesting sigs +--- + +In general, Sorbet does not attempt to infer parameter and return types for +method methods without [method signatures](sigs.md). + +(For further context, see +[Why does Sorbet sometimes need type annotations?](why-type-annotations.md).) + +However, Sorbet has best effort support for **suggesting** method signatures +that the user may want to insert into the codebase. By best effort we mean that +sometimes Sorbet will fail to infer any useful types. Despite limitations, +Sorbet is able to suggest signatures in many cases. There are two main ways to +get Sorbet to suggest a signature for a method. + +## Quickfix code actions in `# typed: strict` or higher + +It's an error in a [`# typed: strict`](static.md) file to have a method without +a signature. When reporting these errors, Sorbet runs its signature suggestion +logic to attempt to provide an autocorrect. + +In editors, these [autocorrects] are surfaced as **quickfix code actions**. +Different language clients have different ways to apply these code actions, but +in VS Code they are surfaced either by clicking on the 💡 lightbulb icon, or by +pressing `Ctrl` + `.` (or `Cmd` + `.`, depending on your platform). + +![](/img/suggest-sig-code-action-01.png) + +[autocorrects]: cli.md#accepting-autocorrect-suggestions + +However, if Sorbet's suggested signature is composed **entirely** of +`T.untyped`, it does not generate an autocorrect nor a quickfix (unless running +with the [`--suggest-unsafe` flag]). This is to avoid accidentally desensitizing +`T.untyped` for new users of Sorbet. + +[`--suggest-unsafe` flag]: cli.md#silencing-errors-in-bulk + +When this is the case, the only remaining option is to use completion item +snippets. + +## Completion item snippets in `# typed: true` or higher + +When generating autocompletion items, Sorbet notices when it's autocompleting a +call to the `sig` method, and attaches a snippet to the signature with a +suggested sig: + +![](/img/suggest-sig-completion-item-01.png) + +These suggestions apply anywhere that method name completion works (namely: +`# typed: true` or higher files), so it doesn't require that there already be an +error for a method lacking a signature. + +In VS Code and other clients with snippet support, the snippet will have custom +tab stops. This allows pressing `TAB` to cycle through the placeholder types in +the suggested signature to fill in a proper type for each placeholder: + + + +## Suggesting signatures in bulk + +Adding signatures to a codebase in bulk is tricky, because Sorbet does not +always suggest a suitable signature, and because adding a signature can cause +type errors to appear elsewhere. + +For the brave, some options that can be useful for adding signatures in bulk are +documented in the Command Line Reference doc, including: + +- `--typed ` and `--typed-override ` + - [Docs link](cli#overriding-strictness-levels) + - These flags are useful to temporarily override the sigil of a file. Using + these flags to upgrade files to `# typed: strict` will mean that Sorbet's + built in "This method does not have a sig" errors will appear, with + autocorrects attached +- `--autocorrect` and `--isolate-error-code ` + - [Docs link](cli.md#limiting-autocorrect-suggestions) + - There are many errors reported at `# typed: strict`, but when bulk-adding + signatures, the only autocorrect that's worth accepting is + [7017](error-reference.md#7017). +- Optionally: `--suggest-unsafe` + - [Docs link](cli.md#silencing-errors-in-bulk) + - This option + +In summary: + +```bash +srb tc --typed=strict --isolate-error-code=7017 --autocorrect + +# or, if it's okay to have entirely `T.untyped` signatures: +srb tc --typed=strict --isolate-error-code=7017 --autocorrect --suggest-unsafe +``` + +> **Note**: while this should generate syntactically valid code, it will likely +> cause many new errors which require manual or semi-automated intervention. +> +> For more tips on running large codemods, see +> [this blog post](https://blog.jez.io/codemods-tips/). diff --git a/website/docs/static.md b/website/docs/static.md index b4b3fab70a..fb16407e38 100644 --- a/website/docs/static.md +++ b/website/docs/static.md @@ -47,7 +47,8 @@ Each strictness level reports all errors at lower levels, plus new errors: all are reported in that file. **Note**: ignoring a file can cause errors to appear in _other_ files, because that other file references something defined in an ignored file. We recommend pushing the entire project to out of `ignore` - (at Stripe, 100% of non-test files are not ignored.) + (at Stripe, `# typed: ignore` is only used for certain autogenerated Ruby + files, which have corresponding well-typed [RBI files](rbi.md).) - At `# typed: false`, only errors related to syntax, constant resolution and correctness of `sig`s are reported. Fixing these errors is the baseline for @@ -73,14 +74,11 @@ Each strictness level reports all errors at lower levels, plus new errors: -- At `# typed: strong`, Sorbet no longer allows [`T.untyped`](untyped.md) as the - intermediate result of any method call. This effectively means that Sorbet - knew the type statically for 100% of calls within a file. Currently, this - sigil is rarely used—usually the only files that are `# typed: strong` are RBI - files and files with empty class definitions. Most Ruby files that actually do - interesting things will have errors in `# typed: strong`. Support for - `typed: strong` files is minimal, as Sorbet changes regularly and new features - often bring new `T.untyped` intermediate values. +- At `# typed: strong`, Sorbet no longer allows usages of + [`T.untyped`](untyped.md) values. This effectively means that Sorbet knew the + type statically for all values within a file. Currently, this sigil is + considered a beta feature, as it has certain known limitations. See + [`# typed: strong`](strong.md) for more information. To recap: adding one of these comments to the top of a Ruby file controls which errors `srb` reports or silences in that file. The strictness level only affects diff --git a/website/docs/stdlib-generics.md b/website/docs/stdlib-generics.md index 710b2645e9..89a4ff4356 100644 --- a/website/docs/stdlib-generics.md +++ b/website/docs/stdlib-generics.md @@ -9,17 +9,19 @@ containers defined in the Ruby standard library looks different from other [class types](class-types.md) despite the fact that Ruby uses classes to represent these values, too. Here's the syntax Sorbet uses: -| Type | Example value | -| ------------------------------ | ------------------------------------- | -| `T::Array[Integer]` | `[1, 2, 3]` | -| `T::Array[String]` | `["hello", "goodbye"]` | -| `T::Hash[Symbol, Integer]` | `{key: 0}` | -| `T::Hash[String, Float]` | `{"key" => 0.0}` | -| `T::Set[Integer]` | `Set[1, 2, 3]` | -| `T::Range[Integer]` | `0..10` | -| `T::Enumerable[Integer]` | _interface implemented by many types_ | -| `T::Enumerator[Integer]` | [1, 2, 3].each | -| `T::Enumerator::Lazy[Integer]` | [1, 2, 3].each.lazy | +| Type | Example value | +| ------------------------------- | ------------------------------------- | +| `T::Array[Integer]` | `[1, 2, 3]` | +| `T::Array[String]` | `["hello", "goodbye"]` | +| `T::Hash[Symbol, Integer]` | `{key: 0}` | +| `T::Hash[String, Float]` | `{"key" => 0.0}` | +| `T::Set[Integer]` | `Set[1, 2, 3]` | +| `T::Range[Integer]` | `0..10` | +| `T::Enumerable[Integer]` | _interface implemented by many types_ | +| `T::Enumerator[Integer]` | [1, 2, 3].each | +| `T::Enumerator::Lazy[Integer]` | [1, 2, 3].each.lazy | +| `T::Enumerator::Chain[Integer]` | [1, 2].chain([3]) | +| `T::Class[Integer]` | Integer | ## Why the `T::` prefix? @@ -33,8 +35,8 @@ When creating user-defined generic classes, the `sorbet-runtime` gem automatically defines this method so that the type annotation syntax works at runtime. -But for classes in the Ruby standard library that Sorbet retroactively defined -as generic classes, the `[]` method will not always be defined at runtime. One +But for classes in the Ruby standard library, which Sorbet retroactively defined +as generic classes, the `[]` method will not be defined at runtime. One potential option would have been to use `sorbet-runtime` to monkey patch the standard library so that the `[]` method is defined for generic classes, but some of these Ruby standard library classes **already** define a meaningful `[]` diff --git a/website/docs/strong.md b/website/docs/strong.md new file mode 100644 index 0000000000..734644d760 --- /dev/null +++ b/website/docs/strong.md @@ -0,0 +1,141 @@ +--- +id: strong +title: Banning untyped from a file +sidebar_label: Banning untyped +--- + +> **Note**: This feature is in beta. Please give us feedback! + +The `# typed: strong` sigil bans all usage of `T.untyped` in a file. + +![](/img/strong.png) + +Writing code at this level can be extremely exacting. We do not recommend using +this level too early in the life cycle of adopting Sorbet, as it can cause an +unreasonably high barrier to entry when changing the codebase in the future. +Frequently, the best approach to dealing with `# typed: strong`-level errors is +to simply ignore them, by downgrading the sigil to `# typed: strict`. + +A better option is to configure Sorbet to highlight untyped code in the editor: + +→ [Highlighting untyped code](highlight-untyped.md) + +This will give most of the benefits of flagging which code is covered by Sorbet, +while not being a large burden. + +For more information on our recommendations when adopting Sorbet, see +[Suggestions for driving adoption](metrics.md#suggestions-for-driving-adoption). + +That being said, we welcome enterprising Sorbet users to try writing code at +this level. If you do have feedback for where it's difficult or impossible to +use this level, please let us know. + +## What counts as a usage of untyped? + +Some examples of things that count as a usage of untyped: + +- Calling a method on an untyped value +- Passing an untyped value to a method +- Conditionally branching on an untyped value +- Returning an untyped value from a method +- etc. + +The list of things that count as usages of untyped will grow over time (which is +part of the reason why this feature is considered "beta"). + +### What doesn't count? + +It's maybe more interesting to see what **doesn't** count as a usage of untyped: + +- `x = T.let(..., SomeType)` where `...` is an untyped expression. + + This is one of the primary ways to fix errors arising in `# typed: strong` + files. The [`T.let`](type-assertions.md) type assertion ascribes a static type + to `x`. Using `x` in place of `...` means that Sorbet will see a usage of + something typed instead of something untyped. + +- Any usage of a type like `T::Array[T.untyped]` or + `T::Hash[String, T.untyped]`, which has `T.untyped` somewhere inside it. + + We may reconsider this decision in the future. Sorbet will, at least, flag + that usages of **elements** of these values are usages of untyped: + + ```ruby + sig { params(xs: T::Array[T.untyped]).void } + def example(xs) + x0 = xs.first # (nothing reported here) + x0.even? + # ^^ Call to method `even?` on `T.untyped` + end + ``` + +- Any place where code type checks only because a method parameter accepts + `T.untyped`. + + This is a compromise. There are currently too many methods in the wild which + are typed as taking `T.untyped` to mean "I accept all kinds of values." Sorbet + currently lacks syntax to declare when this is intentional vs accidental + because a more precise type has not been declared. + +## How can I avoid using `T.untyped`? + +It depends on the source of the `T.untyped`. + +- If the untyped comes from a method which returns `T.untyped`, consider adding + a signature to that method (or improving the existing signature). + +- If it's not possible to write a method signature which applies to the method + definition, use `T.let` at each call site of the method which returns + `T.untyped` to provide a more specific type to the result. + +- If the untyped comes from Sorbet's RBI files for the standard library, please + [make a contribution](faq.md#it-looks-like-sorbets-types-for-the-stdlib-are-wrong) + to improve them! + +- If the untyped comes from other generated RBI files, figure out a way to + include generated signatures in the RBI files. + +- If the untyped comes from a use of `T.unsafe`, either try to remove the call + to `T.unsafe` and fix any type errors, or replace it with [`T.cast`], which + will be a more limited type system escape hatch than `T.unsafe`. + +Ultimately, fixing the error is similar to fixing any other type error. + +[`t.cast`]: type-assertions.md#tcast + +## Why is this feature in beta? + +This feature is currently under development, and we'd love your feedback! + +In addition to the things documented in the +[What doesn't count](#what-doesnt-count) section above, here are some known +limitations: + +- Sometimes, the region Sorbet displays in the error message does not precisely + match the code which is untyped. + + For example, a previous version of this feature accidentally underlined an + entire block (from `do` until `end`) when the value returned from the block + was `T.untyped`. This was fixed by reporting exactly the block's return value. + + Please report cases like these! + + You can use [this Sorbet playground] as a starting point to report issues with + this feature. + +- Sometimes, the source of the untyped is Sorbet itself. + + Certain features of Ruby are hard to statically type and have historically + been supported on a "best effort" basis by marking them `T.untyped`. + + Feel free to browse our [Untyped code milestone] to see if it looks like + someone has already reported a bug. Otherwise, use [this Sorbet playground] to + craft a test case and report it to us. + +Again, to browse the most up-to-date known limitations with this feature, check +the [Untyped code milestone] for the Sorbet project. + +[this sorbet playground]: + https://sorbet.run/#%23%20typed%3A%20strong%0A%23%20To%20report%20an%20issue%2C%20click%20%22Examples%20%E2%98%B0%20%3E%20Create%20issue%20with%20example%22%0A%0AT.unsafe%28nil%29.foo +[this faq entry]: faq#it-looks-like-sorbets-types-for-the-stdlib-are-wrong +[untyped code milestone]: https://github.com/sorbet/sorbet/milestone/20 diff --git a/website/docs/tconfiguration.md b/website/docs/tconfiguration.md index 8bca0ce81e..070b2fd0cd 100644 --- a/website/docs/tconfiguration.md +++ b/website/docs/tconfiguration.md @@ -104,3 +104,28 @@ T::Configuration.sig_validation_error_handler = lambda do |error, opts| puts error.message end ``` + +## Environment variables + +There are a number of environment variables that `sorbet-runtime` reads from to +change its behavior: + +### `SORBET_RUNTIME_ENABLE_CHECKING_IN_TESTS` + +Announces to Sorbet that we are currently in a test environment, so it should +treat any sigs which are marked `.checked(:tests)` as if they were just a normal +sig. This can be set to any truthy value to take effect. + +This can also be done by calling +`T::Configuration.enable_checking_for_sigs_marked_checked_tests` but the +environment variable ensures this value gets set before any sigs are evaluated. + +### `SORBET_RUNTIME_DEFAULT_CHECKED_LEVEL` + +Configure the default checked level for a sig with no explicit `.checked` +builder. When unset, the default checked level is `:always`. This must be set to +a valid checked level (e.g. `always` or `tests`). + +This can also be done by calling `T::Configuration.default_checked_level = ...` +but the environment variable ensures this value gets set before any sigs are +evaluated. diff --git a/website/docs/tstruct.md b/website/docs/tstruct.md index cf427590d2..a765547e7b 100644 --- a/website/docs/tstruct.md +++ b/website/docs/tstruct.md @@ -99,7 +99,7 @@ Before we get ahead of ourselves, consider this code: ```ruby class Example < T::Struct # The `[]` default is cloned on initialization, - # so it is not shared by by multiple instances. + # so it is not shared by multiple instances. prop :vals, T::Array[Integer], default: [] end @@ -197,10 +197,11 @@ child class: ```ruby module Common extend T::Helpers + extend T::Sig interface! - sig {returns(Integer)} + sig {abstract.returns(Integer)} def foo; end - sig {params(Integer).returns(Integer)} + sig {abstract.params(foo: Integer).returns(Integer)} def foo=(foo); end end diff --git a/website/docs/tuples.md b/website/docs/tuples.md index 60a9c60204..dfff81b355 100644 --- a/website/docs/tuples.md +++ b/website/docs/tuples.md @@ -51,7 +51,7 @@ if y_0.is_a?(String) end ``` - + → View on sorbet.run diff --git a/website/docs/type-assertions.md b/website/docs/type-assertions.md index 7d3830ae94..500f9a2931 100644 --- a/website/docs/type-assertions.md +++ b/website/docs/type-assertions.md @@ -267,6 +267,14 @@ These assertions are also subject to the `T::Configuration` hooks that [Runtime Configuration](tconfiguration.md) for more. By default, all of these assertions will raise a `TypeError` if they are violated at runtime. +It's possible to opt out of runtime checking for individual calls to `T.let`, +`T.cast`, and `T.bind` by adding `checked: false`, e.g. +`x = T.let(y, Foo, checked: false)`. This isn't recommended in most +circumstances, even in performance-critical code; while adding `checked(:never)` +to a method signature is an easy way to remove performance overhead, doing the +same for `T.let` removes neither the method call overhead nor the overhead of +constructing any type argument. For more effective options, see below. + ## Comparison of type assertions Here are some other ways to think of the behavior of the individual type @@ -321,3 +329,158 @@ assertions: ``` if it were valid in Ruby to assign to `self`. + +## Performance considerations + +Unlike `sig` annotations, type assertions _always_ have a performance cost, even +if runtime checks are globally disabled or `checked: false` is used at +individual callsites. `T.let` and friends are ordinary Ruby method calls, which +have intrisic overhead, in addition to the overhead of constructing any type +arguments. + +This overhead isn't normally worth worrying about, but in code where you are +already micro-optimizing to reduce method calls or object allocations, there are +a few patterns that may be helpful: + +### Prefer method signatures over type assertions + +It's often possible to avoid using a type assertion at all, without loss of type +safety, with a slight refactoring. + +For example, one common use for a type assertion is defining the type of an +instance variable. One can frequently move this type definition to the signature +of the constructor, taking advantage of Sorbet's ability to infer instance +variable types when variables are set directly from constructor arguments. + +Instead of: + +```ruby +sig {void.checked(:tests)} +def initialize + @foo = T.let(MyObject.new, MyInterface) +end +``` + +Write: + +```ruby +sig {params(foo: MyInterface).void.checked(:tests)} +def initialize(foo: MyObject.new) + @foo = foo +end +``` + +In other circumstances, breaking out a method can avoid a type assertion (which +would itself involve at least one method call anyway). + +For example, rather than: + +```ruby +def hot_method(..) + # ... + x = T.let(polymorphic_factory(foo_please), Foo) + # ... +end +``` + +Write: + +```ruby +def hot_method(..) + # ... + x = make_foo + # ... +end + +sig {returns(FooType).checked(:tests)} +def make_foo + polymorphic_factory(foo_please) +end +``` + +### Avoid constructing type objects + +The construction of an non-trivial type object is typically the most expensive +part of a type assertion at runtime. One can usually mitigate this with the use +of `T.type_alias`. + +For example, rather than: + +```ruby +def hot_method(..) + # ... + foo = T.let({}, T::Hash[T.nilable(Symbol), T.any(Integer, Float)]) + # ... +end +``` + +Write: + +```ruby +FooHash = T.type_alias { T::Hash[T.nilable(Symbol), T.any(Integer, Float)] } + +def hot_method(..) + # ... + foo = T.let({}, FooHash) + # ... +end +``` + +### Put type assertions behind memoization + +Performance-sensitive methods are often memoized. In this case, it's usually +possible to memoize the runtime type check as well. + +For example, rather than: + +```ruby +sig {returns(Foo).checked(:tests)} +def foo + @foo = T.let(@foo, T.nilable(Foo)) + @foo ||= something_expensive +end +``` + +Write: + +```ruby +sig {returns(Foo).checked(:tests)} +def foo + @foo ||= T.let(something_expensive, T.nilable(Foo)) +end +``` + +Note that for class methods, there's a better option: + +```ruby +@foo = T.let(nil, T.nilable(Foo)) + +sig {returns(Foo).checked(:tests)} +def self.foo + @foo ||= something_expensive +end +``` + +### Inline type assertions using flow-sensitivity + +A type assertion can usually be replaced by an explicit `===`, `is_a?` or +equivalent check of a local variable, which will avoid a method call. Sometimes +this makes code more verbose, but sometimes it can be a readability improvement +instead, especially in cases involving `T.must`. + +For example, in place of: + +```ruby +if foo.bar + T.must(foo.bar).baz +end +``` + +Write: + +```ruby +x = foo.bar +if x + x.baz +end +``` diff --git a/website/docs/union-types.md b/website/docs/union-types.md index 1a0a627802..ff95caab70 100644 --- a/website/docs/union-types.md +++ b/website/docs/union-types.md @@ -114,9 +114,9 @@ end href="https://sorbet.run/#%23%20typed%3A%20true%0Aclass%20A%3B%20end%0Aclass%20B%3B%20end%0Aclass%20C%3B%0A%20%20extend%20T%3A%3ASig%0A%0A%20%20sig%20%7Bvoid%7D%0A%20%20def%20bar%3B%20end%0Aend%0A%0Aclass%20D%0A%20%20extend%20T%3A%3ASig%0A%0A%20%20sig%20%7Bparams(x%3A%20T.any(A%2C%20B%2C%20C)).void%7D%0A%20%20def%20foo(x)%0A%20%20%20%20x.bar%20%23%20error%3A%20method%20bar%20does%20not%20exist%20on%20A%20or%20B%0A%0A%20%20%20%20case%20x%0A%20%20%20%20when%20A%2C%20B%0A%20%20%20%20%20%20T.reveal_type(x)%20%23%20Revealed%20type%3A%20T.any(B%2C%20A)%0A%20%20%20%20else%0A%20%20%20%20%20%20T.reveal_type(x)%20%23%20Revealed%20type%3A%20C%0A%20%20%20%20%20%20x.bar%20%23%20OK%2C%20x%20is%20known%20to%20be%20an%20instance%20of%20C%0A%20%20%20%20end%0A%20%20end%0Aend"> → View on sorbet.run -In cases like this where the classes in the union don't actually cary around any -extra data, Sorbet has an even more convenient way to define enumerations. See -[Typed Enumerations via T::Enum](tenum.md). +In cases like this where the classes in the union don't actually carry around +any extra data, Sorbet has an even more convenient way to define enumerations. +See [Typed Enumerations via T::Enum](tenum.md). Note that enumerations using primitive or literal types is not supported. For example, the following is _not_ valid: @@ -133,7 +133,7 @@ end ``` +href="https://sorbet.run/#%23%20typed%3A%20true%0A%0Aclass%20A%0A%20%20extend%20T%3A%3ASig%0A%0A%20%20sig%20%7B%20params%28input_param%3A%20T.any%28'foo'%2C%20'bar'%29%29.void%20%7D%0A%20%20def%20a%28input_param%29%0A%20%20%20%20puts%20input_param%0A%20%20end%0Aend"> → View on sorbet.run ## `T.nilable` and `T::Boolean` diff --git a/website/docs/vscode.md b/website/docs/vscode.md index aca0016633..dd6fe87d61 100644 --- a/website/docs/vscode.md +++ b/website/docs/vscode.md @@ -130,6 +130,46 @@ Workspace symbol search: +Custom extension: Copy Symbol to Clipboard + + + +(If you are not using the Sorbet VS Code, you can reimplement this feature in +your preferred LSP client using the [`sorbet/showSymbol` LSP request].) + +[`sorbet/showsymbol` lsp request]: + https://github.com/sorbet/sorbet/blob/ec02be89e3d1895ea51bc72464538073d27b812c/vscode_extension/src/LanguageClient.ts#L154-L179 + +Highlight `T.untyped` code. This feature is in beta. + +This feature reports diagnostics to the editor for occurrences of `T.untyped` +code. Note that it is not yet perfect and may miss occurrences of such values. + +It can be enabled by adding the following to your VS Code `settings.json` and +either reopening VS Code or restarting Sorbet. + +```json +"sorbet.highlightUntyped": true +``` + +or by using the `Sorbet: Toggle Highlight untyped values` command from the +command palette (note this causes a full restart of Sorbet). + +To enable this feature in other language clients, configure your language client +to send + +```json +"initializationOptions": { + "highlightUntyped": true +} +``` + +when sending the LSP initialize request to the Sorbet language server. + + + ## Switching between configurations The Sorbet extension supports switching between multiple configurations to make @@ -220,7 +260,7 @@ Could not locate Gemfile or .bundle/ directory If the errors are persistent, and you can reproduce your problem in the sandbox at https://sorbet.run/, then you've found an issue with Sorbet in general, not -necessarily the VSCode Sorbet extension. Please file a bug tagged with "IDE" on +necessarily the VS Code Sorbet extension. Please file a bug tagged with "IDE" on the [issue tracker](https://github.com/sorbet/sorbet/issues). If the errors are not persistent: diff --git a/website/docs/why-type-annotations.md b/website/docs/why-type-annotations.md index 16963e7d8e..6fda144336 100644 --- a/website/docs/why-type-annotations.md +++ b/website/docs/why-type-annotations.md @@ -33,6 +33,11 @@ annotations (which would be a cycle). Keep in mind that Sorbet respects overloaded and redefined methods, so even simple expressions like these do not always have well-known result types. +(**Note**: Newer versions of Sorbet will attempt to assume that the type of +`A = MyClass.new` is in fact `MyClass`, and require an explicit annotation +_only_ when that assumption turns out to be incorrect, for example due to an +override.) + ## ... for instance variables? Sorbet always requires type annotations for instance and class variables, with a diff --git a/website/package.json b/website/package.json index 5f4b6de36c..ca48388d2e 100644 --- a/website/package.json +++ b/website/package.json @@ -15,7 +15,7 @@ }, "devDependencies": { "docusaurus": "^1.14.0", - "prettier": "1.18.2" + "prettier": "*" }, "dependencies": { "classnames": "^2.2.6" diff --git a/website/pages/en/community.js b/website/pages/en/community.js index 5b6e4cbe0a..6f179bcb48 100644 --- a/website/pages/en/community.js +++ b/website/pages/en/community.js @@ -153,6 +153,24 @@ class Index extends React.Component { description: 'Measure your progress as you adopt Sorbet, stay motivated!', }, + { + title: 'activerecord-ejection_seat', + link: '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/maxveldink/activerecord-ejection_seat', + description: + 'Eject from an ActiveRecord model to a Sorbet T::Struct, or buckle back in', + }, + { + title: 'sorbet-result', + link: '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/maxveldink/sorbet-result', + description: + 'Introduces T::Result, T::Success and T::Failure types to facilitate Railway Oriented Programming', + }, + { + title: 'sorbet-struct-comparable', + link: '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/samuelgiles/sorbet-struct-comparable', + description: + "Comparable T::Struct's for the equality focused typed Ruby developer", + }, { title: 'Parlour', link: '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/AaronC81/parlour', diff --git a/website/sidebars.json b/website/sidebars.json index 732595ced1..d07fbc2091 100644 --- a/website/sidebars.json +++ b/website/sidebars.json @@ -44,10 +44,16 @@ "class-of", "self-type", "noreturn", + "anything", "attached-class", "intersection-types", "generics", - "non-forcing-constants" + "non-forcing-constants", + "strong" + ], + "Editor Features": [ + "sig-suggestion", + "highlight-untyped" ], "Experimental Features": [ "tuples", diff --git a/website/static/img/copy-symbol.mp4 b/website/static/img/copy-symbol.mp4 new file mode 100644 index 0000000000..38eab1ec01 Binary files /dev/null and b/website/static/img/copy-symbol.mp4 differ diff --git a/website/static/img/highlight-untyped.png b/website/static/img/highlight-untyped.png new file mode 100644 index 0000000000..df75f879d2 Binary files /dev/null and b/website/static/img/highlight-untyped.png differ diff --git a/website/static/img/lsp/highlight_untyped.png b/website/static/img/lsp/highlight_untyped.png new file mode 100644 index 0000000000..3c9c62e404 Binary files /dev/null and b/website/static/img/lsp/highlight_untyped.png differ diff --git a/website/static/img/strong.png b/website/static/img/strong.png new file mode 100644 index 0000000000..e612759b68 Binary files /dev/null and b/website/static/img/strong.png differ diff --git a/website/static/img/suggest-sig-code-action-01.png b/website/static/img/suggest-sig-code-action-01.png new file mode 100644 index 0000000000..60f781c089 Binary files /dev/null and b/website/static/img/suggest-sig-code-action-01.png differ diff --git a/website/static/img/suggest-sig-completion-item-01.png b/website/static/img/suggest-sig-completion-item-01.png new file mode 100644 index 0000000000..0848da8161 Binary files /dev/null and b/website/static/img/suggest-sig-completion-item-01.png differ diff --git a/website/static/img/suggest-sig-completion-item-02.mp4 b/website/static/img/suggest-sig-completion-item-02.mp4 new file mode 100644 index 0000000000..c9fefccd87 Binary files /dev/null and b/website/static/img/suggest-sig-completion-item-02.mp4 differ diff --git a/website/static/js/from-typescript.js b/website/static/js/from-typescript.js index 6a9e0f7240..85238f8d6e 100644 --- a/website/static/js/from-typescript.js +++ b/website/static/js/from-typescript.js @@ -1,6 +1,6 @@ (() => { // Remove the on-page nav, because - document.addEventListener('DOMContentLoaded', function(event) { + document.addEventListener('DOMContentLoaded', function (event) { document.querySelector('.onPageNav').remove(); }); })(); diff --git a/website/yarn.lock b/website/yarn.lock index 329d6fa168..bca3d5edb4 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -2157,9 +2157,9 @@ decamelize@^1.1.2: integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" - integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + version "0.2.2" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" + integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== decompress-response@^3.2.0, decompress-response@^3.3.0: version "3.3.0" @@ -4144,11 +4144,9 @@ json-stringify-safe@~5.0.1: integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= json5@^2.1.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" - integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== - dependencies: - minimist "^1.2.5" + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== jsonfile@^6.0.1: version "6.1.0" @@ -5454,10 +5452,10 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= -prettier@1.18.2: - version "1.18.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea" - integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw== +prettier@*: + version "2.8.7" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450" + integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== prismjs@^1.22.0: version "1.27.0" @@ -5549,9 +5547,9 @@ qs@^6.4.0: side-channel "^1.0.4" qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + version "6.5.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" + integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== query-string@^5.0.1: version "5.1.1"