xref: /llvm-project/lldb/source/Symbol/Variable.cpp (revision 66a88f62cd56e55b5fa0ddb1bdffa549f7565f8f)
1 //===-- Variable.cpp ------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Symbol/Variable.h"
10 
11 #include "lldb/Core/Module.h"
12 #include "lldb/Symbol/Block.h"
13 #include "lldb/Symbol/CompileUnit.h"
14 #include "lldb/Symbol/CompilerDecl.h"
15 #include "lldb/Symbol/CompilerDeclContext.h"
16 #include "lldb/Symbol/Function.h"
17 #include "lldb/Symbol/SymbolContext.h"
18 #include "lldb/Symbol/SymbolFile.h"
19 #include "lldb/Symbol/Type.h"
20 #include "lldb/Symbol/TypeSystem.h"
21 #include "lldb/Symbol/VariableList.h"
22 #include "lldb/Target/ABI.h"
23 #include "lldb/Target/Process.h"
24 #include "lldb/Target/RegisterContext.h"
25 #include "lldb/Target/StackFrame.h"
26 #include "lldb/Target/Target.h"
27 #include "lldb/Target/Thread.h"
28 #include "lldb/Utility/LLDBLog.h"
29 #include "lldb/Utility/Log.h"
30 #include "lldb/Utility/RegularExpression.h"
31 #include "lldb/Utility/Stream.h"
32 #include "lldb/ValueObject/ValueObject.h"
33 #include "lldb/ValueObject/ValueObjectVariable.h"
34 
35 #include "llvm/ADT/Twine.h"
36 
37 using namespace lldb;
38 using namespace lldb_private;
39 
40 Variable::Variable(lldb::user_id_t uid, const char *name, const char *mangled,
41                    const lldb::SymbolFileTypeSP &symfile_type_sp,
42                    ValueType scope, SymbolContextScope *context,
43                    const RangeList &scope_range, Declaration *decl_ptr,
44                    const DWARFExpressionList &location_list, bool external,
45                    bool artificial, bool location_is_constant_data,
46                    bool static_member)
47     : UserID(uid), m_name(name), m_mangled(ConstString(mangled)),
48       m_symfile_type_sp(symfile_type_sp), m_scope(scope),
49       m_owner_scope(context), m_scope_range(scope_range),
50       m_declaration(decl_ptr), m_location_list(location_list), m_external(external),
51       m_artificial(artificial), m_loc_is_const_data(location_is_constant_data),
52       m_static_member(static_member) {}
53 
54 Variable::~Variable() = default;
55 
56 lldb::LanguageType Variable::GetLanguage() const {
57   lldb::LanguageType lang = m_mangled.GuessLanguage();
58   if (lang != lldb::eLanguageTypeUnknown)
59     return lang;
60 
61   if (auto *func = m_owner_scope->CalculateSymbolContextFunction()) {
62     if ((lang = func->GetLanguage()) != lldb::eLanguageTypeUnknown)
63       return lang;
64   } else if (auto *comp_unit =
65                  m_owner_scope->CalculateSymbolContextCompileUnit()) {
66     if ((lang = comp_unit->GetLanguage()) != lldb::eLanguageTypeUnknown)
67       return lang;
68   }
69 
70   return lldb::eLanguageTypeUnknown;
71 }
72 
73 ConstString Variable::GetName() const {
74   ConstString name = m_mangled.GetName();
75   if (name)
76     return name;
77   return m_name;
78 }
79 
80 ConstString Variable::GetUnqualifiedName() const { return m_name; }
81 
82 bool Variable::NameMatches(ConstString name) const {
83   if (m_name == name)
84     return true;
85   SymbolContext variable_sc;
86   m_owner_scope->CalculateSymbolContext(&variable_sc);
87 
88   return m_mangled.NameMatches(name);
89 }
90 bool Variable::NameMatches(const RegularExpression &regex) const {
91   if (regex.Execute(m_name.AsCString()))
92     return true;
93   if (m_mangled)
94     return m_mangled.NameMatches(regex);
95   return false;
96 }
97 
98 Type *Variable::GetType() {
99   if (m_symfile_type_sp)
100     return m_symfile_type_sp->GetType();
101   return nullptr;
102 }
103 
104 void Variable::Dump(Stream *s, bool show_context) const {
105   s->Printf("%p: ", static_cast<const void *>(this));
106   s->Indent();
107   *s << "Variable" << (const UserID &)*this;
108 
109   if (m_name)
110     *s << ", name = \"" << m_name << "\"";
111 
112   if (m_symfile_type_sp) {
113     Type *type = m_symfile_type_sp->GetType();
114     if (type) {
115       s->Format(", type = {{{0:x-16}} {1} (", type->GetID(), type);
116       type->DumpTypeName(s);
117       s->PutChar(')');
118     }
119   }
120 
121   if (m_scope != eValueTypeInvalid) {
122     s->PutCString(", scope = ");
123     switch (m_scope) {
124     case eValueTypeVariableGlobal:
125       s->PutCString(m_external ? "global" : "static");
126       break;
127     case eValueTypeVariableArgument:
128       s->PutCString("parameter");
129       break;
130     case eValueTypeVariableLocal:
131       s->PutCString("local");
132       break;
133     case eValueTypeVariableThreadLocal:
134       s->PutCString("thread local");
135       break;
136     default:
137       s->AsRawOstream() << "??? (" << m_scope << ')';
138     }
139   }
140 
141   if (show_context && m_owner_scope != nullptr) {
142     s->PutCString(", context = ( ");
143     m_owner_scope->DumpSymbolContext(s);
144     s->PutCString(" )");
145   }
146 
147   bool show_fullpaths = false;
148   m_declaration.Dump(s, show_fullpaths);
149 
150   if (m_location_list.IsValid()) {
151     s->PutCString(", location = ");
152     ABISP abi;
153     if (m_owner_scope) {
154       ModuleSP module_sp(m_owner_scope->CalculateSymbolContextModule());
155       if (module_sp)
156         abi = ABI::FindPlugin(ProcessSP(), module_sp->GetArchitecture());
157     }
158     m_location_list.GetDescription(s, lldb::eDescriptionLevelBrief, abi.get());
159   }
160 
161   if (m_external)
162     s->PutCString(", external");
163 
164   if (m_artificial)
165     s->PutCString(", artificial");
166 
167   s->EOL();
168 }
169 
170 bool Variable::DumpDeclaration(Stream *s, bool show_fullpaths,
171                                bool show_module) {
172   bool dumped_declaration_info = false;
173   if (m_owner_scope) {
174     SymbolContext sc;
175     m_owner_scope->CalculateSymbolContext(&sc);
176     sc.block = nullptr;
177     sc.line_entry.Clear();
178     bool show_inlined_frames = false;
179     const bool show_function_arguments = true;
180     const bool show_function_name = true;
181 
182     dumped_declaration_info = sc.DumpStopContext(
183         s, nullptr, Address(), show_fullpaths, show_module, show_inlined_frames,
184         show_function_arguments, show_function_name);
185 
186     if (sc.function)
187       s->PutChar(':');
188   }
189   if (m_declaration.DumpStopContext(s, false))
190     dumped_declaration_info = true;
191   return dumped_declaration_info;
192 }
193 
194 size_t Variable::MemorySize() const { return sizeof(Variable); }
195 
196 CompilerDeclContext Variable::GetDeclContext() {
197   Type *type = GetType();
198   if (type)
199     return type->GetSymbolFile()->GetDeclContextContainingUID(GetID());
200   return CompilerDeclContext();
201 }
202 
203 CompilerDecl Variable::GetDecl() {
204   Type *type = GetType();
205   return type ? type->GetSymbolFile()->GetDeclForUID(GetID()) : CompilerDecl();
206 }
207 
208 void Variable::CalculateSymbolContext(SymbolContext *sc) {
209   if (m_owner_scope) {
210     m_owner_scope->CalculateSymbolContext(sc);
211     sc->variable = this;
212   } else
213     sc->Clear(false);
214 }
215 
216 bool Variable::LocationIsValidForFrame(StackFrame *frame) {
217   if (frame) {
218     Function *function =
219         frame->GetSymbolContext(eSymbolContextFunction).function;
220     if (function) {
221       TargetSP target_sp(frame->CalculateTarget());
222 
223       addr_t loclist_base_load_addr =
224           function->GetAddress().GetLoadAddress(target_sp.get());
225       if (loclist_base_load_addr == LLDB_INVALID_ADDRESS)
226         return false;
227       // It is a location list. We just need to tell if the location list
228       // contains the current address when converted to a load address
229       return m_location_list.ContainsAddress(
230           loclist_base_load_addr,
231           frame->GetFrameCodeAddressForSymbolication().GetLoadAddress(
232               target_sp.get()));
233     }
234   }
235   return false;
236 }
237 
238 bool Variable::LocationIsValidForAddress(const Address &address) {
239   // Be sure to resolve the address to section offset prior to calling this
240   // function.
241   if (address.IsSectionOffset()) {
242     // We need to check if the address is valid for both scope range and value
243     // range.
244     // Empty scope range means block range.
245     bool valid_in_scope_range =
246         GetScopeRange().IsEmpty() || GetScopeRange().FindEntryThatContains(
247                                          address.GetFileAddress()) != nullptr;
248     if (!valid_in_scope_range)
249       return false;
250     SymbolContext sc;
251     CalculateSymbolContext(&sc);
252     if (sc.module_sp == address.GetModule()) {
253       // Is the variable is described by a single location?
254       if (m_location_list.IsAlwaysValidSingleExpr()) {
255         // Yes it is, the location is valid.
256         return true;
257       }
258 
259       if (sc.function) {
260         addr_t loclist_base_file_addr =
261             sc.function->GetAddress().GetFileAddress();
262         if (loclist_base_file_addr == LLDB_INVALID_ADDRESS)
263           return false;
264         // It is a location list. We just need to tell if the location list
265         // contains the current address when converted to a load address
266         return m_location_list.ContainsAddress(loclist_base_file_addr,
267                                                address.GetFileAddress());
268       }
269     }
270   }
271   return false;
272 }
273 
274 bool Variable::IsInScope(StackFrame *frame) {
275   switch (m_scope) {
276   case eValueTypeRegister:
277   case eValueTypeRegisterSet:
278     return frame != nullptr;
279 
280   case eValueTypeConstResult:
281   case eValueTypeVariableGlobal:
282   case eValueTypeVariableStatic:
283   case eValueTypeVariableThreadLocal:
284     return true;
285 
286   case eValueTypeVariableArgument:
287   case eValueTypeVariableLocal:
288     if (frame) {
289       // We don't have a location list, we just need to see if the block that
290       // this variable was defined in is currently
291       Block *deepest_frame_block =
292           frame->GetSymbolContext(eSymbolContextBlock).block;
293       if (deepest_frame_block) {
294         SymbolContext variable_sc;
295         CalculateSymbolContext(&variable_sc);
296 
297         // Check for static or global variable defined at the compile unit
298         // level that wasn't defined in a block
299         if (variable_sc.block == nullptr)
300           return true;
301 
302         // Check if the variable is valid in the current block
303         if (variable_sc.block != deepest_frame_block &&
304             !variable_sc.block->Contains(deepest_frame_block))
305           return false;
306 
307         // If no scope range is specified then it means that the scope is the
308         // same as the scope of the enclosing lexical block.
309         if (m_scope_range.IsEmpty())
310           return true;
311 
312         addr_t file_address = frame->GetFrameCodeAddress().GetFileAddress();
313         return m_scope_range.FindEntryThatContains(file_address) != nullptr;
314       }
315     }
316     break;
317 
318   default:
319     break;
320   }
321   return false;
322 }
323 
324 Status Variable::GetValuesForVariableExpressionPath(
325     llvm::StringRef variable_expr_path, ExecutionContextScope *scope,
326     GetVariableCallback callback, void *baton, VariableList &variable_list,
327     ValueObjectList &valobj_list) {
328   Status error;
329   if (!callback || variable_expr_path.empty()) {
330     error = Status::FromErrorString("unknown error");
331     return error;
332   }
333 
334   switch (variable_expr_path.front()) {
335   case '*':
336     error = Variable::GetValuesForVariableExpressionPath(
337         variable_expr_path.drop_front(), scope, callback, baton, variable_list,
338         valobj_list);
339     if (error.Fail()) {
340       error = Status::FromErrorString("unknown error");
341       return error;
342     }
343     for (uint32_t i = 0; i < valobj_list.GetSize();) {
344       Status tmp_error;
345       ValueObjectSP valobj_sp(
346           valobj_list.GetValueObjectAtIndex(i)->Dereference(tmp_error));
347       if (tmp_error.Fail()) {
348         variable_list.RemoveVariableAtIndex(i);
349         valobj_list.RemoveValueObjectAtIndex(i);
350       } else {
351         valobj_list.SetValueObjectAtIndex(i, valobj_sp);
352         ++i;
353       }
354     }
355     return error;
356   case '&': {
357     error = Variable::GetValuesForVariableExpressionPath(
358         variable_expr_path.drop_front(), scope, callback, baton, variable_list,
359         valobj_list);
360     if (error.Success()) {
361       for (uint32_t i = 0; i < valobj_list.GetSize();) {
362         Status tmp_error;
363         ValueObjectSP valobj_sp(
364             valobj_list.GetValueObjectAtIndex(i)->AddressOf(tmp_error));
365         if (tmp_error.Fail()) {
366           variable_list.RemoveVariableAtIndex(i);
367           valobj_list.RemoveValueObjectAtIndex(i);
368         } else {
369           valobj_list.SetValueObjectAtIndex(i, valobj_sp);
370           ++i;
371         }
372       }
373     } else {
374       error = Status::FromErrorString("unknown error");
375     }
376     return error;
377   } break;
378 
379   default: {
380     static RegularExpression g_regex(
381         llvm::StringRef("^([A-Za-z_:][A-Za-z_0-9:]*)(.*)"));
382     llvm::SmallVector<llvm::StringRef, 2> matches;
383     variable_list.Clear();
384     if (!g_regex.Execute(variable_expr_path, &matches)) {
385       error = Status::FromErrorStringWithFormatv(
386           "unable to extract a variable name from '{0}'", variable_expr_path);
387       return error;
388     }
389     std::string variable_name = matches[1].str();
390     if (!callback(baton, variable_name.c_str(), variable_list)) {
391       error = Status::FromErrorString("unknown error");
392       return error;
393     }
394     uint32_t i = 0;
395     while (i < variable_list.GetSize()) {
396       VariableSP var_sp(variable_list.GetVariableAtIndex(i));
397       ValueObjectSP valobj_sp;
398       if (!var_sp) {
399         variable_list.RemoveVariableAtIndex(i);
400         continue;
401       }
402       ValueObjectSP variable_valobj_sp(
403           ValueObjectVariable::Create(scope, var_sp));
404       if (!variable_valobj_sp) {
405         variable_list.RemoveVariableAtIndex(i);
406         continue;
407       }
408 
409       llvm::StringRef variable_sub_expr_path =
410           variable_expr_path.drop_front(variable_name.size());
411       if (!variable_sub_expr_path.empty()) {
412         valobj_sp = variable_valobj_sp->GetValueForExpressionPath(
413             variable_sub_expr_path);
414         if (!valobj_sp) {
415           error = Status::FromErrorStringWithFormatv(
416               "invalid expression path '{0}' for variable '{1}'",
417               variable_sub_expr_path, var_sp->GetName().GetCString());
418           variable_list.RemoveVariableAtIndex(i);
419           continue;
420         }
421       } else {
422         // Just the name of a variable with no extras
423         valobj_sp = variable_valobj_sp;
424       }
425 
426       valobj_list.Append(valobj_sp);
427       ++i;
428     }
429 
430     if (variable_list.GetSize() > 0) {
431       error.Clear();
432       return error;
433     }
434   } break;
435   }
436   error = Status::FromErrorString("unknown error");
437   return error;
438 }
439 
440 bool Variable::DumpLocations(Stream *s, const Address &address) {
441   SymbolContext sc;
442   CalculateSymbolContext(&sc);
443   ABISP abi;
444   if (m_owner_scope) {
445     ModuleSP module_sp(m_owner_scope->CalculateSymbolContextModule());
446     if (module_sp)
447       abi = ABI::FindPlugin(ProcessSP(), module_sp->GetArchitecture());
448   }
449 
450   const addr_t file_addr = address.GetFileAddress();
451   if (sc.function) {
452     addr_t loclist_base_file_addr = sc.function->GetAddress().GetFileAddress();
453     if (loclist_base_file_addr == LLDB_INVALID_ADDRESS)
454       return false;
455     return m_location_list.DumpLocations(s, eDescriptionLevelBrief,
456                                          loclist_base_file_addr, file_addr,
457                                          abi.get());
458   }
459   return false;
460 }
461 
462 static void PrivateAutoComplete(
463     StackFrame *frame, llvm::StringRef partial_path,
464     const llvm::Twine
465         &prefix_path, // Anything that has been resolved already will be in here
466     const CompilerType &compiler_type, CompletionRequest &request);
467 
468 static void PrivateAutoCompleteMembers(
469     StackFrame *frame, const std::string &partial_member_name,
470     llvm::StringRef partial_path,
471     const llvm::Twine
472         &prefix_path, // Anything that has been resolved already will be in here
473     const CompilerType &compiler_type, CompletionRequest &request) {
474 
475   // We are in a type parsing child members
476   const uint32_t num_bases = compiler_type.GetNumDirectBaseClasses();
477 
478   if (num_bases > 0) {
479     for (uint32_t i = 0; i < num_bases; ++i) {
480       CompilerType base_class_type =
481           compiler_type.GetDirectBaseClassAtIndex(i, nullptr);
482 
483       PrivateAutoCompleteMembers(frame, partial_member_name, partial_path,
484                                  prefix_path,
485                                  base_class_type.GetCanonicalType(), request);
486     }
487   }
488 
489   const uint32_t num_vbases = compiler_type.GetNumVirtualBaseClasses();
490 
491   if (num_vbases > 0) {
492     for (uint32_t i = 0; i < num_vbases; ++i) {
493       CompilerType vbase_class_type =
494           compiler_type.GetVirtualBaseClassAtIndex(i, nullptr);
495 
496       PrivateAutoCompleteMembers(frame, partial_member_name, partial_path,
497                                  prefix_path,
498                                  vbase_class_type.GetCanonicalType(), request);
499     }
500   }
501 
502   // We are in a type parsing child members
503   const uint32_t num_fields = compiler_type.GetNumFields();
504 
505   if (num_fields > 0) {
506     for (uint32_t i = 0; i < num_fields; ++i) {
507       std::string member_name;
508 
509       CompilerType member_compiler_type = compiler_type.GetFieldAtIndex(
510           i, member_name, nullptr, nullptr, nullptr);
511 
512       if (partial_member_name.empty()) {
513         request.AddCompletion((prefix_path + member_name).str());
514       } else if (llvm::StringRef(member_name)
515                      .starts_with(partial_member_name)) {
516         if (member_name == partial_member_name) {
517           PrivateAutoComplete(
518               frame, partial_path,
519               prefix_path + member_name, // Anything that has been resolved
520                                          // already will be in here
521               member_compiler_type.GetCanonicalType(), request);
522         } else if (partial_path.empty()) {
523           request.AddCompletion((prefix_path + member_name).str());
524         }
525       }
526     }
527   }
528 }
529 
530 static void PrivateAutoComplete(
531     StackFrame *frame, llvm::StringRef partial_path,
532     const llvm::Twine
533         &prefix_path, // Anything that has been resolved already will be in here
534     const CompilerType &compiler_type, CompletionRequest &request) {
535   //    printf ("\nPrivateAutoComplete()\n\tprefix_path = '%s'\n\tpartial_path =
536   //    '%s'\n", prefix_path.c_str(), partial_path.c_str());
537   std::string remaining_partial_path;
538 
539   const lldb::TypeClass type_class = compiler_type.GetTypeClass();
540   if (partial_path.empty()) {
541     if (compiler_type.IsValid()) {
542       switch (type_class) {
543       default:
544       case eTypeClassArray:
545       case eTypeClassBlockPointer:
546       case eTypeClassBuiltin:
547       case eTypeClassComplexFloat:
548       case eTypeClassComplexInteger:
549       case eTypeClassEnumeration:
550       case eTypeClassFunction:
551       case eTypeClassMemberPointer:
552       case eTypeClassReference:
553       case eTypeClassTypedef:
554       case eTypeClassVector: {
555         request.AddCompletion(prefix_path.str());
556       } break;
557 
558       case eTypeClassClass:
559       case eTypeClassStruct:
560       case eTypeClassUnion:
561         if (prefix_path.str().back() != '.')
562           request.AddCompletion((prefix_path + ".").str());
563         break;
564 
565       case eTypeClassObjCObject:
566       case eTypeClassObjCInterface:
567         break;
568       case eTypeClassObjCObjectPointer:
569       case eTypeClassPointer: {
570         bool omit_empty_base_classes = true;
571         if (llvm::expectedToStdOptional(
572                 compiler_type.GetNumChildren(omit_empty_base_classes, nullptr))
573                 .value_or(0))
574           request.AddCompletion((prefix_path + "->").str());
575         else {
576           request.AddCompletion(prefix_path.str());
577         }
578       } break;
579       }
580     } else {
581       if (frame) {
582         const bool get_file_globals = true;
583 
584         VariableList *variable_list = frame->GetVariableList(get_file_globals,
585                                                              nullptr);
586 
587         if (variable_list) {
588           for (const VariableSP &var_sp : *variable_list)
589             request.AddCompletion(var_sp->GetName().AsCString());
590         }
591       }
592     }
593   } else {
594     const char ch = partial_path[0];
595     switch (ch) {
596     case '*':
597       if (prefix_path.str().empty()) {
598         PrivateAutoComplete(frame, partial_path.substr(1), "*", compiler_type,
599                             request);
600       }
601       break;
602 
603     case '&':
604       if (prefix_path.isTriviallyEmpty()) {
605         PrivateAutoComplete(frame, partial_path.substr(1), std::string("&"),
606                             compiler_type, request);
607       }
608       break;
609 
610     case '-':
611       if (partial_path.size() > 1 && partial_path[1] == '>' &&
612           !prefix_path.str().empty()) {
613         switch (type_class) {
614         case lldb::eTypeClassPointer: {
615           CompilerType pointee_type(compiler_type.GetPointeeType());
616           if (partial_path.size() > 2 && partial_path[2]) {
617             // If there is more after the "->", then search deeper
618             PrivateAutoComplete(frame, partial_path.substr(2),
619                                 prefix_path + "->",
620                                 pointee_type.GetCanonicalType(), request);
621           } else {
622             // Nothing after the "->", so list all members
623             PrivateAutoCompleteMembers(
624                 frame, std::string(), std::string(), prefix_path + "->",
625                 pointee_type.GetCanonicalType(), request);
626           }
627         } break;
628         default:
629           break;
630         }
631       }
632       break;
633 
634     case '.':
635       if (compiler_type.IsValid()) {
636         switch (type_class) {
637         case lldb::eTypeClassUnion:
638         case lldb::eTypeClassStruct:
639         case lldb::eTypeClassClass:
640           if (partial_path.size() > 1 && partial_path[1]) {
641             // If there is more after the ".", then search deeper
642             PrivateAutoComplete(frame, partial_path.substr(1),
643                                 prefix_path + ".", compiler_type, request);
644 
645           } else {
646             // Nothing after the ".", so list all members
647             PrivateAutoCompleteMembers(frame, std::string(), partial_path,
648                                        prefix_path + ".", compiler_type,
649                                        request);
650           }
651           break;
652         default:
653           break;
654         }
655       }
656       break;
657     default:
658       if (isalpha(ch) || ch == '_' || ch == '$') {
659         const size_t partial_path_len = partial_path.size();
660         size_t pos = 1;
661         while (pos < partial_path_len) {
662           const char curr_ch = partial_path[pos];
663           if (isalnum(curr_ch) || curr_ch == '_' || curr_ch == '$') {
664             ++pos;
665             continue;
666           }
667           break;
668         }
669 
670         std::string token(std::string(partial_path), 0, pos);
671         remaining_partial_path = std::string(partial_path.substr(pos));
672 
673         if (compiler_type.IsValid()) {
674           PrivateAutoCompleteMembers(frame, token, remaining_partial_path,
675                                      prefix_path, compiler_type, request);
676         } else if (frame) {
677           // We haven't found our variable yet
678           const bool get_file_globals = true;
679 
680           VariableList *variable_list =
681               frame->GetVariableList(get_file_globals, nullptr);
682 
683           if (!variable_list)
684             break;
685 
686           for (VariableSP var_sp : *variable_list) {
687 
688             if (!var_sp)
689               continue;
690 
691             llvm::StringRef variable_name = var_sp->GetName().GetStringRef();
692             if (variable_name.starts_with(token)) {
693               if (variable_name == token) {
694                 Type *variable_type = var_sp->GetType();
695                 if (variable_type) {
696                   CompilerType variable_compiler_type(
697                       variable_type->GetForwardCompilerType());
698                   PrivateAutoComplete(
699                       frame, remaining_partial_path,
700                       prefix_path + token, // Anything that has been resolved
701                                            // already will be in here
702                       variable_compiler_type.GetCanonicalType(), request);
703                 } else {
704                   request.AddCompletion((prefix_path + variable_name).str());
705                 }
706               } else if (remaining_partial_path.empty()) {
707                 request.AddCompletion((prefix_path + variable_name).str());
708               }
709             }
710           }
711         }
712       }
713       break;
714     }
715   }
716 }
717 
718 void Variable::AutoComplete(const ExecutionContext &exe_ctx,
719                             CompletionRequest &request) {
720   CompilerType compiler_type;
721 
722   PrivateAutoComplete(exe_ctx.GetFramePtr(), request.GetCursorArgumentPrefix(),
723                       "", compiler_type, request);
724 }
725