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/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/LLDBLog.h" |
31 | #include "lldb/Utility/Log.h" |
32 | #include "lldb/Utility/RegularExpression.h" |
33 | #include "lldb/Utility/Stream.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(sc: &variable_sc); |
87 | |
88 | return m_mangled.NameMatches(name); |
89 | } |
90 | bool Variable::NameMatches(const RegularExpression ®ex) const { |
91 | if (regex.Execute(string: 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(format: "%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(format: ", type = {{{0:x-16}} {1} (" , args: type->GetID(), args&: type); |
116 | type->DumpTypeName(s); |
117 | s->PutChar(ch: ')'); |
118 | } |
119 | } |
120 | |
121 | if (m_scope != eValueTypeInvalid) { |
122 | s->PutCString(cstr: ", scope = " ); |
123 | switch (m_scope) { |
124 | case eValueTypeVariableGlobal: |
125 | s->PutCString(cstr: m_external ? "global" : "static" ); |
126 | break; |
127 | case eValueTypeVariableArgument: |
128 | s->PutCString(cstr: "parameter" ); |
129 | break; |
130 | case eValueTypeVariableLocal: |
131 | s->PutCString(cstr: "local" ); |
132 | break; |
133 | case eValueTypeVariableThreadLocal: |
134 | s->PutCString(cstr: "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(cstr: ", context = ( " ); |
143 | m_owner_scope->DumpSymbolContext(s); |
144 | s->PutCString(cstr: " )" ); |
145 | } |
146 | |
147 | bool show_fullpaths = false; |
148 | m_declaration.Dump(s, show_fullpaths); |
149 | |
150 | if (m_location_list.IsValid()) { |
151 | s->PutCString(cstr: ", 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(process_sp: ProcessSP(), arch: module_sp->GetArchitecture()); |
157 | } |
158 | m_location_list.GetDescription(s, level: lldb::eDescriptionLevelBrief, abi: abi.get()); |
159 | } |
160 | |
161 | if (m_external) |
162 | s->PutCString(cstr: ", external" ); |
163 | |
164 | if (m_artificial) |
165 | s->PutCString(cstr: ", 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: &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, exe_scope: nullptr, so_addr: Address(), show_fullpaths, show_module, show_inlined_frames, |
184 | show_function_arguments, show_function_name); |
185 | |
186 | if (sc.function) |
187 | s->PutChar(ch: ':'); |
188 | } |
189 | if (m_declaration.DumpStopContext(s, show_fullpaths: 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(uid: GetID()); |
200 | return CompilerDeclContext(); |
201 | } |
202 | |
203 | CompilerDecl Variable::GetDecl() { |
204 | Type *type = GetType(); |
205 | return type ? type->GetSymbolFile()->GetDeclForUID(uid: 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(clear_target: false); |
214 | } |
215 | |
216 | bool Variable::LocationIsValidForFrame(StackFrame *frame) { |
217 | if (frame) { |
218 | Function *function = |
219 | frame->GetSymbolContext(resolve_scope: eSymbolContextFunction).function; |
220 | if (function) { |
221 | TargetSP target_sp(frame->CalculateTarget()); |
222 | |
223 | addr_t loclist_base_load_addr = |
224 | function->GetAddressRange().GetBaseAddress().GetLoadAddress( |
225 | target: target_sp.get()); |
226 | if (loclist_base_load_addr == LLDB_INVALID_ADDRESS) |
227 | return false; |
228 | // It is a location list. We just need to tell if the location list |
229 | // contains the current address when converted to a load address |
230 | return m_location_list.ContainsAddress( |
231 | func_load_addr: loclist_base_load_addr, |
232 | addr: frame->GetFrameCodeAddressForSymbolication().GetLoadAddress( |
233 | target: target_sp.get())); |
234 | } |
235 | } |
236 | return false; |
237 | } |
238 | |
239 | bool Variable::LocationIsValidForAddress(const Address &address) { |
240 | // Be sure to resolve the address to section offset prior to calling this |
241 | // function. |
242 | if (address.IsSectionOffset()) { |
243 | // We need to check if the address is valid for both scope range and value |
244 | // range. |
245 | // Empty scope range means block range. |
246 | bool valid_in_scope_range = |
247 | GetScopeRange().IsEmpty() || GetScopeRange().FindEntryThatContains( |
248 | addr: address.GetFileAddress()) != nullptr; |
249 | if (!valid_in_scope_range) |
250 | return false; |
251 | SymbolContext sc; |
252 | CalculateSymbolContext(sc: &sc); |
253 | if (sc.module_sp == address.GetModule()) { |
254 | // Is the variable is described by a single location? |
255 | if (m_location_list.IsAlwaysValidSingleExpr()) { |
256 | // Yes it is, the location is valid. |
257 | return true; |
258 | } |
259 | |
260 | if (sc.function) { |
261 | addr_t loclist_base_file_addr = |
262 | sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); |
263 | if (loclist_base_file_addr == LLDB_INVALID_ADDRESS) |
264 | return false; |
265 | // It is a location list. We just need to tell if the location list |
266 | // contains the current address when converted to a load address |
267 | return m_location_list.ContainsAddress(func_load_addr: loclist_base_file_addr, |
268 | addr: address.GetFileAddress()); |
269 | } |
270 | } |
271 | } |
272 | return false; |
273 | } |
274 | |
275 | bool Variable::IsInScope(StackFrame *frame) { |
276 | switch (m_scope) { |
277 | case eValueTypeRegister: |
278 | case eValueTypeRegisterSet: |
279 | return frame != nullptr; |
280 | |
281 | case eValueTypeConstResult: |
282 | case eValueTypeVariableGlobal: |
283 | case eValueTypeVariableStatic: |
284 | case eValueTypeVariableThreadLocal: |
285 | return true; |
286 | |
287 | case eValueTypeVariableArgument: |
288 | case eValueTypeVariableLocal: |
289 | if (frame) { |
290 | // We don't have a location list, we just need to see if the block that |
291 | // this variable was defined in is currently |
292 | Block *deepest_frame_block = |
293 | frame->GetSymbolContext(resolve_scope: eSymbolContextBlock).block; |
294 | if (deepest_frame_block) { |
295 | SymbolContext variable_sc; |
296 | CalculateSymbolContext(sc: &variable_sc); |
297 | |
298 | // Check for static or global variable defined at the compile unit |
299 | // level that wasn't defined in a block |
300 | if (variable_sc.block == nullptr) |
301 | return true; |
302 | |
303 | // Check if the variable is valid in the current block |
304 | if (variable_sc.block != deepest_frame_block && |
305 | !variable_sc.block->Contains(block: deepest_frame_block)) |
306 | return false; |
307 | |
308 | // If no scope range is specified then it means that the scope is the |
309 | // same as the scope of the enclosing lexical block. |
310 | if (m_scope_range.IsEmpty()) |
311 | return true; |
312 | |
313 | addr_t file_address = frame->GetFrameCodeAddress().GetFileAddress(); |
314 | return m_scope_range.FindEntryThatContains(addr: file_address) != nullptr; |
315 | } |
316 | } |
317 | break; |
318 | |
319 | default: |
320 | break; |
321 | } |
322 | return false; |
323 | } |
324 | |
325 | Status Variable::GetValuesForVariableExpressionPath( |
326 | llvm::StringRef variable_expr_path, ExecutionContextScope *scope, |
327 | GetVariableCallback callback, void *baton, VariableList &variable_list, |
328 | ValueObjectList &valobj_list) { |
329 | Status error; |
330 | if (!callback || variable_expr_path.empty()) { |
331 | error.SetErrorString("unknown error" ); |
332 | return error; |
333 | } |
334 | |
335 | switch (variable_expr_path.front()) { |
336 | case '*': |
337 | error = Variable::GetValuesForVariableExpressionPath( |
338 | variable_expr_path: variable_expr_path.drop_front(), scope, callback, baton, variable_list, |
339 | valobj_list); |
340 | if (error.Fail()) { |
341 | error.SetErrorString("unknown error" ); |
342 | return error; |
343 | } |
344 | for (uint32_t i = 0; i < valobj_list.GetSize();) { |
345 | Status tmp_error; |
346 | ValueObjectSP valobj_sp( |
347 | valobj_list.GetValueObjectAtIndex(idx: i)->Dereference(error&: tmp_error)); |
348 | if (tmp_error.Fail()) { |
349 | variable_list.RemoveVariableAtIndex(idx: i); |
350 | valobj_list.RemoveValueObjectAtIndex(idx: i); |
351 | } else { |
352 | valobj_list.SetValueObjectAtIndex(idx: i, valobj_sp); |
353 | ++i; |
354 | } |
355 | } |
356 | return error; |
357 | case '&': { |
358 | error = Variable::GetValuesForVariableExpressionPath( |
359 | variable_expr_path: variable_expr_path.drop_front(), scope, callback, baton, variable_list, |
360 | valobj_list); |
361 | if (error.Success()) { |
362 | for (uint32_t i = 0; i < valobj_list.GetSize();) { |
363 | Status tmp_error; |
364 | ValueObjectSP valobj_sp( |
365 | valobj_list.GetValueObjectAtIndex(idx: i)->AddressOf(error&: tmp_error)); |
366 | if (tmp_error.Fail()) { |
367 | variable_list.RemoveVariableAtIndex(idx: i); |
368 | valobj_list.RemoveValueObjectAtIndex(idx: i); |
369 | } else { |
370 | valobj_list.SetValueObjectAtIndex(idx: i, valobj_sp); |
371 | ++i; |
372 | } |
373 | } |
374 | } else { |
375 | error.SetErrorString("unknown error" ); |
376 | } |
377 | return error; |
378 | } break; |
379 | |
380 | default: { |
381 | static RegularExpression g_regex( |
382 | llvm::StringRef("^([A-Za-z_:][A-Za-z_0-9:]*)(.*)" )); |
383 | llvm::SmallVector<llvm::StringRef, 2> matches; |
384 | variable_list.Clear(); |
385 | if (!g_regex.Execute(string: variable_expr_path, matches: &matches)) { |
386 | error.SetErrorStringWithFormatv( |
387 | format: "unable to extract a variable name from '{0}'" , args&: variable_expr_path); |
388 | return error; |
389 | } |
390 | std::string variable_name = matches[1].str(); |
391 | if (!callback(baton, variable_name.c_str(), variable_list)) { |
392 | error.SetErrorString("unknown error" ); |
393 | return error; |
394 | } |
395 | uint32_t i = 0; |
396 | while (i < variable_list.GetSize()) { |
397 | VariableSP var_sp(variable_list.GetVariableAtIndex(idx: i)); |
398 | ValueObjectSP valobj_sp; |
399 | if (!var_sp) { |
400 | variable_list.RemoveVariableAtIndex(idx: i); |
401 | continue; |
402 | } |
403 | ValueObjectSP variable_valobj_sp( |
404 | ValueObjectVariable::Create(exe_scope: scope, var_sp)); |
405 | if (!variable_valobj_sp) { |
406 | variable_list.RemoveVariableAtIndex(idx: i); |
407 | continue; |
408 | } |
409 | |
410 | llvm::StringRef variable_sub_expr_path = |
411 | variable_expr_path.drop_front(N: variable_name.size()); |
412 | if (!variable_sub_expr_path.empty()) { |
413 | valobj_sp = variable_valobj_sp->GetValueForExpressionPath( |
414 | expression: variable_sub_expr_path); |
415 | if (!valobj_sp) { |
416 | error.SetErrorStringWithFormatv( |
417 | format: "invalid expression path '{0}' for variable '{1}'" , |
418 | args&: variable_sub_expr_path, args: var_sp->GetName().GetCString()); |
419 | variable_list.RemoveVariableAtIndex(idx: i); |
420 | continue; |
421 | } |
422 | } else { |
423 | // Just the name of a variable with no extras |
424 | valobj_sp = variable_valobj_sp; |
425 | } |
426 | |
427 | valobj_list.Append(val_obj_sp: valobj_sp); |
428 | ++i; |
429 | } |
430 | |
431 | if (variable_list.GetSize() > 0) { |
432 | error.Clear(); |
433 | return error; |
434 | } |
435 | } break; |
436 | } |
437 | error.SetErrorString("unknown error" ); |
438 | return error; |
439 | } |
440 | |
441 | bool Variable::DumpLocations(Stream *s, const Address &address) { |
442 | SymbolContext sc; |
443 | CalculateSymbolContext(sc: &sc); |
444 | ABISP abi; |
445 | if (m_owner_scope) { |
446 | ModuleSP module_sp(m_owner_scope->CalculateSymbolContextModule()); |
447 | if (module_sp) |
448 | abi = ABI::FindPlugin(process_sp: ProcessSP(), arch: module_sp->GetArchitecture()); |
449 | } |
450 | |
451 | const addr_t file_addr = address.GetFileAddress(); |
452 | if (sc.function) { |
453 | addr_t loclist_base_file_addr = |
454 | sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); |
455 | if (loclist_base_file_addr == LLDB_INVALID_ADDRESS) |
456 | return false; |
457 | return m_location_list.DumpLocations(s, level: eDescriptionLevelBrief, |
458 | func_load_addr: loclist_base_file_addr, file_addr, |
459 | abi: abi.get()); |
460 | } |
461 | return false; |
462 | } |
463 | |
464 | static void PrivateAutoComplete( |
465 | StackFrame *frame, llvm::StringRef partial_path, |
466 | const llvm::Twine |
467 | &prefix_path, // Anything that has been resolved already will be in here |
468 | const CompilerType &compiler_type, CompletionRequest &request); |
469 | |
470 | static void PrivateAutoCompleteMembers( |
471 | StackFrame *frame, const std::string &partial_member_name, |
472 | llvm::StringRef partial_path, |
473 | const llvm::Twine |
474 | &prefix_path, // Anything that has been resolved already will be in here |
475 | const CompilerType &compiler_type, CompletionRequest &request) { |
476 | |
477 | // We are in a type parsing child members |
478 | const uint32_t num_bases = compiler_type.GetNumDirectBaseClasses(); |
479 | |
480 | if (num_bases > 0) { |
481 | for (uint32_t i = 0; i < num_bases; ++i) { |
482 | CompilerType base_class_type = |
483 | compiler_type.GetDirectBaseClassAtIndex(idx: i, bit_offset_ptr: nullptr); |
484 | |
485 | PrivateAutoCompleteMembers(frame, partial_member_name, partial_path, |
486 | prefix_path, |
487 | compiler_type: base_class_type.GetCanonicalType(), request); |
488 | } |
489 | } |
490 | |
491 | const uint32_t num_vbases = compiler_type.GetNumVirtualBaseClasses(); |
492 | |
493 | if (num_vbases > 0) { |
494 | for (uint32_t i = 0; i < num_vbases; ++i) { |
495 | CompilerType vbase_class_type = |
496 | compiler_type.GetVirtualBaseClassAtIndex(idx: i, bit_offset_ptr: nullptr); |
497 | |
498 | PrivateAutoCompleteMembers(frame, partial_member_name, partial_path, |
499 | prefix_path, |
500 | compiler_type: vbase_class_type.GetCanonicalType(), request); |
501 | } |
502 | } |
503 | |
504 | // We are in a type parsing child members |
505 | const uint32_t num_fields = compiler_type.GetNumFields(); |
506 | |
507 | if (num_fields > 0) { |
508 | for (uint32_t i = 0; i < num_fields; ++i) { |
509 | std::string member_name; |
510 | |
511 | CompilerType member_compiler_type = compiler_type.GetFieldAtIndex( |
512 | idx: i, name&: member_name, bit_offset_ptr: nullptr, bitfield_bit_size_ptr: nullptr, is_bitfield_ptr: nullptr); |
513 | |
514 | if (partial_member_name.empty()) { |
515 | request.AddCompletion(completion: (prefix_path + member_name).str()); |
516 | } else if (llvm::StringRef(member_name) |
517 | .starts_with(Prefix: partial_member_name)) { |
518 | if (member_name == partial_member_name) { |
519 | PrivateAutoComplete( |
520 | frame, partial_path, |
521 | prefix_path: prefix_path + member_name, // Anything that has been resolved |
522 | // already will be in here |
523 | compiler_type: member_compiler_type.GetCanonicalType(), request); |
524 | } else if (partial_path.empty()) { |
525 | request.AddCompletion(completion: (prefix_path + member_name).str()); |
526 | } |
527 | } |
528 | } |
529 | } |
530 | } |
531 | |
532 | static void PrivateAutoComplete( |
533 | StackFrame *frame, llvm::StringRef partial_path, |
534 | const llvm::Twine |
535 | &prefix_path, // Anything that has been resolved already will be in here |
536 | const CompilerType &compiler_type, CompletionRequest &request) { |
537 | // printf ("\nPrivateAutoComplete()\n\tprefix_path = '%s'\n\tpartial_path = |
538 | // '%s'\n", prefix_path.c_str(), partial_path.c_str()); |
539 | std::string remaining_partial_path; |
540 | |
541 | const lldb::TypeClass type_class = compiler_type.GetTypeClass(); |
542 | if (partial_path.empty()) { |
543 | if (compiler_type.IsValid()) { |
544 | switch (type_class) { |
545 | default: |
546 | case eTypeClassArray: |
547 | case eTypeClassBlockPointer: |
548 | case eTypeClassBuiltin: |
549 | case eTypeClassComplexFloat: |
550 | case eTypeClassComplexInteger: |
551 | case eTypeClassEnumeration: |
552 | case eTypeClassFunction: |
553 | case eTypeClassMemberPointer: |
554 | case eTypeClassReference: |
555 | case eTypeClassTypedef: |
556 | case eTypeClassVector: { |
557 | request.AddCompletion(completion: prefix_path.str()); |
558 | } break; |
559 | |
560 | case eTypeClassClass: |
561 | case eTypeClassStruct: |
562 | case eTypeClassUnion: |
563 | if (prefix_path.str().back() != '.') |
564 | request.AddCompletion(completion: (prefix_path + "." ).str()); |
565 | break; |
566 | |
567 | case eTypeClassObjCObject: |
568 | case eTypeClassObjCInterface: |
569 | break; |
570 | case eTypeClassObjCObjectPointer: |
571 | case eTypeClassPointer: { |
572 | bool omit_empty_base_classes = true; |
573 | if (llvm::expectedToStdOptional( |
574 | E: compiler_type.GetNumChildren(omit_empty_base_classes, exe_ctx: nullptr)) |
575 | .value_or(u: 0)) |
576 | request.AddCompletion(completion: (prefix_path + "->" ).str()); |
577 | else { |
578 | request.AddCompletion(completion: prefix_path.str()); |
579 | } |
580 | } break; |
581 | } |
582 | } else { |
583 | if (frame) { |
584 | const bool get_file_globals = true; |
585 | |
586 | VariableList *variable_list = frame->GetVariableList(get_file_globals, |
587 | error_ptr: nullptr); |
588 | |
589 | if (variable_list) { |
590 | for (const VariableSP &var_sp : *variable_list) |
591 | request.AddCompletion(completion: var_sp->GetName().AsCString()); |
592 | } |
593 | } |
594 | } |
595 | } else { |
596 | const char ch = partial_path[0]; |
597 | switch (ch) { |
598 | case '*': |
599 | if (prefix_path.str().empty()) { |
600 | PrivateAutoComplete(frame, partial_path: partial_path.substr(Start: 1), prefix_path: "*" , compiler_type, |
601 | request); |
602 | } |
603 | break; |
604 | |
605 | case '&': |
606 | if (prefix_path.isTriviallyEmpty()) { |
607 | PrivateAutoComplete(frame, partial_path: partial_path.substr(Start: 1), prefix_path: std::string("&" ), |
608 | compiler_type, request); |
609 | } |
610 | break; |
611 | |
612 | case '-': |
613 | if (partial_path.size() > 1 && partial_path[1] == '>' && |
614 | !prefix_path.str().empty()) { |
615 | switch (type_class) { |
616 | case lldb::eTypeClassPointer: { |
617 | CompilerType pointee_type(compiler_type.GetPointeeType()); |
618 | if (partial_path.size() > 2 && partial_path[2]) { |
619 | // If there is more after the "->", then search deeper |
620 | PrivateAutoComplete(frame, partial_path: partial_path.substr(Start: 2), |
621 | prefix_path: prefix_path + "->" , |
622 | compiler_type: pointee_type.GetCanonicalType(), request); |
623 | } else { |
624 | // Nothing after the "->", so list all members |
625 | PrivateAutoCompleteMembers( |
626 | frame, partial_member_name: std::string(), partial_path: std::string(), prefix_path: prefix_path + "->" , |
627 | compiler_type: pointee_type.GetCanonicalType(), request); |
628 | } |
629 | } break; |
630 | default: |
631 | break; |
632 | } |
633 | } |
634 | break; |
635 | |
636 | case '.': |
637 | if (compiler_type.IsValid()) { |
638 | switch (type_class) { |
639 | case lldb::eTypeClassUnion: |
640 | case lldb::eTypeClassStruct: |
641 | case lldb::eTypeClassClass: |
642 | if (partial_path.size() > 1 && partial_path[1]) { |
643 | // If there is more after the ".", then search deeper |
644 | PrivateAutoComplete(frame, partial_path: partial_path.substr(Start: 1), |
645 | prefix_path: prefix_path + "." , compiler_type, request); |
646 | |
647 | } else { |
648 | // Nothing after the ".", so list all members |
649 | PrivateAutoCompleteMembers(frame, partial_member_name: std::string(), partial_path, |
650 | prefix_path: prefix_path + "." , compiler_type, |
651 | request); |
652 | } |
653 | break; |
654 | default: |
655 | break; |
656 | } |
657 | } |
658 | break; |
659 | default: |
660 | if (isalpha(ch) || ch == '_' || ch == '$') { |
661 | const size_t partial_path_len = partial_path.size(); |
662 | size_t pos = 1; |
663 | while (pos < partial_path_len) { |
664 | const char curr_ch = partial_path[pos]; |
665 | if (isalnum(curr_ch) || curr_ch == '_' || curr_ch == '$') { |
666 | ++pos; |
667 | continue; |
668 | } |
669 | break; |
670 | } |
671 | |
672 | std::string token(std::string(partial_path), 0, pos); |
673 | remaining_partial_path = std::string(partial_path.substr(Start: pos)); |
674 | |
675 | if (compiler_type.IsValid()) { |
676 | PrivateAutoCompleteMembers(frame, partial_member_name: token, partial_path: remaining_partial_path, |
677 | prefix_path, compiler_type, request); |
678 | } else if (frame) { |
679 | // We haven't found our variable yet |
680 | const bool get_file_globals = true; |
681 | |
682 | VariableList *variable_list = |
683 | frame->GetVariableList(get_file_globals, error_ptr: nullptr); |
684 | |
685 | if (!variable_list) |
686 | break; |
687 | |
688 | for (VariableSP var_sp : *variable_list) { |
689 | |
690 | if (!var_sp) |
691 | continue; |
692 | |
693 | llvm::StringRef variable_name = var_sp->GetName().GetStringRef(); |
694 | if (variable_name.starts_with(Prefix: token)) { |
695 | if (variable_name == token) { |
696 | Type *variable_type = var_sp->GetType(); |
697 | if (variable_type) { |
698 | CompilerType variable_compiler_type( |
699 | variable_type->GetForwardCompilerType()); |
700 | PrivateAutoComplete( |
701 | frame, partial_path: remaining_partial_path, |
702 | prefix_path: prefix_path + token, // Anything that has been resolved |
703 | // already will be in here |
704 | compiler_type: variable_compiler_type.GetCanonicalType(), request); |
705 | } else { |
706 | request.AddCompletion(completion: (prefix_path + variable_name).str()); |
707 | } |
708 | } else if (remaining_partial_path.empty()) { |
709 | request.AddCompletion(completion: (prefix_path + variable_name).str()); |
710 | } |
711 | } |
712 | } |
713 | } |
714 | } |
715 | break; |
716 | } |
717 | } |
718 | } |
719 | |
720 | void Variable::AutoComplete(const ExecutionContext &exe_ctx, |
721 | CompletionRequest &request) { |
722 | CompilerType compiler_type; |
723 | |
724 | PrivateAutoComplete(frame: exe_ctx.GetFramePtr(), partial_path: request.GetCursorArgumentPrefix(), |
725 | prefix_path: "" , compiler_type, request); |
726 | } |
727 | |