]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm-project/lldb/source/Interpreter/OptionValueProperties.cpp
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[FreeBSD/FreeBSD.git] / contrib / llvm-project / lldb / source / Interpreter / OptionValueProperties.cpp
1 //===-- OptionValueProperties.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/Interpreter/OptionValueProperties.h"
10
11 #include "lldb/Utility/Flags.h"
12
13 #include "lldb/Core/UserSettingsController.h"
14 #include "lldb/Interpreter/OptionValues.h"
15 #include "lldb/Interpreter/Property.h"
16 #include "lldb/Utility/Args.h"
17 #include "lldb/Utility/Stream.h"
18 #include "lldb/Utility/StringList.h"
19
20 using namespace lldb;
21 using namespace lldb_private;
22
23 OptionValueProperties::OptionValueProperties(ConstString name)
24     : OptionValue(), m_name(name), m_properties(), m_name_to_index() {}
25
26 OptionValueProperties::OptionValueProperties(
27     const OptionValueProperties &global_properties)
28     : OptionValue(global_properties),
29       std::enable_shared_from_this<OptionValueProperties>(),
30       m_name(global_properties.m_name),
31       m_properties(global_properties.m_properties),
32       m_name_to_index(global_properties.m_name_to_index) {
33   // We now have an exact copy of "global_properties". We need to now find all
34   // non-global settings and copy the property values so that all non-global
35   // settings get new OptionValue instances created for them.
36   const size_t num_properties = m_properties.size();
37   for (size_t i = 0; i < num_properties; ++i) {
38     // Duplicate any values that are not global when constructing properties
39     // from a global copy
40     if (!m_properties[i].IsGlobal()) {
41       lldb::OptionValueSP new_value_sp(m_properties[i].GetValue()->DeepCopy());
42       m_properties[i].SetOptionValue(new_value_sp);
43     }
44   }
45 }
46
47 size_t OptionValueProperties::GetNumProperties() const {
48   return m_properties.size();
49 }
50
51 void OptionValueProperties::Initialize(const PropertyDefinitions &defs) {
52   for (const auto &definition : defs) {
53     Property property(definition);
54     assert(property.IsValid());
55     m_name_to_index.Append(ConstString(property.GetName()), m_properties.size());
56     property.GetValue()->SetParent(shared_from_this());
57     m_properties.push_back(property);
58   }
59   m_name_to_index.Sort();
60 }
61
62 void OptionValueProperties::SetValueChangedCallback(
63     uint32_t property_idx, OptionValueChangedCallback callback, void *baton) {
64   Property *property = ProtectedGetPropertyAtIndex(property_idx);
65   if (property)
66     property->SetValueChangedCallback(callback, baton);
67 }
68
69 void OptionValueProperties::AppendProperty(ConstString name,
70                                            ConstString desc,
71                                            bool is_global,
72                                            const OptionValueSP &value_sp) {
73   Property property(name, desc, is_global, value_sp);
74   m_name_to_index.Append(name, m_properties.size());
75   m_properties.push_back(property);
76   value_sp->SetParent(shared_from_this());
77   m_name_to_index.Sort();
78 }
79
80 // bool
81 // OptionValueProperties::GetQualifiedName (Stream &strm)
82 //{
83 //    bool dumped_something = false;
84 ////    lldb::OptionValuePropertiesSP parent_sp(GetParent ());
85 ////    if (parent_sp)
86 ////    {
87 ////        parent_sp->GetQualifiedName (strm);
88 ////        strm.PutChar('.');
89 ////        dumped_something = true;
90 ////    }
91 //    if (m_name)
92 //    {
93 //        strm << m_name;
94 //        dumped_something = true;
95 //    }
96 //    return dumped_something;
97 //}
98 //
99 lldb::OptionValueSP
100 OptionValueProperties::GetValueForKey(const ExecutionContext *exe_ctx,
101                                       ConstString key,
102                                       bool will_modify) const {
103   lldb::OptionValueSP value_sp;
104   size_t idx = m_name_to_index.Find(key, SIZE_MAX);
105   if (idx < m_properties.size())
106     value_sp = GetPropertyAtIndex(exe_ctx, will_modify, idx)->GetValue();
107   return value_sp;
108 }
109
110 lldb::OptionValueSP
111 OptionValueProperties::GetSubValue(const ExecutionContext *exe_ctx,
112                                    llvm::StringRef name, bool will_modify,
113                                    Status &error) const {
114   lldb::OptionValueSP value_sp;
115   if (name.empty())
116     return OptionValueSP();
117
118   llvm::StringRef sub_name;
119   ConstString key;
120   size_t key_len = name.find_first_of(".[{");
121   if (key_len != llvm::StringRef::npos) {
122     key.SetString(name.take_front(key_len));
123     sub_name = name.drop_front(key_len);
124   } else
125     key.SetString(name);
126
127   value_sp = GetValueForKey(exe_ctx, key, will_modify);
128   if (sub_name.empty() || !value_sp)
129     return value_sp;
130
131   switch (sub_name[0]) {
132   case '.': {
133     lldb::OptionValueSP return_val_sp;
134     return_val_sp =
135         value_sp->GetSubValue(exe_ctx, sub_name.drop_front(), will_modify, error);
136     if (!return_val_sp) {
137       if (Properties::IsSettingExperimental(sub_name.drop_front())) {
138         size_t experimental_len =
139             strlen(Properties::GetExperimentalSettingsName());
140         if (sub_name[experimental_len + 1] == '.')
141           return_val_sp = value_sp->GetSubValue(
142               exe_ctx, sub_name.drop_front(experimental_len + 2), will_modify, error);
143         // It isn't an error if an experimental setting is not present.
144         if (!return_val_sp)
145           error.Clear();
146       }
147     }
148     return return_val_sp;
149   }
150   case '{':
151     // Predicate matching for predicates like
152     // "<setting-name>{<predicate>}"
153     // strings are parsed by the current OptionValueProperties subclass to mean
154     // whatever they want to. For instance a subclass of OptionValueProperties
155     // for a lldb_private::Target might implement: "target.run-
156     // args{arch==i386}"   -- only set run args if the arch is i386 "target
157     // .run-args{path=/tmp/a/b/c/a.out}" -- only set run args if the path
158     // matches "target.run-args{basename==test&&arch==x86_64}" -- only set run
159     // args if executable basename is "test" and arch is "x86_64"
160     if (sub_name[1]) {
161       llvm::StringRef predicate_start = sub_name.drop_front();
162       size_t pos = predicate_start.find('}');
163       if (pos != llvm::StringRef::npos) {
164         auto predicate = predicate_start.take_front(pos);
165         auto rest = predicate_start.drop_front(pos);
166         if (PredicateMatches(exe_ctx, predicate)) {
167           if (!rest.empty()) {
168             // Still more subvalue string to evaluate
169             return value_sp->GetSubValue(exe_ctx, rest,
170                                           will_modify, error);
171           } else {
172             // We have a match!
173             break;
174           }
175         }
176       }
177     }
178     // Predicate didn't match or wasn't correctly formed
179     value_sp.reset();
180     break;
181
182   case '[':
183     // Array or dictionary access for subvalues like: "[12]"       -- access
184     // 12th array element "['hello']"  -- dictionary access of key named hello
185     return value_sp->GetSubValue(exe_ctx, sub_name, will_modify, error);
186
187   default:
188     value_sp.reset();
189     break;
190   }
191   return value_sp;
192 }
193
194 Status OptionValueProperties::SetSubValue(const ExecutionContext *exe_ctx,
195                                           VarSetOperationType op,
196                                           llvm::StringRef name,
197                                           llvm::StringRef value) {
198   Status error;
199   const bool will_modify = true;
200   llvm::SmallVector<llvm::StringRef, 8> components;
201   name.split(components, '.');
202   bool name_contains_experimental = false;
203   for (const auto &part : components)
204     if (Properties::IsSettingExperimental(part))
205       name_contains_experimental = true;
206
207   lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name, will_modify, error));
208   if (value_sp)
209     error = value_sp->SetValueFromString(value, op);
210   else {
211     // Don't set an error if the path contained .experimental. - those are
212     // allowed to be missing and should silently fail.
213     if (!name_contains_experimental && error.AsCString() == nullptr) {
214       error.SetErrorStringWithFormat("invalid value path '%s'", name.str().c_str());
215     }
216   }
217   return error;
218 }
219
220 uint32_t
221 OptionValueProperties::GetPropertyIndex(ConstString name) const {
222   return m_name_to_index.Find(name, SIZE_MAX);
223 }
224
225 const Property *
226 OptionValueProperties::GetProperty(const ExecutionContext *exe_ctx,
227                                    bool will_modify,
228                                    ConstString name) const {
229   return GetPropertyAtIndex(
230       exe_ctx, will_modify,
231       m_name_to_index.Find(name, SIZE_MAX));
232 }
233
234 const Property *OptionValueProperties::GetPropertyAtIndex(
235     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
236   return ProtectedGetPropertyAtIndex(idx);
237 }
238
239 lldb::OptionValueSP OptionValueProperties::GetPropertyValueAtIndex(
240     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
241   const Property *setting = GetPropertyAtIndex(exe_ctx, will_modify, idx);
242   if (setting)
243     return setting->GetValue();
244   return OptionValueSP();
245 }
246
247 OptionValuePathMappings *
248 OptionValueProperties::GetPropertyAtIndexAsOptionValuePathMappings(
249     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
250   OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx));
251   if (value_sp)
252     return value_sp->GetAsPathMappings();
253   return nullptr;
254 }
255
256 OptionValueFileSpecList *
257 OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpecList(
258     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
259   OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx));
260   if (value_sp)
261     return value_sp->GetAsFileSpecList();
262   return nullptr;
263 }
264
265 OptionValueArch *OptionValueProperties::GetPropertyAtIndexAsOptionValueArch(
266     const ExecutionContext *exe_ctx, uint32_t idx) const {
267   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
268   if (property)
269     return property->GetValue()->GetAsArch();
270   return nullptr;
271 }
272
273 OptionValueLanguage *
274 OptionValueProperties::GetPropertyAtIndexAsOptionValueLanguage(
275     const ExecutionContext *exe_ctx, uint32_t idx) const {
276   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
277   if (property)
278     return property->GetValue()->GetAsLanguage();
279   return nullptr;
280 }
281
282 bool OptionValueProperties::GetPropertyAtIndexAsArgs(
283     const ExecutionContext *exe_ctx, uint32_t idx, Args &args) const {
284   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
285   if (property) {
286     OptionValue *value = property->GetValue().get();
287     if (value) {
288       const OptionValueArray *array = value->GetAsArray();
289       if (array)
290         return array->GetArgs(args);
291       else {
292         const OptionValueDictionary *dict = value->GetAsDictionary();
293         if (dict)
294           return dict->GetArgs(args);
295       }
296     }
297   }
298   return false;
299 }
300
301 bool OptionValueProperties::SetPropertyAtIndexFromArgs(
302     const ExecutionContext *exe_ctx, uint32_t idx, const Args &args) {
303   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
304   if (property) {
305     OptionValue *value = property->GetValue().get();
306     if (value) {
307       OptionValueArray *array = value->GetAsArray();
308       if (array)
309         return array->SetArgs(args, eVarSetOperationAssign).Success();
310       else {
311         OptionValueDictionary *dict = value->GetAsDictionary();
312         if (dict)
313           return dict->SetArgs(args, eVarSetOperationAssign).Success();
314       }
315     }
316   }
317   return false;
318 }
319
320 bool OptionValueProperties::GetPropertyAtIndexAsBoolean(
321     const ExecutionContext *exe_ctx, uint32_t idx, bool fail_value) const {
322   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
323   if (property) {
324     OptionValue *value = property->GetValue().get();
325     if (value)
326       return value->GetBooleanValue(fail_value);
327   }
328   return fail_value;
329 }
330
331 bool OptionValueProperties::SetPropertyAtIndexAsBoolean(
332     const ExecutionContext *exe_ctx, uint32_t idx, bool new_value) {
333   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
334   if (property) {
335     OptionValue *value = property->GetValue().get();
336     if (value) {
337       value->SetBooleanValue(new_value);
338       return true;
339     }
340   }
341   return false;
342 }
343
344 OptionValueDictionary *
345 OptionValueProperties::GetPropertyAtIndexAsOptionValueDictionary(
346     const ExecutionContext *exe_ctx, uint32_t idx) const {
347   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
348   if (property)
349     return property->GetValue()->GetAsDictionary();
350   return nullptr;
351 }
352
353 int64_t OptionValueProperties::GetPropertyAtIndexAsEnumeration(
354     const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const {
355   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
356   if (property) {
357     OptionValue *value = property->GetValue().get();
358     if (value)
359       return value->GetEnumerationValue(fail_value);
360   }
361   return fail_value;
362 }
363
364 bool OptionValueProperties::SetPropertyAtIndexAsEnumeration(
365     const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value) {
366   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
367   if (property) {
368     OptionValue *value = property->GetValue().get();
369     if (value)
370       return value->SetEnumerationValue(new_value);
371   }
372   return false;
373 }
374
375 const FormatEntity::Entry *
376 OptionValueProperties::GetPropertyAtIndexAsFormatEntity(
377     const ExecutionContext *exe_ctx, uint32_t idx) {
378   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
379   if (property) {
380     OptionValue *value = property->GetValue().get();
381     if (value)
382       return value->GetFormatEntity();
383   }
384   return nullptr;
385 }
386
387 OptionValueFileSpec *
388 OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpec(
389     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
390   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
391   if (property) {
392     OptionValue *value = property->GetValue().get();
393     if (value)
394       return value->GetAsFileSpec();
395   }
396   return nullptr;
397 }
398
399 FileSpec OptionValueProperties::GetPropertyAtIndexAsFileSpec(
400     const ExecutionContext *exe_ctx, uint32_t idx) const {
401   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
402   if (property) {
403     OptionValue *value = property->GetValue().get();
404     if (value)
405       return value->GetFileSpecValue();
406   }
407   return FileSpec();
408 }
409
410 bool OptionValueProperties::SetPropertyAtIndexAsFileSpec(
411     const ExecutionContext *exe_ctx, uint32_t idx,
412     const FileSpec &new_file_spec) {
413   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
414   if (property) {
415     OptionValue *value = property->GetValue().get();
416     if (value)
417       return value->SetFileSpecValue(new_file_spec);
418   }
419   return false;
420 }
421
422 const RegularExpression *
423 OptionValueProperties::GetPropertyAtIndexAsOptionValueRegex(
424     const ExecutionContext *exe_ctx, uint32_t idx) const {
425   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
426   if (property) {
427     OptionValue *value = property->GetValue().get();
428     if (value)
429       return value->GetRegexValue();
430   }
431   return nullptr;
432 }
433
434 OptionValueSInt64 *OptionValueProperties::GetPropertyAtIndexAsOptionValueSInt64(
435     const ExecutionContext *exe_ctx, uint32_t idx) const {
436   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
437   if (property) {
438     OptionValue *value = property->GetValue().get();
439     if (value)
440       return value->GetAsSInt64();
441   }
442   return nullptr;
443 }
444
445 int64_t OptionValueProperties::GetPropertyAtIndexAsSInt64(
446     const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const {
447   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
448   if (property) {
449     OptionValue *value = property->GetValue().get();
450     if (value)
451       return value->GetSInt64Value(fail_value);
452   }
453   return fail_value;
454 }
455
456 bool OptionValueProperties::SetPropertyAtIndexAsSInt64(
457     const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value) {
458   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
459   if (property) {
460     OptionValue *value = property->GetValue().get();
461     if (value)
462       return value->SetSInt64Value(new_value);
463   }
464   return false;
465 }
466
467 llvm::StringRef OptionValueProperties::GetPropertyAtIndexAsString(
468     const ExecutionContext *exe_ctx, uint32_t idx,
469     llvm::StringRef fail_value) const {
470   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
471   if (property) {
472     OptionValue *value = property->GetValue().get();
473     if (value)
474       return value->GetStringValue(fail_value);
475   }
476   return fail_value;
477 }
478
479 bool OptionValueProperties::SetPropertyAtIndexAsString(
480     const ExecutionContext *exe_ctx, uint32_t idx, llvm::StringRef new_value) {
481   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
482   if (property) {
483     OptionValue *value = property->GetValue().get();
484     if (value)
485       return value->SetStringValue(new_value);
486   }
487   return false;
488 }
489
490 OptionValueString *OptionValueProperties::GetPropertyAtIndexAsOptionValueString(
491     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
492   OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx));
493   if (value_sp)
494     return value_sp->GetAsString();
495   return nullptr;
496 }
497
498 uint64_t OptionValueProperties::GetPropertyAtIndexAsUInt64(
499     const ExecutionContext *exe_ctx, uint32_t idx, uint64_t fail_value) const {
500   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
501   if (property) {
502     OptionValue *value = property->GetValue().get();
503     if (value)
504       return value->GetUInt64Value(fail_value);
505   }
506   return fail_value;
507 }
508
509 bool OptionValueProperties::SetPropertyAtIndexAsUInt64(
510     const ExecutionContext *exe_ctx, uint32_t idx, uint64_t new_value) {
511   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
512   if (property) {
513     OptionValue *value = property->GetValue().get();
514     if (value)
515       return value->SetUInt64Value(new_value);
516   }
517   return false;
518 }
519
520 bool OptionValueProperties::Clear() {
521   const size_t num_properties = m_properties.size();
522   for (size_t i = 0; i < num_properties; ++i)
523     m_properties[i].GetValue()->Clear();
524   return true;
525 }
526
527 Status OptionValueProperties::SetValueFromString(llvm::StringRef value,
528                                                  VarSetOperationType op) {
529   Status error;
530
531   //    Args args(value_cstr);
532   //    const size_t argc = args.GetArgumentCount();
533   switch (op) {
534   case eVarSetOperationClear:
535     Clear();
536     break;
537
538   case eVarSetOperationReplace:
539   case eVarSetOperationAssign:
540   case eVarSetOperationRemove:
541   case eVarSetOperationInsertBefore:
542   case eVarSetOperationInsertAfter:
543   case eVarSetOperationAppend:
544   case eVarSetOperationInvalid:
545     error = OptionValue::SetValueFromString(value, op);
546     break;
547   }
548
549   return error;
550 }
551
552 void OptionValueProperties::DumpValue(const ExecutionContext *exe_ctx,
553                                       Stream &strm, uint32_t dump_mask) {
554   const size_t num_properties = m_properties.size();
555   for (size_t i = 0; i < num_properties; ++i) {
556     const Property *property = GetPropertyAtIndex(exe_ctx, false, i);
557     if (property) {
558       OptionValue *option_value = property->GetValue().get();
559       assert(option_value);
560       const bool transparent_value = option_value->ValueIsTransparent();
561       property->Dump(exe_ctx, strm, dump_mask);
562       if (!transparent_value)
563         strm.EOL();
564     }
565   }
566 }
567
568 Status OptionValueProperties::DumpPropertyValue(const ExecutionContext *exe_ctx,
569                                                 Stream &strm,
570                                                 llvm::StringRef property_path,
571                                                 uint32_t dump_mask) {
572   Status error;
573   const bool will_modify = false;
574   lldb::OptionValueSP value_sp(
575       GetSubValue(exe_ctx, property_path, will_modify, error));
576   if (value_sp) {
577     if (!value_sp->ValueIsTransparent()) {
578       if (dump_mask & eDumpOptionName)
579         strm.PutCString(property_path);
580       if (dump_mask & ~eDumpOptionName)
581         strm.PutChar(' ');
582     }
583     value_sp->DumpValue(exe_ctx, strm, dump_mask);
584   }
585   return error;
586 }
587
588 lldb::OptionValueSP OptionValueProperties::DeepCopy() const {
589   llvm_unreachable("this shouldn't happen");
590 }
591
592 const Property *OptionValueProperties::GetPropertyAtPath(
593     const ExecutionContext *exe_ctx, bool will_modify, llvm::StringRef name) const {
594   const Property *property = nullptr;
595   if (name.empty())
596     return nullptr;
597   llvm::StringRef sub_name;
598   ConstString key;
599   size_t key_len = name.find_first_of(".[{");
600
601   if (key_len != llvm::StringRef::npos) {
602     key.SetString(name.take_front(key_len));
603     sub_name = name.drop_front(key_len);
604   } else
605     key.SetString(name);
606
607   property = GetProperty(exe_ctx, will_modify, key);
608   if (sub_name.empty() || !property)
609     return property;
610
611   if (sub_name[0] == '.') {
612     OptionValueProperties *sub_properties =
613         property->GetValue()->GetAsProperties();
614     if (sub_properties)
615       return sub_properties->GetPropertyAtPath(exe_ctx, will_modify,
616                                                 sub_name.drop_front());
617   }
618   return nullptr;
619 }
620
621 void OptionValueProperties::DumpAllDescriptions(CommandInterpreter &interpreter,
622                                                 Stream &strm) const {
623   size_t max_name_len = 0;
624   const size_t num_properties = m_properties.size();
625   for (size_t i = 0; i < num_properties; ++i) {
626     const Property *property = ProtectedGetPropertyAtIndex(i);
627     if (property)
628       max_name_len = std::max<size_t>(property->GetName().size(), max_name_len);
629   }
630   for (size_t i = 0; i < num_properties; ++i) {
631     const Property *property = ProtectedGetPropertyAtIndex(i);
632     if (property)
633       property->DumpDescription(interpreter, strm, max_name_len, false);
634   }
635 }
636
637 void OptionValueProperties::Apropos(
638     llvm::StringRef keyword,
639     std::vector<const Property *> &matching_properties) const {
640   const size_t num_properties = m_properties.size();
641   StreamString strm;
642   for (size_t i = 0; i < num_properties; ++i) {
643     const Property *property = ProtectedGetPropertyAtIndex(i);
644     if (property) {
645       const OptionValueProperties *properties =
646           property->GetValue()->GetAsProperties();
647       if (properties) {
648         properties->Apropos(keyword, matching_properties);
649       } else {
650         bool match = false;
651         llvm::StringRef name = property->GetName();
652         if (name.contains_lower(keyword))
653           match = true;
654         else {
655           llvm::StringRef desc = property->GetDescription();
656           if (desc.contains_lower(keyword))
657             match = true;
658         }
659         if (match) {
660           matching_properties.push_back(property);
661         }
662       }
663     }
664   }
665 }
666
667 lldb::OptionValuePropertiesSP
668 OptionValueProperties::GetSubProperty(const ExecutionContext *exe_ctx,
669                                       ConstString name) {
670   lldb::OptionValueSP option_value_sp(GetValueForKey(exe_ctx, name, false));
671   if (option_value_sp) {
672     OptionValueProperties *ov_properties = option_value_sp->GetAsProperties();
673     if (ov_properties)
674       return ov_properties->shared_from_this();
675   }
676   return lldb::OptionValuePropertiesSP();
677 }