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