Skip to content

Commit 91e64a9

Browse files
committed
Desugar conditional sends
1 parent 092387a commit 91e64a9

File tree

1 file changed

+67
-2
lines changed

1 file changed

+67
-2
lines changed

parser/prism/Translator.cc

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1929,9 +1929,74 @@ ast::ExpressionPtr Translator::desugar(pm_node_t *node) {
19291929

19301930
auto block = desugarBlock(callNode->block, callNode->arguments, callNode->closing_loc);
19311931

1932+
// Conditional send handling
19321933
if (PM_NODE_FLAG_P(callNode, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
1933-
categoryCounterInc("Prism fallback", "PM_CALL_NODE_FLAGS_SAFE_NAVIGATION");
1934-
throw PrismFallback{};
1934+
if (this->preserveConcreteSyntax) {
1935+
// Desugaring to a InsSeq + If causes a problem for Extract to Variable; the fake If will be where
1936+
// the new variable is inserted, which is incorrect. Instead, desugar to a regular send, so that the
1937+
// insertion happens in the correct place (what the csend is inside);
1938+
1939+
// Replace the original method name with a new special one that conveys that this is a CSend, so
1940+
// that a&.foo is treated as different from a.foo when checking for structural equality.
1941+
auto receiver = desugar(callNode->receiver);
1942+
auto newFun = ctx.state.freshNameUnique(core::UniqueNameKind::DesugarCsend, methodName, 1);
1943+
return desugarMethodCall(move(receiver), newFun, methodNameLoc, callNode->arguments,
1944+
callNode->closing_loc, move(block), location, isPrivateOk);
1945+
}
1946+
1947+
// Desugar:
1948+
// result = receiver&.method()
1949+
// to:
1950+
// result = begin
1951+
// $temp = receiver
1952+
// if ::NilClass === $temp
1953+
// ::<Magic>.<nil-for-safe-navigation>($temp)
1954+
// else
1955+
// $temp.method()
1956+
// end
1957+
// end
1958+
1959+
core::NameRef tempRecvName = nextUniqueDesugarName(core::Names::assignTemp());
1960+
auto recvLoc = translateLoc(callNode->receiver->location);
1961+
// // Assign some desugar-produced nodes with zero-length Locs so IDE ignores them when mapping text
1962+
// // location to node.
1963+
auto loc = location;
1964+
auto zeroLengthLoc = loc.copyWithZeroLength();
1965+
auto zeroLengthRecvLoc = recvLoc.copyWithZeroLength();
1966+
auto csendLoc = recvLoc.copyEndWithZeroLength();
1967+
if (recvLoc.endPos() + 1 <= ctx.file.data(ctx).source().size()) {
1968+
auto ampersandLoc = core::LocOffsets{recvLoc.endPos(), recvLoc.endPos() + 1};
1969+
// The arg loc for the synthetic variable created for the purpose of this safe navigation
1970+
// check is a bit of a hack. It's intentionally one character too short so that for
1971+
// completion requests it doesn't match `x&.|` (which would defeat completion requests.)
1972+
if (ctx.locAt(ampersandLoc).source(ctx) == "&") {
1973+
csendLoc = ampersandLoc;
1974+
}
1975+
}
1976+
1977+
ENFORCE(callNode->receiver != nullptr, "Conditional sends should always have a receiver.");
1978+
1979+
// $temp = receiver
1980+
auto receiver = desugar(callNode->receiver);
1981+
auto assignment = MK::Assign(zeroLengthRecvLoc, tempRecvName, desugar(callNode->receiver));
1982+
1983+
// Just compare with `NilClass` to avoid potentially calling into a class-defined `==`
1984+
auto cond =
1985+
MK::Send1(zeroLengthLoc, ast::MK::Constant(zeroLengthRecvLoc, core::Symbols::NilClass()),
1986+
core::Names::tripleEq(), zeroLengthRecvLoc, MK::Local(zeroLengthRecvLoc, tempRecvName));
1987+
1988+
// Workaround to match legacy desugar https://github.com/sorbet/sorbet/issues/9756
1989+
auto isPrivateOk = false;
1990+
1991+
auto tempRecv = MK::Local(zeroLengthRecvLoc, tempRecvName);
1992+
auto send = desugarMethodCall(move(tempRecv), methodName, methodNameLoc, callNode->arguments,
1993+
callNode->closing_loc, move(block), location, isPrivateOk);
1994+
1995+
ExpressionPtr nil =
1996+
MK::Send1(recvLoc.copyEndWithZeroLength(), MK::Magic(zeroLengthLoc),
1997+
core::Names::nilForSafeNavigation(), zeroLengthLoc, MK::Local(csendLoc, tempRecvName));
1998+
auto if_ = MK::If(zeroLengthLoc, move(cond), move(nil), move(send));
1999+
return MK::InsSeq1(location, move(assignment), move(if_));
19352000
}
19362001

19372002
return desugarMethodCall(move(receiver), methodName, methodNameLoc, callNode->arguments,

0 commit comments

Comments
 (0)