xref: /llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp (revision c046497bf087f00e520b112c07cbaa6a44d7ea31)
1 //===-- CPlusPlusLanguage.cpp -----------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "CPlusPlusLanguage.h"
11 
12 // C Includes
13 // C++ Includes
14 #include <cctype>
15 #include <cstring>
16 #include <functional>
17 #include <mutex>
18 
19 // Other libraries and framework includes
20 #include "llvm/ADT/StringRef.h"
21 
22 // Project includes
23 #include "lldb/Core/ConstString.h"
24 #include "lldb/Core/PluginManager.h"
25 #include "lldb/Core/RegularExpression.h"
26 #include "lldb/Core/UniqueCStringMap.h"
27 #include "lldb/DataFormatters/CXXFunctionPointer.h"
28 #include "lldb/DataFormatters/DataVisualization.h"
29 #include "lldb/DataFormatters/FormattersHelpers.h"
30 #include "lldb/DataFormatters/VectorType.h"
31 #include "lldb/Symbol/SymbolFile.h"
32 #include "lldb/Symbol/TypeList.h"
33 #include "lldb/Target/Target.h"
34 
35 #include "BlockPointer.h"
36 #include "CxxStringTypes.h"
37 #include "LibCxx.h"
38 #include "LibCxxAtomic.h"
39 #include "LibStdcpp.h"
40 
41 using namespace lldb;
42 using namespace lldb_private;
43 using namespace lldb_private::formatters;
44 
45 void CPlusPlusLanguage::Initialize() {
46   PluginManager::RegisterPlugin(GetPluginNameStatic(), "C++ Language",
47                                 CreateInstance);
48 }
49 
50 void CPlusPlusLanguage::Terminate() {
51   PluginManager::UnregisterPlugin(CreateInstance);
52 }
53 
54 lldb_private::ConstString CPlusPlusLanguage::GetPluginNameStatic() {
55   static ConstString g_name("cplusplus");
56   return g_name;
57 }
58 
59 //------------------------------------------------------------------
60 // PluginInterface protocol
61 //------------------------------------------------------------------
62 
63 lldb_private::ConstString CPlusPlusLanguage::GetPluginName() {
64   return GetPluginNameStatic();
65 }
66 
67 uint32_t CPlusPlusLanguage::GetPluginVersion() { return 1; }
68 
69 //------------------------------------------------------------------
70 // Static Functions
71 //------------------------------------------------------------------
72 
73 Language *CPlusPlusLanguage::CreateInstance(lldb::LanguageType language) {
74   if (Language::LanguageIsCPlusPlus(language))
75     return new CPlusPlusLanguage();
76   return nullptr;
77 }
78 
79 void CPlusPlusLanguage::MethodName::Clear() {
80   m_full.Clear();
81   m_basename = llvm::StringRef();
82   m_context = llvm::StringRef();
83   m_arguments = llvm::StringRef();
84   m_qualifiers = llvm::StringRef();
85   m_type = eTypeInvalid;
86   m_parsed = false;
87   m_parse_error = false;
88 }
89 
90 bool ReverseFindMatchingChars(const llvm::StringRef &s,
91                               const llvm::StringRef &left_right_chars,
92                               size_t &left_pos, size_t &right_pos,
93                               size_t pos = llvm::StringRef::npos) {
94   assert(left_right_chars.size() == 2);
95   left_pos = llvm::StringRef::npos;
96   const char left_char = left_right_chars[0];
97   const char right_char = left_right_chars[1];
98   pos = s.find_last_of(left_right_chars, pos);
99   if (pos == llvm::StringRef::npos || s[pos] == left_char)
100     return false;
101   right_pos = pos;
102   uint32_t depth = 1;
103   while (pos > 0 && depth > 0) {
104     pos = s.find_last_of(left_right_chars, pos);
105     if (pos == llvm::StringRef::npos)
106       return false;
107     if (s[pos] == left_char) {
108       if (--depth == 0) {
109         left_pos = pos;
110         return left_pos < right_pos;
111       }
112     } else if (s[pos] == right_char) {
113       ++depth;
114     }
115   }
116   return false;
117 }
118 
119 static bool IsValidBasename(const llvm::StringRef &basename) {
120   // Check that the basename matches with the following regular expression or is
121   // an operator name:
122   // "^~?([A-Za-z_][A-Za-z_0-9]*)(<.*>)?$"
123   // We are using a hand written implementation because it is significantly more
124   // efficient then
125   // using the general purpose regular expression library.
126   size_t idx = 0;
127   if (basename.size() > 0 && basename[0] == '~')
128     idx = 1;
129 
130   if (basename.size() <= idx)
131     return false; // Empty string or "~"
132 
133   if (!std::isalpha(basename[idx]) && basename[idx] != '_')
134     return false; // First charater (after removing the possible '~'') isn't in
135                   // [A-Za-z_]
136 
137   // Read all characters matching [A-Za-z_0-9]
138   ++idx;
139   while (idx < basename.size()) {
140     if (!std::isalnum(basename[idx]) && basename[idx] != '_')
141       break;
142     ++idx;
143   }
144 
145   // We processed all characters. It is a vaild basename.
146   if (idx == basename.size())
147     return true;
148 
149   // Check for basename with template arguments
150   // TODO: Improve the quality of the validation with validating the template
151   // arguments
152   if (basename[idx] == '<' && basename.back() == '>')
153     return true;
154 
155   // Check if the basename is a vaild C++ operator name
156   if (!basename.startswith("operator"))
157     return false;
158 
159   static RegularExpression g_operator_regex(
160       llvm::StringRef("^(operator)( "
161                       "?)([A-Za-z_][A-Za-z_0-9]*|\\(\\)|"
162                       "\\[\\]|[\\^<>=!\\/"
163                       "*+-]+)(<.*>)?(\\[\\])?$"));
164   std::string basename_str(basename.str());
165   return g_operator_regex.Execute(basename_str, nullptr);
166 }
167 
168 void CPlusPlusLanguage::MethodName::Parse() {
169   if (!m_parsed && m_full) {
170     //        ConstString mangled;
171     //        m_full.GetMangledCounterpart(mangled);
172     //        printf ("\n   parsing = '%s'\n", m_full.GetCString());
173     //        if (mangled)
174     //            printf ("   mangled = '%s'\n", mangled.GetCString());
175     m_parse_error = false;
176     m_parsed = true;
177     llvm::StringRef full(m_full.GetCString());
178 
179     size_t arg_start, arg_end;
180     llvm::StringRef parens("()", 2);
181     if (ReverseFindMatchingChars(full, parens, arg_start, arg_end)) {
182       m_arguments = full.substr(arg_start, arg_end - arg_start + 1);
183       if (arg_end + 1 < full.size())
184         m_qualifiers = full.substr(arg_end + 1);
185       if (arg_start > 0) {
186         size_t basename_end = arg_start;
187         size_t context_start = 0;
188         size_t context_end = llvm::StringRef::npos;
189         if (basename_end > 0 && full[basename_end - 1] == '>') {
190           // TODO: handle template junk...
191           // Templated function
192           size_t template_start, template_end;
193           llvm::StringRef lt_gt("<>", 2);
194           if (ReverseFindMatchingChars(full, lt_gt, template_start,
195                                        template_end, basename_end)) {
196             // Check for templated functions that include return type like:
197             // 'void foo<Int>()'
198             context_start = full.rfind(' ', template_start);
199             if (context_start == llvm::StringRef::npos)
200               context_start = 0;
201             else
202               ++context_start;
203 
204             context_end = full.rfind(':', template_start);
205             if (context_end == llvm::StringRef::npos ||
206                 context_end < context_start)
207               context_end = context_start;
208           } else {
209             context_end = full.rfind(':', basename_end);
210           }
211         } else if (context_end == llvm::StringRef::npos) {
212           context_end = full.rfind(':', basename_end);
213         }
214 
215         if (context_end == llvm::StringRef::npos)
216           m_basename = full.substr(0, basename_end);
217         else {
218           if (context_start < context_end)
219             m_context =
220                 full.substr(context_start, context_end - 1 - context_start);
221           const size_t basename_begin = context_end + 1;
222           m_basename =
223               full.substr(basename_begin, basename_end - basename_begin);
224         }
225         m_type = eTypeUnknownMethod;
226       } else {
227         m_parse_error = true;
228         return;
229       }
230 
231       if (!IsValidBasename(m_basename)) {
232         // The C++ basename doesn't match our regular expressions so this can't
233         // be a valid C++ method, clear everything out and indicate an error
234         m_context = llvm::StringRef();
235         m_basename = llvm::StringRef();
236         m_arguments = llvm::StringRef();
237         m_qualifiers = llvm::StringRef();
238         m_parse_error = true;
239       }
240     } else {
241       m_parse_error = true;
242     }
243   }
244 }
245 
246 llvm::StringRef CPlusPlusLanguage::MethodName::GetBasename() {
247   if (!m_parsed)
248     Parse();
249   return m_basename;
250 }
251 
252 llvm::StringRef CPlusPlusLanguage::MethodName::GetContext() {
253   if (!m_parsed)
254     Parse();
255   return m_context;
256 }
257 
258 llvm::StringRef CPlusPlusLanguage::MethodName::GetArguments() {
259   if (!m_parsed)
260     Parse();
261   return m_arguments;
262 }
263 
264 llvm::StringRef CPlusPlusLanguage::MethodName::GetQualifiers() {
265   if (!m_parsed)
266     Parse();
267   return m_qualifiers;
268 }
269 
270 std::string CPlusPlusLanguage::MethodName::GetScopeQualifiedName() {
271   if (!m_parsed)
272     Parse();
273   if (m_basename.empty() || m_context.empty())
274     return std::string();
275 
276   std::string res;
277   res += m_context;
278   res += "::";
279   res += m_basename;
280 
281   return res;
282 }
283 
284 bool CPlusPlusLanguage::IsCPPMangledName(const char *name) {
285   // FIXME, we should really run through all the known C++ Language plugins and
286   // ask each one if
287   // this is a C++ mangled name, but we can put that off till there is actually
288   // more than one
289   // we care about.
290 
291   return (name != nullptr && name[0] == '_' && name[1] == 'Z');
292 }
293 
294 bool CPlusPlusLanguage::ExtractContextAndIdentifier(
295     const char *name, llvm::StringRef &context, llvm::StringRef &identifier) {
296   static RegularExpression g_basename_regex(llvm::StringRef(
297       "^(([A-Za-z_][A-Za-z_0-9]*::)*)(~?[A-Za-z_~][A-Za-z_0-9]*)$"));
298   RegularExpression::Match match(4);
299   if (g_basename_regex.Execute(llvm::StringRef::withNullAsEmpty(name),
300                                &match)) {
301     match.GetMatchAtIndex(name, 1, context);
302     match.GetMatchAtIndex(name, 3, identifier);
303     return true;
304   }
305   return false;
306 }
307 
308 class CPPRuntimeEquivalents {
309 public:
310   CPPRuntimeEquivalents() {
311     m_impl.Append(ConstString("std::basic_string<char, std::char_traits<char>, "
312                               "std::allocator<char> >")
313                       .GetStringRef(),
314                   ConstString("basic_string<char>"));
315 
316     // these two (with a prefixed std::) occur when c++stdlib string class
317     // occurs as a template argument in some STL container
318     m_impl.Append(ConstString("std::basic_string<char, std::char_traits<char>, "
319                               "std::allocator<char> >")
320                       .GetStringRef(),
321                   ConstString("std::basic_string<char>"));
322 
323     m_impl.Sort();
324   }
325 
326   void Add(ConstString &type_name, ConstString &type_equivalent) {
327     m_impl.Insert(type_name.GetStringRef(), type_equivalent);
328   }
329 
330   uint32_t FindExactMatches(ConstString &type_name,
331                             std::vector<ConstString> &equivalents) {
332     uint32_t count = 0;
333 
334     for (ImplData match =
335              m_impl.FindFirstValueForName(type_name.GetStringRef());
336          match != nullptr; match = m_impl.FindNextValueForName(match)) {
337       equivalents.push_back(match->value);
338       count++;
339     }
340 
341     return count;
342   }
343 
344   // partial matches can occur when a name with equivalents is a template
345   // argument.
346   // e.g. we may have "class Foo" be a match for "struct Bar". if we have a
347   // typename
348   // such as "class Templatized<class Foo, Anything>" we want this to be
349   // replaced with
350   // "class Templatized<struct Bar, Anything>". Since partial matching is time
351   // consuming
352   // once we get a partial match, we add it to the exact matches list for faster
353   // retrieval
354   uint32_t FindPartialMatches(ConstString &type_name,
355                               std::vector<ConstString> &equivalents) {
356     uint32_t count = 0;
357 
358     llvm::StringRef type_name_cstr = type_name.GetStringRef();
359 
360     size_t items_count = m_impl.GetSize();
361 
362     for (size_t item = 0; item < items_count; item++) {
363       llvm::StringRef key_cstr = m_impl.GetCStringAtIndex(item);
364       if (type_name_cstr.contains(key_cstr)) {
365         count += AppendReplacements(type_name_cstr, key_cstr, equivalents);
366       }
367     }
368 
369     return count;
370   }
371 
372 private:
373   std::string &replace(std::string &target, std::string &pattern,
374                        std::string &with) {
375     size_t pos;
376     size_t pattern_len = pattern.size();
377 
378     while ((pos = target.find(pattern)) != std::string::npos)
379       target.replace(pos, pattern_len, with);
380 
381     return target;
382   }
383 
384   uint32_t AppendReplacements(llvm::StringRef original,
385                               llvm::StringRef matching_key,
386                               std::vector<ConstString> &equivalents) {
387     std::string matching_key_str(matching_key);
388     ConstString original_const(original);
389 
390     uint32_t count = 0;
391 
392     for (ImplData match = m_impl.FindFirstValueForName(matching_key);
393          match != nullptr; match = m_impl.FindNextValueForName(match)) {
394       std::string target(original);
395       std::string equiv_class(match->value.AsCString());
396 
397       replace(target, matching_key_str, equiv_class);
398 
399       ConstString target_const(target.c_str());
400 
401 // you will most probably want to leave this off since it might make this map
402 // grow indefinitely
403 #ifdef ENABLE_CPP_EQUIVALENTS_MAP_TO_GROW
404       Add(original_const, target_const);
405 #endif
406       equivalents.push_back(target_const);
407 
408       count++;
409     }
410 
411     return count;
412   }
413 
414   typedef UniqueCStringMap<ConstString> Impl;
415   typedef const Impl::Entry *ImplData;
416   Impl m_impl;
417 };
418 
419 static CPPRuntimeEquivalents &GetEquivalentsMap() {
420   static CPPRuntimeEquivalents g_equivalents_map;
421   return g_equivalents_map;
422 }
423 
424 uint32_t
425 CPlusPlusLanguage::FindEquivalentNames(ConstString type_name,
426                                        std::vector<ConstString> &equivalents) {
427   uint32_t count = GetEquivalentsMap().FindExactMatches(type_name, equivalents);
428 
429   bool might_have_partials =
430       (count == 0) // if we have a full name match just use it
431       && (strchr(type_name.AsCString(), '<') !=
432               nullptr // we should only have partial matches when templates are
433                       // involved, check that we have
434           && strchr(type_name.AsCString(), '>') != nullptr); // angle brackets
435                                                              // in the type_name
436                                                              // before trying to
437                                                              // scan for partial
438                                                              // matches
439 
440   if (might_have_partials)
441     count = GetEquivalentsMap().FindPartialMatches(type_name, equivalents);
442 
443   return count;
444 }
445 
446 static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
447   if (!cpp_category_sp)
448     return;
449 
450   TypeSummaryImpl::Flags stl_summary_flags;
451   stl_summary_flags.SetCascades(true)
452       .SetSkipPointers(false)
453       .SetSkipReferences(false)
454       .SetDontShowChildren(true)
455       .SetDontShowValue(true)
456       .SetShowMembersOneLiner(false)
457       .SetHideItemNames(false);
458 
459 #ifndef LLDB_DISABLE_PYTHON
460   lldb::TypeSummaryImplSP std_string_summary_sp(new CXXFunctionSummaryFormat(
461       stl_summary_flags, lldb_private::formatters::LibcxxStringSummaryProvider,
462       "std::string summary provider"));
463   lldb::TypeSummaryImplSP std_wstring_summary_sp(new CXXFunctionSummaryFormat(
464       stl_summary_flags, lldb_private::formatters::LibcxxWStringSummaryProvider,
465       "std::wstring summary provider"));
466 
467   cpp_category_sp->GetTypeSummariesContainer()->Add(
468       ConstString("std::__1::string"), std_string_summary_sp);
469   cpp_category_sp->GetTypeSummariesContainer()->Add(
470       ConstString("std::__ndk1::string"), std_string_summary_sp);
471   cpp_category_sp->GetTypeSummariesContainer()->Add(
472       ConstString("std::__1::basic_string<char, std::__1::char_traits<char>, "
473                   "std::__1::allocator<char> >"),
474       std_string_summary_sp);
475   cpp_category_sp->GetTypeSummariesContainer()->Add(
476       ConstString("std::__ndk1::basic_string<char, "
477                   "std::__ndk1::char_traits<char>, "
478                   "std::__ndk1::allocator<char> >"),
479       std_string_summary_sp);
480 
481   cpp_category_sp->GetTypeSummariesContainer()->Add(
482       ConstString("std::__1::wstring"), std_wstring_summary_sp);
483   cpp_category_sp->GetTypeSummariesContainer()->Add(
484       ConstString("std::__ndk1::wstring"), std_wstring_summary_sp);
485   cpp_category_sp->GetTypeSummariesContainer()->Add(
486       ConstString("std::__1::basic_string<wchar_t, "
487                   "std::__1::char_traits<wchar_t>, "
488                   "std::__1::allocator<wchar_t> >"),
489       std_wstring_summary_sp);
490   cpp_category_sp->GetTypeSummariesContainer()->Add(
491       ConstString("std::__ndk1::basic_string<wchar_t, "
492                   "std::__ndk1::char_traits<wchar_t>, "
493                   "std::__ndk1::allocator<wchar_t> >"),
494       std_wstring_summary_sp);
495 
496   SyntheticChildren::Flags stl_synth_flags;
497   stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(
498       false);
499 
500   AddCXXSynthetic(
501       cpp_category_sp,
502       lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEndCreator,
503       "libc++ std::vector<bool> synthetic children",
504       ConstString(
505           "^std::__(ndk)?1::vector<bool, std::__(ndk)?1::allocator<bool> >$"),
506       stl_synth_flags, true);
507   AddCXXSynthetic(
508       cpp_category_sp,
509       lldb_private::formatters::LibcxxStdVectorSyntheticFrontEndCreator,
510       "libc++ std::vector synthetic children",
511       ConstString("^std::__(ndk)?1::vector<.+>(( )?&)?$"), stl_synth_flags,
512       true);
513   AddCXXSynthetic(
514       cpp_category_sp,
515       lldb_private::formatters::LibcxxStdListSyntheticFrontEndCreator,
516       "libc++ std::list synthetic children",
517       ConstString("^std::__(ndk)?1::list<.+>(( )?&)?$"), stl_synth_flags, true);
518   AddCXXSynthetic(
519       cpp_category_sp,
520       lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator,
521       "libc++ std::map synthetic children",
522       ConstString("^std::__(ndk)?1::map<.+> >(( )?&)?$"), stl_synth_flags,
523       true);
524   AddCXXSynthetic(
525       cpp_category_sp,
526       lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEndCreator,
527       "libc++ std::vector<bool> synthetic children",
528       ConstString("std::__(ndk)?1::vector<std::__(ndk)?1::allocator<bool> >"),
529       stl_synth_flags);
530   AddCXXSynthetic(
531       cpp_category_sp,
532       lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEndCreator,
533       "libc++ std::vector<bool> synthetic children",
534       ConstString(
535           "std::__(ndk)?1::vector<bool, std::__(ndk)?1::allocator<bool> >"),
536       stl_synth_flags);
537   AddCXXSynthetic(
538       cpp_category_sp,
539       lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator,
540       "libc++ std::set synthetic children",
541       ConstString("^std::__(ndk)?1::set<.+> >(( )?&)?$"), stl_synth_flags,
542       true);
543   AddCXXSynthetic(
544       cpp_category_sp,
545       lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator,
546       "libc++ std::multiset synthetic children",
547       ConstString("^std::__(ndk)?1::multiset<.+> >(( )?&)?$"), stl_synth_flags,
548       true);
549   AddCXXSynthetic(
550       cpp_category_sp,
551       lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator,
552       "libc++ std::multimap synthetic children",
553       ConstString("^std::__(ndk)?1::multimap<.+> >(( )?&)?$"), stl_synth_flags,
554       true);
555   AddCXXSynthetic(
556       cpp_category_sp,
557       lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator,
558       "libc++ std::unordered containers synthetic children",
559       ConstString("^(std::__(ndk)?1::)unordered_(multi)?(map|set)<.+> >$"),
560       stl_synth_flags, true);
561   AddCXXSynthetic(
562       cpp_category_sp,
563       lldb_private::formatters::LibcxxInitializerListSyntheticFrontEndCreator,
564       "libc++ std::initializer_list synthetic children",
565       ConstString("^std::initializer_list<.+>(( )?&)?$"), stl_synth_flags,
566       true);
567   AddCXXSynthetic(
568       cpp_category_sp,
569       lldb_private::formatters::LibcxxAtomicSyntheticFrontEndCreator,
570       "libc++ std::atomic synthetic children",
571       ConstString("^std::__(ndk)?1::atomic<.+>$"), stl_synth_flags, true);
572 
573   cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(
574       RegularExpressionSP(new RegularExpression(
575           llvm::StringRef("^(std::__(ndk)?1::)deque<.+>(( )?&)?$"))),
576       SyntheticChildrenSP(new ScriptedSyntheticChildren(
577           stl_synth_flags,
578           "lldb.formatters.cpp.libcxx.stddeque_SynthProvider")));
579 
580   AddCXXSynthetic(
581       cpp_category_sp,
582       lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator,
583       "shared_ptr synthetic children",
584       ConstString("^(std::__(ndk)?1::)shared_ptr<.+>(( )?&)?$"),
585       stl_synth_flags, true);
586   AddCXXSynthetic(
587       cpp_category_sp,
588       lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator,
589       "weak_ptr synthetic children",
590       ConstString("^(std::__(ndk)?1::)weak_ptr<.+>(( )?&)?$"), stl_synth_flags,
591       true);
592 
593   stl_summary_flags.SetDontShowChildren(false);
594   stl_summary_flags.SetSkipPointers(false);
595   AddCXXSummary(
596       cpp_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider,
597       "libc++ std::vector<bool> summary provider",
598       ConstString(
599           "std::__(ndk)?1::vector<bool, std::__(ndk)?1::allocator<bool> >"),
600       stl_summary_flags, true);
601   AddCXXSummary(cpp_category_sp,
602                 lldb_private::formatters::LibcxxContainerSummaryProvider,
603                 "libc++ std::vector summary provider",
604                 ConstString("^std::__(ndk)?1::vector<.+>(( )?&)?$"),
605                 stl_summary_flags, true);
606   AddCXXSummary(cpp_category_sp,
607                 lldb_private::formatters::LibcxxContainerSummaryProvider,
608                 "libc++ std::list summary provider",
609                 ConstString("^std::__(ndk)?1::list<.+>(( )?&)?$"),
610                 stl_summary_flags, true);
611   AddCXXSummary(cpp_category_sp,
612                 lldb_private::formatters::LibcxxContainerSummaryProvider,
613                 "libc++ std::map summary provider",
614                 ConstString("^std::__(ndk)?1::map<.+>(( )?&)?$"),
615                 stl_summary_flags, true);
616   AddCXXSummary(cpp_category_sp,
617                 lldb_private::formatters::LibcxxContainerSummaryProvider,
618                 "libc++ std::deque summary provider",
619                 ConstString("^std::__(ndk)?1::deque<.+>(( )?&)?$"),
620                 stl_summary_flags, true);
621   AddCXXSummary(cpp_category_sp,
622                 lldb_private::formatters::LibcxxContainerSummaryProvider,
623                 "libc++ std::set summary provider",
624                 ConstString("^std::__(ndk)?1::set<.+>(( )?&)?$"),
625                 stl_summary_flags, true);
626   AddCXXSummary(cpp_category_sp,
627                 lldb_private::formatters::LibcxxContainerSummaryProvider,
628                 "libc++ std::multiset summary provider",
629                 ConstString("^std::__(ndk)?1::multiset<.+>(( )?&)?$"),
630                 stl_summary_flags, true);
631   AddCXXSummary(cpp_category_sp,
632                 lldb_private::formatters::LibcxxContainerSummaryProvider,
633                 "libc++ std::multimap summary provider",
634                 ConstString("^std::__(ndk)?1::multimap<.+>(( )?&)?$"),
635                 stl_summary_flags, true);
636   AddCXXSummary(
637       cpp_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider,
638       "libc++ std::unordered containers summary provider",
639       ConstString("^(std::__(ndk)?1::)unordered_(multi)?(map|set)<.+> >$"),
640       stl_summary_flags, true);
641   AddCXXSummary(
642       cpp_category_sp, lldb_private::formatters::LibCxxAtomicSummaryProvider,
643       "libc++ std::atomic summary provider",
644       ConstString("^std::__(ndk)?1::atomic<.+>$"), stl_summary_flags, true);
645 
646   stl_summary_flags.SetSkipPointers(true);
647 
648   AddCXXSummary(cpp_category_sp,
649                 lldb_private::formatters::LibcxxSmartPointerSummaryProvider,
650                 "libc++ std::shared_ptr summary provider",
651                 ConstString("^std::__(ndk)?1::shared_ptr<.+>(( )?&)?$"),
652                 stl_summary_flags, true);
653   AddCXXSummary(cpp_category_sp,
654                 lldb_private::formatters::LibcxxSmartPointerSummaryProvider,
655                 "libc++ std::weak_ptr summary provider",
656                 ConstString("^std::__(ndk)?1::weak_ptr<.+>(( )?&)?$"),
657                 stl_summary_flags, true);
658 
659   AddCXXSynthetic(
660       cpp_category_sp,
661       lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator,
662       "std::vector iterator synthetic children",
663       ConstString("^std::__(ndk)?1::__wrap_iter<.+>$"), stl_synth_flags, true);
664 
665   AddCXXSummary(
666       cpp_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider,
667       "libc++ std::vector<bool> summary provider",
668       ConstString(
669           "std::__(ndk)?1::vector<bool, std::__(ndk)?1::allocator<bool> >"),
670       stl_summary_flags);
671   AddCXXSynthetic(
672       cpp_category_sp,
673       lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator,
674       "std::map iterator synthetic children",
675       ConstString("^std::__(ndk)?1::__map_iterator<.+>$"), stl_synth_flags,
676       true);
677 
678   AddCXXSynthetic(
679       cpp_category_sp, lldb_private::formatters::LibcxxFunctionFrontEndCreator,
680       "std::function synthetic value provider",
681       ConstString("^std::__1::function<.+>$"), stl_synth_flags, true);
682 #endif
683 }
684 
685 static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
686   if (!cpp_category_sp)
687     return;
688 
689   TypeSummaryImpl::Flags stl_summary_flags;
690   stl_summary_flags.SetCascades(true)
691       .SetSkipPointers(false)
692       .SetSkipReferences(false)
693       .SetDontShowChildren(true)
694       .SetDontShowValue(true)
695       .SetShowMembersOneLiner(false)
696       .SetHideItemNames(false);
697 
698   lldb::TypeSummaryImplSP std_string_summary_sp(
699       new StringSummaryFormat(stl_summary_flags, "${var._M_dataplus._M_p}"));
700 
701   lldb::TypeSummaryImplSP cxx11_string_summary_sp(new CXXFunctionSummaryFormat(
702       stl_summary_flags, LibStdcppStringSummaryProvider,
703       "libstdc++ c++11 std::string summary provider"));
704   lldb::TypeSummaryImplSP cxx11_wstring_summary_sp(new CXXFunctionSummaryFormat(
705       stl_summary_flags, LibStdcppWStringSummaryProvider,
706       "libstdc++ c++11 std::wstring summary provider"));
707 
708   cpp_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::string"),
709                                                     std_string_summary_sp);
710   cpp_category_sp->GetTypeSummariesContainer()->Add(
711       ConstString("std::basic_string<char>"), std_string_summary_sp);
712   cpp_category_sp->GetTypeSummariesContainer()->Add(
713       ConstString("std::basic_string<char,std::char_traits<char>,std::"
714                   "allocator<char> >"),
715       std_string_summary_sp);
716   cpp_category_sp->GetTypeSummariesContainer()->Add(
717       ConstString("std::basic_string<char, std::char_traits<char>, "
718                   "std::allocator<char> >"),
719       std_string_summary_sp);
720 
721   cpp_category_sp->GetTypeSummariesContainer()->Add(
722       ConstString("std::__cxx11::string"), cxx11_string_summary_sp);
723   cpp_category_sp->GetTypeSummariesContainer()->Add(
724       ConstString("std::__cxx11::basic_string<char, std::char_traits<char>, "
725                   "std::allocator<char> >"),
726       cxx11_string_summary_sp);
727 
728   // making sure we force-pick the summary for printing wstring (_M_p is a
729   // wchar_t*)
730   lldb::TypeSummaryImplSP std_wstring_summary_sp(
731       new StringSummaryFormat(stl_summary_flags, "${var._M_dataplus._M_p%S}"));
732 
733   cpp_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::wstring"),
734                                                     std_wstring_summary_sp);
735   cpp_category_sp->GetTypeSummariesContainer()->Add(
736       ConstString("std::basic_string<wchar_t>"), std_wstring_summary_sp);
737   cpp_category_sp->GetTypeSummariesContainer()->Add(
738       ConstString("std::basic_string<wchar_t,std::char_traits<wchar_t>,std::"
739                   "allocator<wchar_t> >"),
740       std_wstring_summary_sp);
741   cpp_category_sp->GetTypeSummariesContainer()->Add(
742       ConstString("std::basic_string<wchar_t, std::char_traits<wchar_t>, "
743                   "std::allocator<wchar_t> >"),
744       std_wstring_summary_sp);
745 
746   cpp_category_sp->GetTypeSummariesContainer()->Add(
747       ConstString("std::__cxx11::wstring"), cxx11_wstring_summary_sp);
748   cpp_category_sp->GetTypeSummariesContainer()->Add(
749       ConstString("std::__cxx11::basic_string<wchar_t, "
750                   "std::char_traits<wchar_t>, std::allocator<wchar_t> >"),
751       cxx11_wstring_summary_sp);
752 
753 #ifndef LLDB_DISABLE_PYTHON
754 
755   SyntheticChildren::Flags stl_synth_flags;
756   stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(
757       false);
758 
759   cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(
760       RegularExpressionSP(
761           new RegularExpression(llvm::StringRef("^std::vector<.+>(( )?&)?$"))),
762       SyntheticChildrenSP(new ScriptedSyntheticChildren(
763           stl_synth_flags,
764           "lldb.formatters.cpp.gnu_libstdcpp.StdVectorSynthProvider")));
765   cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(
766       RegularExpressionSP(
767           new RegularExpression(llvm::StringRef("^std::map<.+> >(( )?&)?$"))),
768       SyntheticChildrenSP(new ScriptedSyntheticChildren(
769           stl_synth_flags,
770           "lldb.formatters.cpp.gnu_libstdcpp.StdMapSynthProvider")));
771   cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(
772       RegularExpressionSP(new RegularExpression(
773           llvm::StringRef("^std::(__cxx11::)?list<.+>(( )?&)?$"))),
774       SyntheticChildrenSP(new ScriptedSyntheticChildren(
775           stl_synth_flags,
776           "lldb.formatters.cpp.gnu_libstdcpp.StdListSynthProvider")));
777   stl_summary_flags.SetDontShowChildren(false);
778   stl_summary_flags.SetSkipPointers(true);
779   cpp_category_sp->GetRegexTypeSummariesContainer()->Add(
780       RegularExpressionSP(
781           new RegularExpression(llvm::StringRef("^std::vector<.+>(( )?&)?$"))),
782       TypeSummaryImplSP(
783           new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
784   cpp_category_sp->GetRegexTypeSummariesContainer()->Add(
785       RegularExpressionSP(
786           new RegularExpression(llvm::StringRef("^std::map<.+> >(( )?&)?$"))),
787       TypeSummaryImplSP(
788           new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
789   cpp_category_sp->GetRegexTypeSummariesContainer()->Add(
790       RegularExpressionSP(new RegularExpression(
791           llvm::StringRef("^std::(__cxx11::)?list<.+>(( )?&)?$"))),
792       TypeSummaryImplSP(
793           new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
794 
795   AddCXXSynthetic(
796       cpp_category_sp,
797       lldb_private::formatters::LibStdcppVectorIteratorSyntheticFrontEndCreator,
798       "std::vector iterator synthetic children",
799       ConstString("^__gnu_cxx::__normal_iterator<.+>$"), stl_synth_flags, true);
800 
801   AddCXXSynthetic(
802       cpp_category_sp,
803       lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEndCreator,
804       "std::map iterator synthetic children",
805       ConstString("^std::_Rb_tree_iterator<.+>$"), stl_synth_flags, true);
806 
807   AddCXXSynthetic(
808       cpp_category_sp,
809       lldb_private::formatters::LibStdcppUniquePtrSyntheticFrontEndCreator,
810       "std::unique_ptr synthetic children",
811       ConstString("^std::unique_ptr<.+>(( )?&)?$"), stl_synth_flags, true);
812   AddCXXSynthetic(
813       cpp_category_sp,
814       lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator,
815       "std::shared_ptr synthetic children",
816       ConstString("^std::shared_ptr<.+>(( )?&)?$"), stl_synth_flags, true);
817   AddCXXSynthetic(
818       cpp_category_sp,
819       lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator,
820       "std::weak_ptr synthetic children",
821       ConstString("^std::weak_ptr<.+>(( )?&)?$"), stl_synth_flags, true);
822   AddCXXSynthetic(
823       cpp_category_sp,
824       lldb_private::formatters::LibStdcppTupleSyntheticFrontEndCreator,
825       "std::tuple synthetic children", ConstString("^std::tuple<.+>(( )?&)?$"),
826       stl_synth_flags, true);
827 
828   AddCXXSummary(cpp_category_sp,
829                 lldb_private::formatters::LibStdcppUniquePointerSummaryProvider,
830                 "libstdc++ std::unique_ptr summary provider",
831                 ConstString("^std::unique_ptr<.+>(( )?&)?$"), stl_summary_flags,
832                 true);
833   AddCXXSummary(cpp_category_sp,
834                 lldb_private::formatters::LibStdcppSmartPointerSummaryProvider,
835                 "libstdc++ std::shared_ptr summary provider",
836                 ConstString("^std::shared_ptr<.+>(( )?&)?$"), stl_summary_flags,
837                 true);
838   AddCXXSummary(cpp_category_sp,
839                 lldb_private::formatters::LibStdcppSmartPointerSummaryProvider,
840                 "libstdc++ std::weak_ptr summary provider",
841                 ConstString("^std::weak_ptr<.+>(( )?&)?$"), stl_summary_flags,
842                 true);
843 #endif
844 }
845 
846 static void LoadSystemFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
847   if (!cpp_category_sp)
848     return;
849 
850   TypeSummaryImpl::Flags string_flags;
851   string_flags.SetCascades(true)
852       .SetSkipPointers(true)
853       .SetSkipReferences(false)
854       .SetDontShowChildren(true)
855       .SetDontShowValue(false)
856       .SetShowMembersOneLiner(false)
857       .SetHideItemNames(false);
858 
859   TypeSummaryImpl::Flags string_array_flags;
860   string_array_flags.SetCascades(true)
861       .SetSkipPointers(true)
862       .SetSkipReferences(false)
863       .SetDontShowChildren(true)
864       .SetDontShowValue(true)
865       .SetShowMembersOneLiner(false)
866       .SetHideItemNames(false);
867 
868 #ifndef LLDB_DISABLE_PYTHON
869   // FIXME because of a bug in the FormattersContainer we need to add a summary
870   // for both X* and const X* (<rdar://problem/12717717>)
871   AddCXXSummary(
872       cpp_category_sp, lldb_private::formatters::Char16StringSummaryProvider,
873       "char16_t * summary provider", ConstString("char16_t *"), string_flags);
874   AddCXXSummary(cpp_category_sp,
875                 lldb_private::formatters::Char16StringSummaryProvider,
876                 "char16_t [] summary provider",
877                 ConstString("char16_t \\[[0-9]+\\]"), string_array_flags, true);
878 
879   AddCXXSummary(
880       cpp_category_sp, lldb_private::formatters::Char32StringSummaryProvider,
881       "char32_t * summary provider", ConstString("char32_t *"), string_flags);
882   AddCXXSummary(cpp_category_sp,
883                 lldb_private::formatters::Char32StringSummaryProvider,
884                 "char32_t [] summary provider",
885                 ConstString("char32_t \\[[0-9]+\\]"), string_array_flags, true);
886 
887   AddCXXSummary(
888       cpp_category_sp, lldb_private::formatters::WCharStringSummaryProvider,
889       "wchar_t * summary provider", ConstString("wchar_t *"), string_flags);
890   AddCXXSummary(cpp_category_sp,
891                 lldb_private::formatters::WCharStringSummaryProvider,
892                 "wchar_t * summary provider",
893                 ConstString("wchar_t \\[[0-9]+\\]"), string_array_flags, true);
894 
895   AddCXXSummary(
896       cpp_category_sp, lldb_private::formatters::Char16StringSummaryProvider,
897       "unichar * summary provider", ConstString("unichar *"), string_flags);
898 
899   TypeSummaryImpl::Flags widechar_flags;
900   widechar_flags.SetDontShowValue(true)
901       .SetSkipPointers(true)
902       .SetSkipReferences(false)
903       .SetCascades(true)
904       .SetDontShowChildren(true)
905       .SetHideItemNames(true)
906       .SetShowMembersOneLiner(false);
907 
908   AddCXXSummary(
909       cpp_category_sp, lldb_private::formatters::Char16SummaryProvider,
910       "char16_t summary provider", ConstString("char16_t"), widechar_flags);
911   AddCXXSummary(
912       cpp_category_sp, lldb_private::formatters::Char32SummaryProvider,
913       "char32_t summary provider", ConstString("char32_t"), widechar_flags);
914   AddCXXSummary(cpp_category_sp, lldb_private::formatters::WCharSummaryProvider,
915                 "wchar_t summary provider", ConstString("wchar_t"),
916                 widechar_flags);
917 
918   AddCXXSummary(
919       cpp_category_sp, lldb_private::formatters::Char16SummaryProvider,
920       "unichar summary provider", ConstString("unichar"), widechar_flags);
921 #endif
922 }
923 
924 std::unique_ptr<Language::TypeScavenger> CPlusPlusLanguage::GetTypeScavenger() {
925   class CPlusPlusTypeScavenger : public Language::TypeScavenger {
926   private:
927     class CPlusPlusTypeScavengerResult : public Language::TypeScavenger::Result {
928     public:
929       CPlusPlusTypeScavengerResult(CompilerType type)
930       : Language::TypeScavenger::Result(), m_compiler_type(type) {}
931 
932       bool IsValid() override { return m_compiler_type.IsValid(); }
933 
934       bool DumpToStream(Stream &stream, bool print_help_if_available) override {
935         if (IsValid()) {
936           m_compiler_type.DumpTypeDescription(&stream);
937           stream.EOL();
938           return true;
939         }
940         return false;
941       }
942 
943       ~CPlusPlusTypeScavengerResult() override = default;
944 
945     private:
946       CompilerType m_compiler_type;
947     };
948 
949   protected:
950     CPlusPlusTypeScavenger() = default;
951 
952     ~CPlusPlusTypeScavenger() override = default;
953 
954     bool Find_Impl(ExecutionContextScope *exe_scope, const char *key,
955                    ResultSet &results) override {
956       bool result = false;
957 
958       Target *target = exe_scope->CalculateTarget().get();
959       if (target) {
960         const auto &images(target->GetImages());
961         SymbolContext null_sc;
962         ConstString cs_key(key);
963         llvm::DenseSet<SymbolFile*> searched_sym_files;
964         TypeList matches;
965         images.FindTypes(null_sc,
966                          cs_key,
967                          false,
968                          UINT32_MAX,
969                          searched_sym_files,
970                          matches);
971         for (const auto& match : matches.Types()) {
972           if (match.get()) {
973             CompilerType compiler_type(match->GetFullCompilerType());
974             LanguageType lang_type(compiler_type.GetMinimumLanguage());
975             // other plugins will find types for other languages - here we only do C and C++
976             if (!Language::LanguageIsC(lang_type) && !Language::LanguageIsCPlusPlus(lang_type))
977               continue;
978             if (compiler_type.IsTypedefType())
979               compiler_type = compiler_type.GetTypedefedType();
980             std::unique_ptr<Language::TypeScavenger::Result> scavengeresult(
981                                                                     new CPlusPlusTypeScavengerResult(compiler_type));
982             results.insert(std::move(scavengeresult));
983             result = true;
984           }
985         }
986       }
987 
988       return result;
989     }
990 
991     friend class lldb_private::CPlusPlusLanguage;
992   };
993 
994   return std::unique_ptr<TypeScavenger>(new CPlusPlusTypeScavenger());
995 }
996 
997 lldb::TypeCategoryImplSP CPlusPlusLanguage::GetFormatters() {
998   static std::once_flag g_initialize;
999   static TypeCategoryImplSP g_category;
1000 
1001   std::call_once(g_initialize, [this]() -> void {
1002     DataVisualization::Categories::GetCategory(GetPluginName(), g_category);
1003     if (g_category) {
1004       LoadLibCxxFormatters(g_category);
1005       LoadLibStdcppFormatters(g_category);
1006       LoadSystemFormatters(g_category);
1007     }
1008   });
1009   return g_category;
1010 }
1011 
1012 HardcodedFormatters::HardcodedSummaryFinder
1013 CPlusPlusLanguage::GetHardcodedSummaries() {
1014   static std::once_flag g_initialize;
1015   static ConstString g_vectortypes("VectorTypes");
1016   static HardcodedFormatters::HardcodedSummaryFinder g_formatters;
1017 
1018   std::call_once(g_initialize, []() -> void {
1019     g_formatters.push_back(
1020         [](lldb_private::ValueObject &valobj, lldb::DynamicValueType,
1021            FormatManager &) -> TypeSummaryImpl::SharedPointer {
1022           static CXXFunctionSummaryFormat::SharedPointer formatter_sp(
1023               new CXXFunctionSummaryFormat(
1024                   TypeSummaryImpl::Flags(),
1025                   lldb_private::formatters::CXXFunctionPointerSummaryProvider,
1026                   "Function pointer summary provider"));
1027           if (valobj.GetCompilerType().IsFunctionPointerType()) {
1028             return formatter_sp;
1029           }
1030           return nullptr;
1031         });
1032     g_formatters.push_back(
1033         [](lldb_private::ValueObject &valobj, lldb::DynamicValueType,
1034            FormatManager &fmt_mgr) -> TypeSummaryImpl::SharedPointer {
1035           static CXXFunctionSummaryFormat::SharedPointer formatter_sp(
1036               new CXXFunctionSummaryFormat(
1037                   TypeSummaryImpl::Flags()
1038                       .SetCascades(true)
1039                       .SetDontShowChildren(true)
1040                       .SetHideItemNames(true)
1041                       .SetShowMembersOneLiner(true)
1042                       .SetSkipPointers(true)
1043                       .SetSkipReferences(false),
1044                   lldb_private::formatters::VectorTypeSummaryProvider,
1045                   "vector_type pointer summary provider"));
1046           if (valobj.GetCompilerType().IsVectorType(nullptr, nullptr)) {
1047             if (fmt_mgr.GetCategory(g_vectortypes)->IsEnabled())
1048               return formatter_sp;
1049           }
1050           return nullptr;
1051         });
1052     g_formatters.push_back(
1053         [](lldb_private::ValueObject &valobj, lldb::DynamicValueType,
1054            FormatManager &fmt_mgr) -> TypeSummaryImpl::SharedPointer {
1055           static CXXFunctionSummaryFormat::SharedPointer formatter_sp(
1056               new CXXFunctionSummaryFormat(
1057                   TypeSummaryImpl::Flags()
1058                       .SetCascades(true)
1059                       .SetDontShowChildren(true)
1060                       .SetHideItemNames(true)
1061                       .SetShowMembersOneLiner(true)
1062                       .SetSkipPointers(true)
1063                       .SetSkipReferences(false),
1064                   lldb_private::formatters::BlockPointerSummaryProvider,
1065                   "block pointer summary provider"));
1066           if (valobj.GetCompilerType().IsBlockPointerType(nullptr)) {
1067             return formatter_sp;
1068           }
1069           return nullptr;
1070         });
1071   });
1072 
1073   return g_formatters;
1074 }
1075 
1076 HardcodedFormatters::HardcodedSyntheticFinder
1077 CPlusPlusLanguage::GetHardcodedSynthetics() {
1078   static std::once_flag g_initialize;
1079   static ConstString g_vectortypes("VectorTypes");
1080   static HardcodedFormatters::HardcodedSyntheticFinder g_formatters;
1081 
1082   std::call_once(g_initialize, []() -> void {
1083     g_formatters.push_back([](lldb_private::ValueObject &valobj,
1084                               lldb::DynamicValueType,
1085                               FormatManager &
1086                                   fmt_mgr) -> SyntheticChildren::SharedPointer {
1087       static CXXSyntheticChildren::SharedPointer formatter_sp(
1088           new CXXSyntheticChildren(
1089               SyntheticChildren::Flags()
1090                   .SetCascades(true)
1091                   .SetSkipPointers(true)
1092                   .SetSkipReferences(true)
1093                   .SetNonCacheable(true),
1094               "vector_type synthetic children",
1095               lldb_private::formatters::VectorTypeSyntheticFrontEndCreator));
1096       if (valobj.GetCompilerType().IsVectorType(nullptr, nullptr)) {
1097         if (fmt_mgr.GetCategory(g_vectortypes)->IsEnabled())
1098           return formatter_sp;
1099       }
1100       return nullptr;
1101     });
1102     g_formatters.push_back([](lldb_private::ValueObject &valobj,
1103                               lldb::DynamicValueType,
1104                               FormatManager &
1105                                   fmt_mgr) -> SyntheticChildren::SharedPointer {
1106       static CXXSyntheticChildren::SharedPointer formatter_sp(
1107           new CXXSyntheticChildren(
1108               SyntheticChildren::Flags()
1109                   .SetCascades(true)
1110                   .SetSkipPointers(true)
1111                   .SetSkipReferences(true)
1112                   .SetNonCacheable(true),
1113               "block pointer synthetic children",
1114               lldb_private::formatters::BlockPointerSyntheticFrontEndCreator));
1115       if (valobj.GetCompilerType().IsBlockPointerType(nullptr)) {
1116         return formatter_sp;
1117       }
1118       return nullptr;
1119     });
1120 
1121   });
1122 
1123   return g_formatters;
1124 }
1125