@@ -321,7 +321,9 @@ class SCIPState {
321
321
322
322
// Save definition when you have a sorbet Symbol.
323
323
// Meant for methods, fields etc., but not local variables.
324
- absl::Status saveDefinition (const core::GlobalState &gs, core::FileRef file, core::SymbolRef symRef) {
324
+ // TODO(varun): Should we always pass in the location instead of sometimes only?
325
+ absl::Status saveDefinition (const core::GlobalState &gs, core::FileRef file, core::SymbolRef symRef,
326
+ std::optional<core::LocOffsets> loc = std::nullopt) {
325
327
// TODO:(varun) Should we cache here too to avoid emitting duplicate definitions?
326
328
scip::Symbol symbol;
327
329
auto status = symbolForExpr (gs, symRef, symbol);
@@ -333,11 +335,19 @@ class SCIPState {
333
335
return valueOrStatus.status ();
334
336
}
335
337
string &symbolString = *valueOrStatus.value ();
336
- auto occLocStatus = occurrenceLoc (gs, symRef);
337
- if (!occLocStatus.ok ()) {
338
- return occLocStatus.status ();
338
+
339
+ core::Loc occLoc;
340
+ if (loc.has_value ()) {
341
+ occLoc = core::Loc (file, loc.value ());
342
+ } else {
343
+ auto occLocStatus = occurrenceLoc (gs, symRef);
344
+ if (!occLocStatus.ok ()) {
345
+ return occLocStatus.status ();
346
+ }
347
+ occLoc = occLocStatus.value ();
339
348
}
340
- return this ->saveDefinitionImpl (gs, file, symbolString, occLocStatus.value ());
349
+
350
+ return this ->saveDefinitionImpl (gs, file, symbolString, occLoc);
341
351
}
342
352
343
353
absl::Status saveReference (const core::GlobalState &gs, core::FileRef file, OwnedLocal occ, int32_t symbol_roles) {
@@ -404,6 +414,19 @@ core::SymbolRef lookupRecursive(const core::GlobalState &gs, const core::SymbolR
404
414
return lookupRecursive (gs, owner.enclosingClass (gs), name);
405
415
}
406
416
417
+ std::string format_ancestry (const core::GlobalState &gs, core::SymbolRef sym) {
418
+ UnorderedSet<core::SymbolRef> visited;
419
+ auto i = 0 ;
420
+ std::ostringstream out;
421
+ while (sym.exists () && !visited.contains (sym)) {
422
+ out << fmt::format (" #{}{}{}\n " , std::string (i * 2 , ' ' ), i == 0 ? " " : " <- " , sym.name (gs).toString (gs));
423
+ visited.insert (sym);
424
+ sym = sym.owner (gs);
425
+ i++;
426
+ }
427
+ return out.str ();
428
+ }
429
+
407
430
class CFGTraversal final {
408
431
// A map from each basic block to the locals in it.
409
432
//
@@ -540,28 +563,14 @@ class CFGTraversal final {
540
563
void traverse (const cfg::CFG &cfg) {
541
564
auto &gs = this ->ctx .state ;
542
565
auto method = this ->ctx .owner ;
566
+ auto isMethodFileStaticInit = method == gs.lookupStaticInitForFile (this ->ctx .file );
543
567
544
568
// I don't fully understand the doc comment for forwardsTopoSort; it seems backwards in practice.
545
569
for (auto it = cfg.forwardsTopoSort .rbegin (); it != cfg.forwardsTopoSort .rend (); ++it) {
546
570
cfg::BasicBlock *bb = *it;
547
571
print_dbg (" # Looking at block id: {} ptr: {}\n " , bb->id , (void *)bb);
548
572
this ->copyLocalsFromParents (bb, cfg);
549
573
for (auto &binding : bb->exprs ) {
550
- if (auto *aliasInstr = cfg::cast_instruction<cfg::Alias>(binding.value )) {
551
- auto aliasName = aliasInstr->name ;
552
- if (!aliasName.exists ()) {
553
- // TODO(varun): When does this happen?
554
- continue ;
555
- }
556
- auto aliasedSym = lookupRecursive (gs, method, aliasName);
557
- if (!aliasedSym.exists ()) {
558
- print_err (" # lookup for symbol {} failed starting from {}\n " , aliasName.shortName (gs),
559
- method.toString (gs));
560
- continue ;
561
- }
562
- this ->addLocal (bb, binding.bind .variable );
563
- continue ;
564
- }
565
574
if (!binding.loc .exists () || binding.loc .empty ()) { // TODO(varun): When can each case happen?
566
575
continue ;
567
576
}
@@ -627,7 +636,37 @@ class CFGTraversal final {
627
636
break ;
628
637
}
629
638
case cfg::Tag::Alias: {
630
- ENFORCE (false , " already handled earlier" );
639
+ auto alias = cfg::cast_instruction<cfg::Alias>(binding.value );
640
+ auto aliasedSym = alias->what ;
641
+ if (!aliasedSym.exists ()) {
642
+ if (!alias->name .exists ()) {
643
+ print_dbg (" # alias name doesn't exist @ {}, what = {}\n " ,
644
+ core::Loc (this ->ctx .file , binding.loc ).showRaw (gs), alias->what .showRaw (gs));
645
+ break ;
646
+ }
647
+ print_dbg (" # missing symbol for RHS {}\n " , alias->name .shortName (gs));
648
+ break ;
649
+ } else if (aliasedSym == core::Symbols::Magic ()) {
650
+ break ;
651
+ }
652
+ absl::Status status;
653
+ auto loc = binding.loc ;
654
+ auto source = this ->ctx .locAt (binding.loc ).source (gs);
655
+ if (source.has_value () && source.value ().find (" ::" sv) != std::string::npos) {
656
+ loc.beginLoc = binding.loc .endPos () -
657
+ static_cast <uint32_t >(aliasedSym.name (gs).shortName (gs).length ());
658
+ }
659
+ if (aliasedSym.isClassOrModule () &&
660
+ (isMethodFileStaticInit ||
661
+ method == gs.lookupStaticInitForClass (aliasedSym.asClassOrModuleRef ().data (gs)->owner ))) {
662
+ status = this ->scipState .saveDefinition (gs, this ->ctx .file , aliasedSym, loc);
663
+ } else {
664
+ // When we have code like MyModule::MyClass, the source location in binding.loc corresponds
665
+ // to 'MyModule::MyClass', whereas we want a range for 'MyClass'. So we cut off the prefix.
666
+ status = this ->scipState .saveReference (gs, this ->ctx .file , aliasedSym, loc, 0 );
667
+ }
668
+ ENFORCE (status.ok ());
669
+ this ->addLocal (bb, binding.bind .variable );
631
670
break ;
632
671
}
633
672
case cfg::Tag::Return: {
0 commit comments