Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
6ac71ad
Include parens in multi-assign locations (#9859)
amomchilov Jan 29, 2026
dc94ae8
Improve CI message formatting (#9858)
amomchilov Jan 29, 2026
da20490
Don't suggest imports that violate `visible_to` rules (#9840)
neilparikh Jan 29, 2026
6436b11
Extract `desugarMethodCall()` helper (#9823)
amomchilov Jan 30, 2026
3535e02
Re-enable passing Prism tests (#9863)
amomchilov Feb 2, 2026
d040c9a
Add code action to override overridable method (#9838)
jez Feb 2, 2026
c96f53f
Add completion item to override a method (#9851)
jez Feb 2, 2026
15cde2a
Revert "Add completion item to override a method (#9851)" (#9867)
jez Feb 2, 2026
c100b34
Add completion item to override a method (#9868)
jez Feb 3, 2026
de16ece
Add `--remote_download_outputs=all` to `--config=dbg` (#9872)
jez Feb 3, 2026
9f3e88e
Create `include/` symlinks in LLVM toolchain (#9873)
jez Feb 4, 2026
5d5a6cc
Allow `**` to specify anonymous kwarg hash parameter types (#9857)
egiurleo Feb 5, 2026
fd48d7e
Fix missing predicate in case match (#9877)
thomasmarshall Feb 5, 2026
6d93834
Fix malformed sig parsed as multi-target (#9878)
thomasmarshall Feb 5, 2026
cf813f3
Fix invalid index in for (#9882)
thomasmarshall Feb 5, 2026
e7b4210
Handle implicit begin node (#9879)
thomasmarshall Feb 5, 2026
f1f3bc8
Use resolved constant for missing nodes (#9883)
thomasmarshall Feb 5, 2026
c8763ae
Fix missing constant name in assignment (#9881)
thomasmarshall Feb 5, 2026
788604a
Cut down on use of template metaprogramming (#9884)
jez Feb 5, 2026
65a6166
Revert "Cut down on use of template metaprogramming (#9884)" (#9886)
jez Feb 5, 2026
703498a
Check dynamic const multi-assignment (#9876)
thomasmarshall Feb 5, 2026
76deba0
Use constant name missing for invalid constant path (#9880)
thomasmarshall Feb 6, 2026
8dcddde
Desugar `PM_SUPER_NODE` (#9824)
amomchilov Feb 6, 2026
30bdc31
Add more errors for definitions in package roots (#9743)
elliottt Feb 6, 2026
91f2319
Implement VS Code's `getWordRangeAtPosition` for handling `insertText…
jez Feb 6, 2026
04f89c8
Directly desugar lambdas (#9825)
amomchilov Feb 9, 2026
d758254
Directly desugar `block_given?` (#9827)
amomchilov Feb 9, 2026
177625b
Move `liftTopLevel()` logic into Prism translator (#9889)
amomchilov Feb 9, 2026
ff421d1
Preserve constant resolution in RSpec.describe with class arg (#9890)
alexevanczuk Feb 9, 2026
f4ecc21
Restrict symbol table loops to new symbols (#9648)
elliottt Feb 9, 2026
7aa05ae
Implement --gen-packages-update-visibility-for flag (#9856)
neilparikh Feb 10, 2026
0a21f53
Implement MethodTypeToParserNodePrism (#9813)
KaanOzkan Feb 11, 2026
fb55a83
Name the genPackages argument in LSPIndexer.cc (#9892)
elliottt Feb 11, 2026
435b656
Implement `SignatureTranslatorPrism` and `TypeParamsToparserNodesPris…
KaanOzkan Feb 11, 2026
09b6e71
Return the Import from importsPackage (#9893)
elliottt Feb 12, 2026
faf9e28
Finish that comment (#9895)
elliottt Feb 13, 2026
094345e
Remove unused test file (#9898)
movermeyer-stripe Feb 13, 2026
00c4692
Make members_ private in ClassOrModule (#9891)
elliottt Feb 13, 2026
8015b04
Hide internal magic constants from `sorbet/showSymbol` (#9901)
jez Feb 17, 2026
293d3e8
Log the file names for slow fast path runs (#9894)
neilparikh Feb 17, 2026
4134404
Fix invalid block handling (#9861)
jesse-shopify Feb 17, 2026
1b0a557
Desugar conditional sends
amomchilov Dec 9, 2025
ed864f9
[TMP] isolate broken test
amomchilov Jan 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ build --cxxopt=-Wno-unqualified-std-cast-call --host_cxxopt=-Wno-unqualified-std
build:dbg --copt=-O0
build:dbg --compilation_mode=dbg
build:dbg --config=debugsymbols
# As of Bazel 7, cached files are not put into the `bazel-bin/` folder unless they're toplevel targets.
# We want to force files to be downloaded because otherwise clangd won't find them (and possibly lldb, though I haven't checked)
# TODO(jez) Do newer compile commands generators suffer from this problem? The one we use is archived
build:dbg --remote_download_outputs=all

build:rubydbg --copt=-DRUBY_DEBUG --copt=-DVM_CHECK_MODE=1 --copt=-DTRANSIENT_HEAP_CHECK_MODE --copt=-DRGENGC_CHECK_MODE --copt=-DENC_DEBUG

Expand Down
25 changes: 17 additions & 8 deletions .buildkite/test-static-sanitized.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,30 @@ if [ "$err" -ne 0 ]; then
echo 'Run this command to run failing tests locally:' >> "$failing_tests"
echo >> "$failing_tests"
echo '```bash' >> "$failing_tests"
echo "./bazel test \\" >> "$failing_tests"

# Take the lines that start with target labels.
# Lines look like "//foo FAILED in 10s"
{ ./bazel test --test_summary=terse "${test_args[@]}" || true ; } | \
grep '^//' | \
sed -e 's/ .*/ \\/' | \
sed -e 's/^/ /' >> "$failing_tests"
failing_targets=$(
{ ./bazel test --test_summary=terse "${test_args[@]}" || true ; } | \
grep '^//' | \
sed -e 's/ .*//'
)

num_failing=$(echo "$failing_targets" | wc -l | tr -d ' ')

# Put this last as an easy way to not have a `\` on the last line.
#
# Use --config=dbg instead of sanitized because it's more likely that they've
# already built this config locally, and most test failures reproduce outside
# of sanitized mode anyways.
echo ' --config=dbg --test_output=errors' >> "$failing_tests"
if [ "$num_failing" -eq 1 ]; then
# Single test - put everything on one line
echo "./bazel test --config=dbg --test_output=errors $failing_targets" >> "$failing_tests"
else
# Multiple tests - use multi-line format
echo "./bazel test --config=dbg --test_output=errors \\" >> "$failing_tests"
# Add backslash to all but the last line, indent all lines
echo "$failing_targets" | sed -e '$!s/$/ \\/' -e 's/^/ /' >> "$failing_tests"
fi

echo '```' >> "$failing_tests"

buildkite-agent annotate --context "test-static-sanitized.sh" --style error --append < "$failing_tests"
Expand Down
8 changes: 1 addition & 7 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,7 @@
// Disable feature where Clangd auto-imports headers for missing references.
// It inserts relative paths that we don't want.
"clangd.arguments": ["--header-insertion=never"],
// TODO(jez) Something changed and now clangd doesn't work when we invoke the
// one out of the bazel sandbox. The one installed to /usr/bin from XCode mostly
// works, modulo some extra warnings, so let's just use that until we figure out
// a better way forward.
// TODO(jez) Revisit when we next upgrade llvm_toolchain
//"clangd.path": "bazel-sorbet/external/llvm_toolchain_15_0_7/bin/clangd",
"clangd.path": "clangd",
"clangd.path": "bazel-sorbet/external/llvm_toolchain_15_0_7/bin/clangd",
"files.associations": {
"*.rbi": "ruby",
"*.rbupdated": "ruby",
Expand Down
3 changes: 3 additions & 0 deletions ast/desugar/Desugar.cc
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,9 @@ ExpressionPtr desugarBlock(DesugarContext dctx, parser::Block *block) {
} else {
// This must have been a csend; That will have been desugared
// into an insseq with an If in the expression.
//
// Bug: This could also be an `If` created by desugaring a `block_given?` call.
// https://github.com/sorbet/sorbet/issues/9860
res = move(recv);
auto is = cast_tree<InsSeq>(res);
if (!is) {
Expand Down
55 changes: 4 additions & 51 deletions ast/desugar/PrismDesugar.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,62 +22,15 @@ namespace sorbet::ast::prismDesugar {

using namespace std;

namespace {

// Translate a tree to an expression. NOTE: this should only be called from `node2TreeImpl`.
ExpressionPtr node2TreeImplBody(parser::Node *what) {
ENFORCE(what != nullptr);
ENFORCE(what->hasDesugaredExpr(), "Node has no desugared expression");

auto expr = what->takeDesugaredExpr();
ENFORCE(expr != nullptr, "Node has null desugared expr");

return expr;
}

// Translate trees by calling `node2TreeBody`, and manually reset the unique_ptr argument when it's done.
ExpressionPtr node2TreeImpl(unique_ptr<parser::Node> &what) {
auto res = node2TreeImplBody(what.get());
what.reset();
return res;
}

ExpressionPtr liftTopLevel(core::LocOffsets loc, ExpressionPtr what) {
ClassDef::RHS_store rhs;
ClassDef::ANCESTORS_store ancestors;
ancestors.emplace_back(MK::Constant(loc, core::Symbols::todo()));
auto insSeq = cast_tree<InsSeq>(what);
if (insSeq) {
rhs.reserve(insSeq->stats.size() + 1);
for (auto &stat : insSeq->stats) {
rhs.emplace_back(move(stat));
}
rhs.emplace_back(move(insSeq->expr));
} else {
rhs.emplace_back(move(what));
}
return make_expression<ClassDef>(loc, loc, core::Symbols::root(), MK::EmptyTree(), move(ancestors), move(rhs),
ClassDef::Kind::Class);
}
} // namespace

ExpressionPtr node2Tree(core::MutableContext ctx, unique_ptr<parser::Node> what, bool preserveConcreteSyntax) {
try {
// Callers should not pass null - when Prism falls back, use legacy desugar instead
ENFORCE(what != nullptr, "node2Tree called with null tree");
ENFORCE(what->hasDesugaredExpr(), "Node has no desugared expression");

auto result = what->takeDesugaredExpr();
ENFORCE(result != nullptr, "Node has null desugared expr");

auto liftedClassDefLoc = what->loc;
auto result = node2TreeImpl(what);
if (result.loc().exists()) {
// If the desugared expression has a different loc, we want to use that. This can happen
// because (:block (:send)) desugars to (:send (:block)), but the (:block) node just has
// the loc of the `do ... end`, while the (:send) has the whole loc
//
// But if we desugared to EmptyTree (either intentionally or because there was an
// unsupported node type), we want to use the loc of the original node.
liftedClassDefLoc = result.loc();
}
result = liftTopLevel(liftedClassDefLoc, move(result));
auto verifiedResult = Verifier::run(ctx, move(result));
return verifiedResult;
} catch (SorbetException &) {
Expand Down
7 changes: 7 additions & 0 deletions common/exception/Exception.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,12 @@ template <typename... TArgs> [[noreturn]] bool Exception::raise(fmt::format_stri
stopInDebugger();
throw SorbetException(message);
}

// Indicates that a particular code path should never be reached, with an explanation of why.
// Throws a `sorbet::SorbetException` when triggered to help with debugging.
template <typename... TArgs>
[[noreturn]] void unreachable(fmt::format_string<TArgs...> reasonFormatStr, TArgs &&...args) {
Exception::raise(reasonFormatStr, std::forward<TArgs>(args)...);
}
} // namespace sorbet
#endif // SORBET_ERRO_H
65 changes: 47 additions & 18 deletions core/GlobalState.cc
Original file line number Diff line number Diff line change
Expand Up @@ -995,8 +995,7 @@ void GlobalState::computeLinearization() {
Timer timer(this->tracer(), "resolver.compute_linearization");

// TODO: this does not support `prepend`
for (int i = 1; i < this->classAndModulesUsed(); ++i) {
const auto &ref = core::ClassOrModuleRef(*this, i);
for (auto ref : this->newClassOrModules()) {
computeClassLinearization(*this, ref);
}
}
Expand Down Expand Up @@ -2070,6 +2069,7 @@ unique_ptr<GlobalState> GlobalState::deepCopyGlobalState(bool keepId) const {
Timer timeit2(tracer(), "GlobalState::deepCopyOut");
result->creation = timeit2.getFlowEdge();
}
result->symbolOffsets = this->symbolOffsets;
return result;
}

Expand All @@ -2078,7 +2078,8 @@ unique_ptr<GlobalState> GlobalState::copyForIndexThread(
const vector<string> &extraPackageFilesDirectorySlashDeprecatedPrefixes,
const vector<string> &extraPackageFilesDirectorySlashPrefixes,
const vector<string> &packageSkipRBIExportEnforcementDirs, const vector<string> &allowRelaxedPackagerChecksFor,
const vector<string> &packagerLayers, string errorHint, bool genPackages) const {
const vector<string> &updateVisibilityFor, const vector<string> &packagerLayers, string errorHint,
bool genPackages) const {
ENFORCE(fileTableFrozen);
auto result = make_unique<GlobalState>(this->errorQueue, this->epochManager);

Expand All @@ -2096,10 +2097,10 @@ unique_ptr<GlobalState> GlobalState::copyForIndexThread(
if (packagerEnabled) {
core::UnfreezeNameTable unfreezeToEnterPackagerOptionsGS(*result);
core::packages::UnfreezePackages unfreezeToEnterPackagerOptionsPackageDB = result->unfreezePackages();
result->setPackagerOptions(extraPackageFilesDirectoryUnderscorePrefixes,
extraPackageFilesDirectorySlashDeprecatedPrefixes,
extraPackageFilesDirectorySlashPrefixes, packageSkipRBIExportEnforcementDirs,
allowRelaxedPackagerChecksFor, packagerLayers, errorHint, genPackages);
result->setPackagerOptions(
extraPackageFilesDirectoryUnderscorePrefixes, extraPackageFilesDirectorySlashDeprecatedPrefixes,
extraPackageFilesDirectorySlashPrefixes, packageSkipRBIExportEnforcementDirs, allowRelaxedPackagerChecksFor,
updateVisibilityFor, packagerLayers, errorHint, genPackages);
}

return result;
Expand All @@ -2110,7 +2111,8 @@ unique_ptr<GlobalState> GlobalState::copyForLSPTypechecker(
const vector<string> &extraPackageFilesDirectorySlashDeprecatedPrefixes,
const vector<string> &extraPackageFilesDirectorySlashPrefixes,
const vector<string> &packageSkipRBIExportEnforcementDirs, const vector<string> &allowRelaxedPackagerChecksFor,
const vector<string> &packagerLayers, string errorHint, bool genPackages) const {
const vector<string> &updateVisibilityFor, const vector<string> &packagerLayers, string errorHint,
bool genPackages) const {
auto result = make_unique<GlobalState>(this->errorQueue, this->epochManager);

result->initEmpty();
Expand All @@ -2124,10 +2126,10 @@ unique_ptr<GlobalState> GlobalState::copyForLSPTypechecker(
if (packagerEnabled) {
core::UnfreezeNameTable unfreezeToEnterPackagerOptionsGS(*result);
core::packages::UnfreezePackages unfreezeToEnterPackagerOptionsPackageDB = result->unfreezePackages();
result->setPackagerOptions(extraPackageFilesDirectoryUnderscorePrefixes,
extraPackageFilesDirectorySlashDeprecatedPrefixes,
extraPackageFilesDirectorySlashPrefixes, packageSkipRBIExportEnforcementDirs,
allowRelaxedPackagerChecksFor, packagerLayers, errorHint, genPackages);
result->setPackagerOptions(
extraPackageFilesDirectoryUnderscorePrefixes, extraPackageFilesDirectorySlashDeprecatedPrefixes,
extraPackageFilesDirectorySlashPrefixes, packageSkipRBIExportEnforcementDirs, allowRelaxedPackagerChecksFor,
updateVisibilityFor, packagerLayers, errorHint, genPackages);
}

return result;
Expand All @@ -2137,7 +2139,8 @@ GlobalState::copyForSlowPath(const vector<string> &extraPackageFilesDirectoryUnd
const vector<string> &extraPackageFilesDirectorySlashDeprecatedPrefixes,
const vector<string> &extraPackageFilesDirectorySlashPrefixes,
const vector<string> &packageSkipRBIExportEnforcementDirs,
const vector<string> &allowRelaxedPackagerChecksFor, const vector<string> &packagerLayers,
const vector<string> &allowRelaxedPackagerChecksFor,
const vector<string> &updateVisibilityFor, const vector<string> &packagerLayers,
string errorHint, bool genPackages) const {
auto result = make_unique<GlobalState>(this->errorQueue, this->epochManager);

Expand Down Expand Up @@ -2168,10 +2171,10 @@ GlobalState::copyForSlowPath(const vector<string> &extraPackageFilesDirectoryUnd
if (packageDB().enabled()) {
core::UnfreezeNameTable unfreezeToEnterPackagerOptionsGS(*result);
core::packages::UnfreezePackages unfreezeToEnterPackagerOptionsPackageDB = result->unfreezePackages();
result->setPackagerOptions(extraPackageFilesDirectoryUnderscorePrefixes,
extraPackageFilesDirectorySlashDeprecatedPrefixes,
extraPackageFilesDirectorySlashPrefixes, packageSkipRBIExportEnforcementDirs,
allowRelaxedPackagerChecksFor, packagerLayers, errorHint, genPackages);
result->setPackagerOptions(
extraPackageFilesDirectoryUnderscorePrefixes, extraPackageFilesDirectorySlashDeprecatedPrefixes,
extraPackageFilesDirectorySlashPrefixes, packageSkipRBIExportEnforcementDirs, allowRelaxedPackagerChecksFor,
updateVisibilityFor, packagerLayers, errorHint, genPackages);
}

return result;
Expand Down Expand Up @@ -2322,7 +2325,8 @@ void GlobalState::setPackagerOptions(const vector<string> &extraPackageFilesDire
const vector<string> &extraPackageFilesDirectorySlashPrefixes,
const vector<string> &packageSkipRBIExportEnforcementDirs,
const vector<string> &allowRelaxedPackagerChecksFor,
const vector<string> &packagerLayers, string errorHint, bool genPackages) {
const vector<string> &updateVisibilityFor, const vector<string> &packagerLayers,
string errorHint, bool genPackages) {
ENFORCE_NO_TIMER(!packageDB_.frozen);

packageDB_.enabled_ = true;
Expand All @@ -2332,6 +2336,7 @@ void GlobalState::setPackagerOptions(const vector<string> &extraPackageFilesDire
packageDB_.extraPackageFilesDirectorySlashPrefixes_ = extraPackageFilesDirectorySlashPrefixes;
packageDB_.skipRBIExportEnforcementDirs_ = packageSkipRBIExportEnforcementDirs;
packageDB_.allowRelaxedPackagerChecksFor_ = allowRelaxedPackagerChecksFor;
packageDB_.updateVisibilityFor_ = updateVisibilityFor;
absl::c_transform(packagerLayers, std::back_inserter(packageDB_.layers_),
[this](const auto &layer) { return enterNameUTF8(layer); });
packageDB_.errorHint_ = errorHint;
Expand Down Expand Up @@ -2514,4 +2519,28 @@ spdlog::logger &GlobalState::tracer() const {
return errorQueue->tracer;
}

SymbolTableOffsets::SymbolTableOffsets(const core::GlobalState &gs)
: classAndModulesOffset{gs.classAndModulesUsed()}, methodsOffset{gs.methodsUsed()}, fieldsOffset{gs.fieldsUsed()},
typeMembersOffset{gs.typeMembersUsed()}, typeParametersOffset{gs.typeParametersUsed()} {}

SymbolRange<ClassOrModuleRef> SymbolTableOffsets::classOrModuleRefs(const GlobalState &gs) const {
return SymbolRange<ClassOrModuleRef>(this->classAndModulesOffset, gs.classAndModulesUsed());
}

SymbolRange<MethodRef> SymbolTableOffsets::methodRefs(const GlobalState &gs) const {
return SymbolRange<MethodRef>(this->methodsOffset, gs.methodsUsed());
}

SymbolRange<FieldRef> SymbolTableOffsets::fieldRefs(const GlobalState &gs) const {
return SymbolRange<FieldRef>(this->fieldsOffset, gs.fieldsUsed());
}

SymbolRange<TypeMemberRef> SymbolTableOffsets::typeMemberRefs(const GlobalState &gs) const {
return SymbolRange<TypeMemberRef>(this->typeMembersOffset, gs.typeMembersUsed());
}

SymbolRange<TypeParameterRef> SymbolTableOffsets::typeParameterRefs(const GlobalState &gs) const {
return SymbolRange<TypeParameterRef>(this->typeParametersOffset, gs.typeParametersUsed());
}

} // namespace sorbet::core
Loading
Loading