@@ -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