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