1254721Semaste//===-- Variable.cpp --------------------------------------------*- C++ -*-===//
2254721Semaste//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6254721Semaste//
7254721Semaste//===----------------------------------------------------------------------===//
8254721Semaste
9254721Semaste#include "lldb/Symbol/Variable.h"
10254721Semaste
11254721Semaste#include "lldb/Core/Module.h"
12254721Semaste#include "lldb/Core/ValueObject.h"
13254721Semaste#include "lldb/Core/ValueObjectVariable.h"
14254721Semaste#include "lldb/Symbol/Block.h"
15314564Sdim#include "lldb/Symbol/CompileUnit.h"
16296417Sdim#include "lldb/Symbol/CompilerDecl.h"
17296417Sdim#include "lldb/Symbol/CompilerDeclContext.h"
18254721Semaste#include "lldb/Symbol/Function.h"
19254721Semaste#include "lldb/Symbol/SymbolContext.h"
20296417Sdim#include "lldb/Symbol/SymbolFile.h"
21254721Semaste#include "lldb/Symbol/Type.h"
22296417Sdim#include "lldb/Symbol/TypeSystem.h"
23254721Semaste#include "lldb/Symbol/VariableList.h"
24254721Semaste#include "lldb/Target/ABI.h"
25254721Semaste#include "lldb/Target/Process.h"
26254721Semaste#include "lldb/Target/RegisterContext.h"
27254721Semaste#include "lldb/Target/StackFrame.h"
28314564Sdim#include "lldb/Target/Target.h"
29254721Semaste#include "lldb/Target/Thread.h"
30321369Sdim#include "lldb/Utility/RegularExpression.h"
31321369Sdim#include "lldb/Utility/Stream.h"
32254721Semaste
33314564Sdim#include "llvm/ADT/Twine.h"
34314564Sdim
35254721Semasteusing namespace lldb;
36254721Semasteusing namespace lldb_private;
37254721Semaste
38360784SdimVariable::Variable(lldb::user_id_t uid, const char *name, const char *mangled,
39360784Sdim                   const lldb::SymbolFileTypeSP &symfile_type_sp,
40360784Sdim                   ValueType scope, SymbolContextScope *context,
41360784Sdim                   const RangeList &scope_range, Declaration *decl_ptr,
42360784Sdim                   const DWARFExpression &location, bool external,
43360784Sdim                   bool artificial, bool static_member)
44314564Sdim    : UserID(uid), m_name(name), m_mangled(ConstString(mangled)),
45314564Sdim      m_symfile_type_sp(symfile_type_sp), m_scope(scope),
46314564Sdim      m_owner_scope(context), m_scope_range(scope_range),
47314564Sdim      m_declaration(decl_ptr), m_location(location), m_external(external),
48344779Sdim      m_artificial(artificial), m_loc_is_const_data(false),
49344779Sdim      m_static_member(static_member) {}
50254721Semaste
51314564SdimVariable::~Variable() {}
52254721Semaste
53314564Sdimlldb::LanguageType Variable::GetLanguage() const {
54360784Sdim  lldb::LanguageType lang = m_mangled.GuessLanguage();
55360784Sdim  if (lang != lldb::eLanguageTypeUnknown)
56360784Sdim    return lang;
57360784Sdim
58360784Sdim  if (auto *func = m_owner_scope->CalculateSymbolContextFunction()) {
59360784Sdim    if ((lang = func->GetLanguage()) != lldb::eLanguageTypeUnknown)
60360784Sdim      return lang;
61360784Sdim  } else if (auto *comp_unit =
62360784Sdim                 m_owner_scope->CalculateSymbolContextCompileUnit()) {
63360784Sdim    if ((lang = comp_unit->GetLanguage()) != lldb::eLanguageTypeUnknown)
64360784Sdim      return lang;
65360784Sdim  }
66360784Sdim
67314564Sdim  return lldb::eLanguageTypeUnknown;
68288943Sdim}
69254721Semaste
70314564SdimConstString Variable::GetName() const {
71314564Sdim  ConstString name = m_mangled.GetName(GetLanguage());
72314564Sdim  if (name)
73314564Sdim    return name;
74314564Sdim  return m_name;
75254721Semaste}
76254721Semaste
77314564SdimConstString Variable::GetUnqualifiedName() const { return m_name; }
78296417Sdim
79353358Sdimbool Variable::NameMatches(ConstString name) const {
80314564Sdim  if (m_name == name)
81314564Sdim    return true;
82314564Sdim  SymbolContext variable_sc;
83314564Sdim  m_owner_scope->CalculateSymbolContext(&variable_sc);
84296417Sdim
85314564Sdim  LanguageType language = eLanguageTypeUnknown;
86314564Sdim  if (variable_sc.comp_unit)
87314564Sdim    language = variable_sc.comp_unit->GetLanguage();
88314564Sdim  return m_mangled.NameMatches(name, language);
89288943Sdim}
90314564Sdimbool Variable::NameMatches(const RegularExpression &regex) const {
91314564Sdim  if (regex.Execute(m_name.AsCString()))
92314564Sdim    return true;
93314564Sdim  if (m_mangled)
94314564Sdim    return m_mangled.NameMatches(regex, GetLanguage());
95314564Sdim  return false;
96254721Semaste}
97254721Semaste
98314564SdimType *Variable::GetType() {
99314564Sdim  if (m_symfile_type_sp)
100314564Sdim    return m_symfile_type_sp->GetType();
101314564Sdim  return nullptr;
102254721Semaste}
103254721Semaste
104314564Sdimvoid Variable::Dump(Stream *s, bool show_context) const {
105314564Sdim  s->Printf("%p: ", static_cast<const void *>(this));
106314564Sdim  s->Indent();
107314564Sdim  *s << "Variable" << (const UserID &)*this;
108254721Semaste
109314564Sdim  if (m_name)
110314564Sdim    *s << ", name = \"" << m_name << "\"";
111254721Semaste
112314564Sdim  if (m_symfile_type_sp) {
113314564Sdim    Type *type = m_symfile_type_sp->GetType();
114314564Sdim    if (type) {
115360784Sdim      s->Format(", type = {{{0:x-16}} {1} (", type->GetID(), type);
116314564Sdim      type->DumpTypeName(s);
117314564Sdim      s->PutChar(')');
118254721Semaste    }
119314564Sdim  }
120254721Semaste
121314564Sdim  if (m_scope != eValueTypeInvalid) {
122314564Sdim    s->PutCString(", scope = ");
123314564Sdim    switch (m_scope) {
124314564Sdim    case eValueTypeVariableGlobal:
125314564Sdim      s->PutCString(m_external ? "global" : "static");
126314564Sdim      break;
127314564Sdim    case eValueTypeVariableArgument:
128314564Sdim      s->PutCString("parameter");
129314564Sdim      break;
130314564Sdim    case eValueTypeVariableLocal:
131314564Sdim      s->PutCString("local");
132314564Sdim      break;
133314564Sdim    case eValueTypeVariableThreadLocal:
134314564Sdim      s->PutCString("thread local");
135314564Sdim      break;
136314564Sdim    default:
137360784Sdim      s->AsRawOstream() << "??? (" << m_scope << ')';
138254721Semaste    }
139314564Sdim  }
140254721Semaste
141314564Sdim  if (show_context && m_owner_scope != nullptr) {
142314564Sdim    s->PutCString(", context = ( ");
143314564Sdim    m_owner_scope->DumpSymbolContext(s);
144314564Sdim    s->PutCString(" )");
145314564Sdim  }
146254721Semaste
147314564Sdim  bool show_fullpaths = false;
148314564Sdim  m_declaration.Dump(s, show_fullpaths);
149254721Semaste
150314564Sdim  if (m_location.IsValid()) {
151314564Sdim    s->PutCString(", location = ");
152314564Sdim    lldb::addr_t loclist_base_addr = LLDB_INVALID_ADDRESS;
153314564Sdim    if (m_location.IsLocationList()) {
154314564Sdim      SymbolContext variable_sc;
155314564Sdim      m_owner_scope->CalculateSymbolContext(&variable_sc);
156314564Sdim      if (variable_sc.function)
157314564Sdim        loclist_base_addr = variable_sc.function->GetAddressRange()
158314564Sdim                                .GetBaseAddress()
159314564Sdim                                .GetFileAddress();
160254721Semaste    }
161344779Sdim    ABISP abi;
162314564Sdim    if (m_owner_scope) {
163314564Sdim      ModuleSP module_sp(m_owner_scope->CalculateSymbolContextModule());
164314564Sdim      if (module_sp)
165344779Sdim        abi = ABI::FindPlugin(ProcessSP(), module_sp->GetArchitecture());
166314564Sdim    }
167314564Sdim    m_location.GetDescription(s, lldb::eDescriptionLevelBrief,
168344779Sdim                              loclist_base_addr, abi.get());
169314564Sdim  }
170254721Semaste
171314564Sdim  if (m_external)
172314564Sdim    s->PutCString(", external");
173254721Semaste
174314564Sdim  if (m_artificial)
175314564Sdim    s->PutCString(", artificial");
176254721Semaste
177314564Sdim  s->EOL();
178254721Semaste}
179254721Semaste
180314564Sdimbool Variable::DumpDeclaration(Stream *s, bool show_fullpaths,
181314564Sdim                               bool show_module) {
182314564Sdim  bool dumped_declaration_info = false;
183314564Sdim  if (m_owner_scope) {
184314564Sdim    SymbolContext sc;
185314564Sdim    m_owner_scope->CalculateSymbolContext(&sc);
186314564Sdim    sc.block = nullptr;
187314564Sdim    sc.line_entry.Clear();
188314564Sdim    bool show_inlined_frames = false;
189314564Sdim    const bool show_function_arguments = true;
190314564Sdim    const bool show_function_name = true;
191314564Sdim
192314564Sdim    dumped_declaration_info = sc.DumpStopContext(
193314564Sdim        s, nullptr, Address(), show_fullpaths, show_module, show_inlined_frames,
194314564Sdim        show_function_arguments, show_function_name);
195314564Sdim
196314564Sdim    if (sc.function)
197314564Sdim      s->PutChar(':');
198314564Sdim  }
199314564Sdim  if (m_declaration.DumpStopContext(s, false))
200314564Sdim    dumped_declaration_info = true;
201314564Sdim  return dumped_declaration_info;
202254721Semaste}
203254721Semaste
204314564Sdimsize_t Variable::MemorySize() const { return sizeof(Variable); }
205314564Sdim
206314564SdimCompilerDeclContext Variable::GetDeclContext() {
207314564Sdim  Type *type = GetType();
208314564Sdim  if (type)
209314564Sdim    return type->GetSymbolFile()->GetDeclContextContainingUID(GetID());
210314564Sdim  return CompilerDeclContext();
211254721Semaste}
212254721Semaste
213314564SdimCompilerDecl Variable::GetDecl() {
214314564Sdim  Type *type = GetType();
215314564Sdim  return type ? type->GetSymbolFile()->GetDeclForUID(GetID()) : CompilerDecl();
216296417Sdim}
217254721Semaste
218314564Sdimvoid Variable::CalculateSymbolContext(SymbolContext *sc) {
219314564Sdim  if (m_owner_scope) {
220314564Sdim    m_owner_scope->CalculateSymbolContext(sc);
221314564Sdim    sc->variable = this;
222314564Sdim  } else
223314564Sdim    sc->Clear(false);
224296417Sdim}
225296417Sdim
226314564Sdimbool Variable::LocationIsValidForFrame(StackFrame *frame) {
227314564Sdim  // Is the variable is described by a single location?
228314564Sdim  if (!m_location.IsLocationList()) {
229314564Sdim    // Yes it is, the location is valid.
230314564Sdim    return true;
231314564Sdim  }
232314564Sdim
233314564Sdim  if (frame) {
234314564Sdim    Function *function =
235314564Sdim        frame->GetSymbolContext(eSymbolContextFunction).function;
236314564Sdim    if (function) {
237314564Sdim      TargetSP target_sp(frame->CalculateTarget());
238314564Sdim
239314564Sdim      addr_t loclist_base_load_addr =
240314564Sdim          function->GetAddressRange().GetBaseAddress().GetLoadAddress(
241314564Sdim              target_sp.get());
242314564Sdim      if (loclist_base_load_addr == LLDB_INVALID_ADDRESS)
243314564Sdim        return false;
244341825Sdim      // It is a location list. We just need to tell if the location list
245341825Sdim      // contains the current address when converted to a load address
246314564Sdim      return m_location.LocationListContainsAddress(
247314564Sdim          loclist_base_load_addr,
248314564Sdim          frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get()));
249288943Sdim    }
250314564Sdim  }
251314564Sdim  return false;
252254721Semaste}
253254721Semaste
254314564Sdimbool Variable::LocationIsValidForAddress(const Address &address) {
255341825Sdim  // Be sure to resolve the address to section offset prior to calling this
256341825Sdim  // function.
257314564Sdim  if (address.IsSectionOffset()) {
258314564Sdim    SymbolContext sc;
259314564Sdim    CalculateSymbolContext(&sc);
260314564Sdim    if (sc.module_sp == address.GetModule()) {
261314564Sdim      // Is the variable is described by a single location?
262314564Sdim      if (!m_location.IsLocationList()) {
263314564Sdim        // Yes it is, the location is valid.
264254721Semaste        return true;
265314564Sdim      }
266254721Semaste
267314564Sdim      if (sc.function) {
268314564Sdim        addr_t loclist_base_file_addr =
269314564Sdim            sc.function->GetAddressRange().GetBaseAddress().GetFileAddress();
270314564Sdim        if (loclist_base_file_addr == LLDB_INVALID_ADDRESS)
271314564Sdim          return false;
272341825Sdim        // It is a location list. We just need to tell if the location list
273341825Sdim        // contains the current address when converted to a load address
274314564Sdim        return m_location.LocationListContainsAddress(loclist_base_file_addr,
275314564Sdim                                                      address.GetFileAddress());
276314564Sdim      }
277254721Semaste    }
278314564Sdim  }
279314564Sdim  return false;
280254721Semaste}
281254721Semaste
282314564Sdimbool Variable::IsInScope(StackFrame *frame) {
283314564Sdim  switch (m_scope) {
284314564Sdim  case eValueTypeRegister:
285314564Sdim  case eValueTypeRegisterSet:
286314564Sdim    return frame != nullptr;
287254721Semaste
288314564Sdim  case eValueTypeConstResult:
289314564Sdim  case eValueTypeVariableGlobal:
290314564Sdim  case eValueTypeVariableStatic:
291314564Sdim  case eValueTypeVariableThreadLocal:
292314564Sdim    return true;
293254721Semaste
294314564Sdim  case eValueTypeVariableArgument:
295314564Sdim  case eValueTypeVariableLocal:
296314564Sdim    if (frame) {
297341825Sdim      // We don't have a location list, we just need to see if the block that
298341825Sdim      // this variable was defined in is currently
299314564Sdim      Block *deepest_frame_block =
300314564Sdim          frame->GetSymbolContext(eSymbolContextBlock).block;
301314564Sdim      if (deepest_frame_block) {
302314564Sdim        SymbolContext variable_sc;
303314564Sdim        CalculateSymbolContext(&variable_sc);
304254721Semaste
305314564Sdim        // Check for static or global variable defined at the compile unit
306314564Sdim        // level that wasn't defined in a block
307314564Sdim        if (variable_sc.block == nullptr)
308314564Sdim          return true;
309309124Sdim
310314564Sdim        // Check if the variable is valid in the current block
311314564Sdim        if (variable_sc.block != deepest_frame_block &&
312314564Sdim            !variable_sc.block->Contains(deepest_frame_block))
313314564Sdim          return false;
314254721Semaste
315314564Sdim        // If no scope range is specified then it means that the scope is the
316341825Sdim        // same as the scope of the enclosing lexical block.
317314564Sdim        if (m_scope_range.IsEmpty())
318314564Sdim          return true;
319309124Sdim
320314564Sdim        addr_t file_address = frame->GetFrameCodeAddress().GetFileAddress();
321314564Sdim        return m_scope_range.FindEntryThatContains(file_address) != nullptr;
322314564Sdim      }
323314564Sdim    }
324314564Sdim    break;
325309124Sdim
326314564Sdim  default:
327314564Sdim    break;
328314564Sdim  }
329314564Sdim  return false;
330314564Sdim}
331314564Sdim
332321369SdimStatus Variable::GetValuesForVariableExpressionPath(
333314564Sdim    llvm::StringRef variable_expr_path, ExecutionContextScope *scope,
334314564Sdim    GetVariableCallback callback, void *baton, VariableList &variable_list,
335314564Sdim    ValueObjectList &valobj_list) {
336321369Sdim  Status error;
337314564Sdim  if (!callback || variable_expr_path.empty()) {
338314564Sdim    error.SetErrorString("unknown error");
339314564Sdim    return error;
340314564Sdim  }
341314564Sdim
342314564Sdim  switch (variable_expr_path.front()) {
343314564Sdim  case '*':
344314564Sdim    error = Variable::GetValuesForVariableExpressionPath(
345314564Sdim        variable_expr_path.drop_front(), scope, callback, baton, variable_list,
346314564Sdim        valobj_list);
347314564Sdim    if (error.Fail()) {
348314564Sdim      error.SetErrorString("unknown error");
349314564Sdim      return error;
350314564Sdim    }
351314564Sdim    for (uint32_t i = 0; i < valobj_list.GetSize();) {
352321369Sdim      Status tmp_error;
353314564Sdim      ValueObjectSP valobj_sp(
354314564Sdim          valobj_list.GetValueObjectAtIndex(i)->Dereference(tmp_error));
355314564Sdim      if (tmp_error.Fail()) {
356314564Sdim        variable_list.RemoveVariableAtIndex(i);
357314564Sdim        valobj_list.RemoveValueObjectAtIndex(i);
358314564Sdim      } else {
359314564Sdim        valobj_list.SetValueObjectAtIndex(i, valobj_sp);
360314564Sdim        ++i;
361314564Sdim      }
362314564Sdim    }
363314564Sdim    return error;
364314564Sdim  case '&': {
365314564Sdim    error = Variable::GetValuesForVariableExpressionPath(
366314564Sdim        variable_expr_path.drop_front(), scope, callback, baton, variable_list,
367314564Sdim        valobj_list);
368314564Sdim    if (error.Success()) {
369314564Sdim      for (uint32_t i = 0; i < valobj_list.GetSize();) {
370321369Sdim        Status tmp_error;
371314564Sdim        ValueObjectSP valobj_sp(
372314564Sdim            valobj_list.GetValueObjectAtIndex(i)->AddressOf(tmp_error));
373314564Sdim        if (tmp_error.Fail()) {
374314564Sdim          variable_list.RemoveVariableAtIndex(i);
375314564Sdim          valobj_list.RemoveValueObjectAtIndex(i);
376314564Sdim        } else {
377314564Sdim          valobj_list.SetValueObjectAtIndex(i, valobj_sp);
378314564Sdim          ++i;
379254721Semaste        }
380314564Sdim      }
381314564Sdim    } else {
382314564Sdim      error.SetErrorString("unknown error");
383314564Sdim    }
384314564Sdim    return error;
385314564Sdim  } break;
386254721Semaste
387314564Sdim  default: {
388314564Sdim    static RegularExpression g_regex(
389314564Sdim        llvm::StringRef("^([A-Za-z_:][A-Za-z_0-9:]*)(.*)"));
390360784Sdim    llvm::SmallVector<llvm::StringRef, 2> matches;
391314564Sdim    variable_list.Clear();
392360784Sdim    if (!g_regex.Execute(variable_expr_path, &matches)) {
393314564Sdim      error.SetErrorStringWithFormat(
394314564Sdim          "unable to extract a variable name from '%s'",
395314564Sdim          variable_expr_path.str().c_str());
396314564Sdim      return error;
397254721Semaste    }
398360784Sdim    std::string variable_name = matches[1].str();
399314564Sdim    if (!callback(baton, variable_name.c_str(), variable_list)) {
400314564Sdim      error.SetErrorString("unknown error");
401314564Sdim      return error;
402314564Sdim    }
403314564Sdim    uint32_t i = 0;
404314564Sdim    while (i < variable_list.GetSize()) {
405314564Sdim      VariableSP var_sp(variable_list.GetVariableAtIndex(i));
406314564Sdim      ValueObjectSP valobj_sp;
407314564Sdim      if (!var_sp) {
408314564Sdim        variable_list.RemoveVariableAtIndex(i);
409314564Sdim        continue;
410314564Sdim      }
411314564Sdim      ValueObjectSP variable_valobj_sp(
412314564Sdim          ValueObjectVariable::Create(scope, var_sp));
413314564Sdim      if (!variable_valobj_sp) {
414314564Sdim        variable_list.RemoveVariableAtIndex(i);
415314564Sdim        continue;
416314564Sdim      }
417254721Semaste
418314564Sdim      llvm::StringRef variable_sub_expr_path =
419314564Sdim          variable_expr_path.drop_front(variable_name.size());
420314564Sdim      if (!variable_sub_expr_path.empty()) {
421314564Sdim        valobj_sp = variable_valobj_sp->GetValueForExpressionPath(
422327952Sdim            variable_sub_expr_path);
423314564Sdim        if (!valobj_sp) {
424314564Sdim          error.SetErrorStringWithFormat(
425314564Sdim              "invalid expression path '%s' for variable '%s'",
426314564Sdim              variable_sub_expr_path.str().c_str(),
427314564Sdim              var_sp->GetName().GetCString());
428314564Sdim          variable_list.RemoveVariableAtIndex(i);
429314564Sdim          continue;
430314564Sdim        }
431314564Sdim      } else {
432314564Sdim        // Just the name of a variable with no extras
433314564Sdim        valobj_sp = variable_valobj_sp;
434314564Sdim      }
435254721Semaste
436314564Sdim      valobj_list.Append(valobj_sp);
437314564Sdim      ++i;
438254721Semaste    }
439314564Sdim
440314564Sdim    if (variable_list.GetSize() > 0) {
441314564Sdim      error.Clear();
442314564Sdim      return error;
443314564Sdim    }
444314564Sdim  } break;
445314564Sdim  }
446314564Sdim  error.SetErrorString("unknown error");
447314564Sdim  return error;
448254721Semaste}
449254721Semaste
450314564Sdimbool Variable::DumpLocationForAddress(Stream *s, const Address &address) {
451341825Sdim  // Be sure to resolve the address to section offset prior to calling this
452341825Sdim  // function.
453314564Sdim  if (address.IsSectionOffset()) {
454314564Sdim    SymbolContext sc;
455314564Sdim    CalculateSymbolContext(&sc);
456314564Sdim    if (sc.module_sp == address.GetModule()) {
457344779Sdim      ABISP abi;
458314564Sdim      if (m_owner_scope) {
459314564Sdim        ModuleSP module_sp(m_owner_scope->CalculateSymbolContextModule());
460314564Sdim        if (module_sp)
461344779Sdim          abi = ABI::FindPlugin(ProcessSP(), module_sp->GetArchitecture());
462314564Sdim      }
463254721Semaste
464314564Sdim      const addr_t file_addr = address.GetFileAddress();
465314564Sdim      if (sc.function) {
466314564Sdim        if (sc.function->GetAddressRange().ContainsFileAddress(address)) {
467314564Sdim          addr_t loclist_base_file_addr =
468314564Sdim              sc.function->GetAddressRange().GetBaseAddress().GetFileAddress();
469314564Sdim          if (loclist_base_file_addr == LLDB_INVALID_ADDRESS)
470314564Sdim            return false;
471314564Sdim          return m_location.DumpLocationForAddress(s, eDescriptionLevelBrief,
472314564Sdim                                                   loclist_base_file_addr,
473344779Sdim                                                   file_addr, abi.get());
474254721Semaste        }
475314564Sdim      }
476344779Sdim      return m_location.DumpLocationForAddress(s, eDescriptionLevelBrief,
477344779Sdim                                               LLDB_INVALID_ADDRESS, file_addr,
478344779Sdim                                               abi.get());
479254721Semaste    }
480314564Sdim  }
481314564Sdim  return false;
482254721Semaste}
483254721Semaste
484314564Sdimstatic void PrivateAutoComplete(
485314564Sdim    StackFrame *frame, llvm::StringRef partial_path,
486314564Sdim    const llvm::Twine
487314564Sdim        &prefix_path, // Anything that has been resolved already will be in here
488360784Sdim    const CompilerType &compiler_type, CompletionRequest &request);
489254721Semaste
490314564Sdimstatic void PrivateAutoCompleteMembers(
491314564Sdim    StackFrame *frame, const std::string &partial_member_name,
492314564Sdim    llvm::StringRef partial_path,
493314564Sdim    const llvm::Twine
494314564Sdim        &prefix_path, // Anything that has been resolved already will be in here
495360784Sdim    const CompilerType &compiler_type, CompletionRequest &request) {
496254721Semaste
497314564Sdim  // We are in a type parsing child members
498314564Sdim  const uint32_t num_bases = compiler_type.GetNumDirectBaseClasses();
499254721Semaste
500314564Sdim  if (num_bases > 0) {
501314564Sdim    for (uint32_t i = 0; i < num_bases; ++i) {
502314564Sdim      CompilerType base_class_type =
503314564Sdim          compiler_type.GetDirectBaseClassAtIndex(i, nullptr);
504314564Sdim
505360784Sdim      PrivateAutoCompleteMembers(frame, partial_member_name, partial_path,
506360784Sdim                                 prefix_path,
507360784Sdim                                 base_class_type.GetCanonicalType(), request);
508254721Semaste    }
509314564Sdim  }
510254721Semaste
511314564Sdim  const uint32_t num_vbases = compiler_type.GetNumVirtualBaseClasses();
512314564Sdim
513314564Sdim  if (num_vbases > 0) {
514314564Sdim    for (uint32_t i = 0; i < num_vbases; ++i) {
515314564Sdim      CompilerType vbase_class_type =
516314564Sdim          compiler_type.GetVirtualBaseClassAtIndex(i, nullptr);
517314564Sdim
518360784Sdim      PrivateAutoCompleteMembers(frame, partial_member_name, partial_path,
519360784Sdim                                 prefix_path,
520360784Sdim                                 vbase_class_type.GetCanonicalType(), request);
521254721Semaste    }
522314564Sdim  }
523254721Semaste
524314564Sdim  // We are in a type parsing child members
525314564Sdim  const uint32_t num_fields = compiler_type.GetNumFields();
526314564Sdim
527314564Sdim  if (num_fields > 0) {
528314564Sdim    for (uint32_t i = 0; i < num_fields; ++i) {
529314564Sdim      std::string member_name;
530314564Sdim
531314564Sdim      CompilerType member_compiler_type = compiler_type.GetFieldAtIndex(
532314564Sdim          i, member_name, nullptr, nullptr, nullptr);
533314564Sdim
534314564Sdim      if (partial_member_name.empty() ||
535314564Sdim          member_name.find(partial_member_name) == 0) {
536314564Sdim        if (member_name == partial_member_name) {
537314564Sdim          PrivateAutoComplete(
538314564Sdim              frame, partial_path,
539314564Sdim              prefix_path + member_name, // Anything that has been resolved
540314564Sdim                                         // already will be in here
541360784Sdim              member_compiler_type.GetCanonicalType(), request);
542314564Sdim        } else {
543360784Sdim          request.AddCompletion((prefix_path + member_name).str());
544254721Semaste        }
545314564Sdim      }
546254721Semaste    }
547314564Sdim  }
548254721Semaste}
549254721Semaste
550314564Sdimstatic void PrivateAutoComplete(
551314564Sdim    StackFrame *frame, llvm::StringRef partial_path,
552314564Sdim    const llvm::Twine
553314564Sdim        &prefix_path, // Anything that has been resolved already will be in here
554360784Sdim    const CompilerType &compiler_type, CompletionRequest &request) {
555314564Sdim  //    printf ("\nPrivateAutoComplete()\n\tprefix_path = '%s'\n\tpartial_path =
556314564Sdim  //    '%s'\n", prefix_path.c_str(), partial_path.c_str());
557314564Sdim  std::string remaining_partial_path;
558254721Semaste
559314564Sdim  const lldb::TypeClass type_class = compiler_type.GetTypeClass();
560314564Sdim  if (partial_path.empty()) {
561314564Sdim    if (compiler_type.IsValid()) {
562314564Sdim      switch (type_class) {
563314564Sdim      default:
564314564Sdim      case eTypeClassArray:
565314564Sdim      case eTypeClassBlockPointer:
566314564Sdim      case eTypeClassBuiltin:
567314564Sdim      case eTypeClassComplexFloat:
568314564Sdim      case eTypeClassComplexInteger:
569314564Sdim      case eTypeClassEnumeration:
570314564Sdim      case eTypeClassFunction:
571314564Sdim      case eTypeClassMemberPointer:
572314564Sdim      case eTypeClassReference:
573314564Sdim      case eTypeClassTypedef:
574314564Sdim      case eTypeClassVector: {
575360784Sdim        request.AddCompletion(prefix_path.str());
576314564Sdim      } break;
577254721Semaste
578314564Sdim      case eTypeClassClass:
579314564Sdim      case eTypeClassStruct:
580314564Sdim      case eTypeClassUnion:
581314564Sdim        if (prefix_path.str().back() != '.')
582360784Sdim          request.AddCompletion((prefix_path + ".").str());
583314564Sdim        break;
584314564Sdim
585314564Sdim      case eTypeClassObjCObject:
586314564Sdim      case eTypeClassObjCInterface:
587314564Sdim        break;
588314564Sdim      case eTypeClassObjCObjectPointer:
589314564Sdim      case eTypeClassPointer: {
590314564Sdim        bool omit_empty_base_classes = true;
591344779Sdim        if (compiler_type.GetNumChildren(omit_empty_base_classes, nullptr) > 0)
592360784Sdim          request.AddCompletion((prefix_path + "->").str());
593314564Sdim        else {
594360784Sdim          request.AddCompletion(prefix_path.str());
595254721Semaste        }
596314564Sdim      } break;
597314564Sdim      }
598314564Sdim    } else {
599314564Sdim      if (frame) {
600314564Sdim        const bool get_file_globals = true;
601314564Sdim
602314564Sdim        VariableList *variable_list = frame->GetVariableList(get_file_globals);
603314564Sdim
604314564Sdim        if (variable_list) {
605360784Sdim          for (const VariableSP &var_sp : *variable_list)
606360784Sdim            request.AddCompletion(var_sp->GetName().AsCString());
607254721Semaste        }
608314564Sdim      }
609254721Semaste    }
610314564Sdim  } else {
611314564Sdim    const char ch = partial_path[0];
612314564Sdim    switch (ch) {
613314564Sdim    case '*':
614314564Sdim      if (prefix_path.str().empty()) {
615314564Sdim        PrivateAutoComplete(frame, partial_path.substr(1), "*", compiler_type,
616360784Sdim                            request);
617314564Sdim      }
618314564Sdim      break;
619254721Semaste
620314564Sdim    case '&':
621314564Sdim      if (prefix_path.isTriviallyEmpty()) {
622314564Sdim        PrivateAutoComplete(frame, partial_path.substr(1), std::string("&"),
623360784Sdim                            compiler_type, request);
624314564Sdim      }
625314564Sdim      break;
626314564Sdim
627314564Sdim    case '-':
628341825Sdim      if (partial_path.size() > 1 && partial_path[1] == '>' &&
629341825Sdim          !prefix_path.str().empty()) {
630314564Sdim        switch (type_class) {
631314564Sdim        case lldb::eTypeClassPointer: {
632314564Sdim          CompilerType pointee_type(compiler_type.GetPointeeType());
633341825Sdim          if (partial_path.size() > 2 && partial_path[2]) {
634314564Sdim            // If there is more after the "->", then search deeper
635360784Sdim            PrivateAutoComplete(frame, partial_path.substr(2),
636360784Sdim                                prefix_path + "->",
637360784Sdim                                pointee_type.GetCanonicalType(), request);
638314564Sdim          } else {
639314564Sdim            // Nothing after the "->", so list all members
640314564Sdim            PrivateAutoCompleteMembers(
641314564Sdim                frame, std::string(), std::string(), prefix_path + "->",
642360784Sdim                pointee_type.GetCanonicalType(), request);
643314564Sdim          }
644314564Sdim        } break;
645314564Sdim        default:
646314564Sdim          break;
647314564Sdim        }
648314564Sdim      }
649314564Sdim      break;
650314564Sdim
651314564Sdim    case '.':
652314564Sdim      if (compiler_type.IsValid()) {
653314564Sdim        switch (type_class) {
654314564Sdim        case lldb::eTypeClassUnion:
655314564Sdim        case lldb::eTypeClassStruct:
656314564Sdim        case lldb::eTypeClassClass:
657341825Sdim          if (partial_path.size() > 1 && partial_path[1]) {
658314564Sdim            // If there is more after the ".", then search deeper
659314564Sdim            PrivateAutoComplete(frame, partial_path.substr(1),
660360784Sdim                                prefix_path + ".", compiler_type, request);
661314564Sdim
662314564Sdim          } else {
663314564Sdim            // Nothing after the ".", so list all members
664314564Sdim            PrivateAutoCompleteMembers(frame, std::string(), partial_path,
665314564Sdim                                       prefix_path + ".", compiler_type,
666360784Sdim                                       request);
667314564Sdim          }
668314564Sdim          break;
669314564Sdim        default:
670314564Sdim          break;
671314564Sdim        }
672314564Sdim      }
673314564Sdim      break;
674314564Sdim    default:
675314564Sdim      if (isalpha(ch) || ch == '_' || ch == '$') {
676314564Sdim        const size_t partial_path_len = partial_path.size();
677314564Sdim        size_t pos = 1;
678314564Sdim        while (pos < partial_path_len) {
679314564Sdim          const char curr_ch = partial_path[pos];
680314564Sdim          if (isalnum(curr_ch) || curr_ch == '_' || curr_ch == '$') {
681314564Sdim            ++pos;
682314564Sdim            continue;
683314564Sdim          }
684314564Sdim          break;
685314564Sdim        }
686314564Sdim
687314564Sdim        std::string token(partial_path, 0, pos);
688314564Sdim        remaining_partial_path = partial_path.substr(pos);
689314564Sdim
690314564Sdim        if (compiler_type.IsValid()) {
691314564Sdim          PrivateAutoCompleteMembers(frame, token, remaining_partial_path,
692360784Sdim                                     prefix_path, compiler_type, request);
693314564Sdim        } else if (frame) {
694314564Sdim          // We haven't found our variable yet
695314564Sdim          const bool get_file_globals = true;
696254721Semaste
697314564Sdim          VariableList *variable_list =
698314564Sdim              frame->GetVariableList(get_file_globals);
699314564Sdim
700314564Sdim          if (!variable_list)
701254721Semaste            break;
702254721Semaste
703360784Sdim          for (VariableSP var_sp : *variable_list) {
704314564Sdim
705360784Sdim            if (!var_sp)
706314564Sdim              continue;
707314564Sdim
708360784Sdim            llvm::StringRef variable_name = var_sp->GetName().GetStringRef();
709360784Sdim            if (variable_name.startswith(token)) {
710360784Sdim              if (variable_name == token) {
711360784Sdim                Type *variable_type = var_sp->GetType();
712314564Sdim                if (variable_type) {
713314564Sdim                  CompilerType variable_compiler_type(
714314564Sdim                      variable_type->GetForwardCompilerType());
715314564Sdim                  PrivateAutoComplete(
716314564Sdim                      frame, remaining_partial_path,
717314564Sdim                      prefix_path + token, // Anything that has been resolved
718314564Sdim                                           // already will be in here
719360784Sdim                      variable_compiler_type.GetCanonicalType(), request);
720314564Sdim                } else {
721360784Sdim                  request.AddCompletion((prefix_path + variable_name).str());
722254721Semaste                }
723314564Sdim              } else if (remaining_partial_path.empty()) {
724360784Sdim                request.AddCompletion((prefix_path + variable_name).str());
725314564Sdim              }
726254721Semaste            }
727314564Sdim          }
728254721Semaste        }
729314564Sdim      }
730314564Sdim      break;
731254721Semaste    }
732314564Sdim  }
733254721Semaste}
734254721Semaste
735360784Sdimvoid Variable::AutoComplete(const ExecutionContext &exe_ctx,
736360784Sdim                            CompletionRequest &request) {
737314564Sdim  CompilerType compiler_type;
738254721Semaste
739341825Sdim  PrivateAutoComplete(exe_ctx.GetFramePtr(), request.GetCursorArgumentPrefix(),
740360784Sdim                      "", compiler_type, request);
741254721Semaste}
742