xref: /llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp (revision 2fe8327406050d2585d2ced910a678e28caefcf5)
1 //===-- CPlusPlusNameParser.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 "CPlusPlusNameParser.h"
10 
11 #include "clang/Basic/IdentifierTable.h"
12 #include "clang/Basic/TokenKinds.h"
13 #include "llvm/ADT/StringMap.h"
14 #include "llvm/Support/Threading.h"
15 #include <optional>
16 
17 using namespace lldb;
18 using namespace lldb_private;
19 using llvm::Optional;
20 using ParsedFunction = lldb_private::CPlusPlusNameParser::ParsedFunction;
21 using ParsedName = lldb_private::CPlusPlusNameParser::ParsedName;
22 namespace tok = clang::tok;
23 
24 std::optional<ParsedFunction> CPlusPlusNameParser::ParseAsFunctionDefinition() {
25   m_next_token_index = 0;
26   std::optional<ParsedFunction> result(std::nullopt);
27 
28   // Try to parse the name as function without a return type specified e.g.
29   // main(int, char*[])
30   {
31     Bookmark start_position = SetBookmark();
32     result = ParseFunctionImpl(false);
33     if (result && !HasMoreTokens())
34       return result;
35   }
36 
37   // Try to parse the name as function with function pointer return type e.g.
38   // void (*get_func(const char*))()
39   result = ParseFuncPtr(true);
40   if (result)
41     return result;
42 
43   // Finally try to parse the name as a function with non-function return type
44   // e.g. int main(int, char*[])
45   result = ParseFunctionImpl(true);
46   if (HasMoreTokens())
47     return std::nullopt;
48   return result;
49 }
50 
51 std::optional<ParsedName> CPlusPlusNameParser::ParseAsFullName() {
52   m_next_token_index = 0;
53   std::optional<ParsedNameRanges> name_ranges = ParseFullNameImpl();
54   if (!name_ranges)
55     return std::nullopt;
56   if (HasMoreTokens())
57     return std::nullopt;
58   ParsedName result;
59   result.basename = GetTextForRange(name_ranges->basename_range);
60   result.context = GetTextForRange(name_ranges->context_range);
61   return result;
62 }
63 
64 bool CPlusPlusNameParser::HasMoreTokens() {
65   return m_next_token_index < m_tokens.size();
66 }
67 
68 void CPlusPlusNameParser::Advance() { ++m_next_token_index; }
69 
70 void CPlusPlusNameParser::TakeBack() { --m_next_token_index; }
71 
72 bool CPlusPlusNameParser::ConsumeToken(tok::TokenKind kind) {
73   if (!HasMoreTokens())
74     return false;
75 
76   if (!Peek().is(kind))
77     return false;
78 
79   Advance();
80   return true;
81 }
82 
83 template <typename... Ts> bool CPlusPlusNameParser::ConsumeToken(Ts... kinds) {
84   if (!HasMoreTokens())
85     return false;
86 
87   if (!Peek().isOneOf(kinds...))
88     return false;
89 
90   Advance();
91   return true;
92 }
93 
94 CPlusPlusNameParser::Bookmark CPlusPlusNameParser::SetBookmark() {
95   return Bookmark(m_next_token_index);
96 }
97 
98 size_t CPlusPlusNameParser::GetCurrentPosition() { return m_next_token_index; }
99 
100 clang::Token &CPlusPlusNameParser::Peek() {
101   assert(HasMoreTokens());
102   return m_tokens[m_next_token_index];
103 }
104 
105 std::optional<ParsedFunction>
106 CPlusPlusNameParser::ParseFunctionImpl(bool expect_return_type) {
107   Bookmark start_position = SetBookmark();
108 
109   ParsedFunction result;
110   if (expect_return_type) {
111     size_t return_start = GetCurrentPosition();
112     // Consume return type if it's expected.
113     if (!ConsumeToken(tok::kw_auto) && !ConsumeTypename())
114       return std::nullopt;
115 
116     size_t return_end = GetCurrentPosition();
117     result.return_type = GetTextForRange(Range(return_start, return_end));
118   }
119 
120   auto maybe_name = ParseFullNameImpl();
121   if (!maybe_name) {
122     return std::nullopt;
123   }
124 
125   size_t argument_start = GetCurrentPosition();
126   if (!ConsumeArguments()) {
127     return std::nullopt;
128   }
129 
130   size_t qualifiers_start = GetCurrentPosition();
131   SkipFunctionQualifiers();
132   size_t end_position = GetCurrentPosition();
133 
134   result.name.basename = GetTextForRange(maybe_name->basename_range);
135   result.name.context = GetTextForRange(maybe_name->context_range);
136   result.arguments = GetTextForRange(Range(argument_start, qualifiers_start));
137   result.qualifiers = GetTextForRange(Range(qualifiers_start, end_position));
138   start_position.Remove();
139   return result;
140 }
141 
142 std::optional<ParsedFunction>
143 CPlusPlusNameParser::ParseFuncPtr(bool expect_return_type) {
144   // This function parses a function definition
145   // that returns a pointer type.
146   // E.g., double (*(*func(long))(int))(float)
147 
148   // Step 1:
149   // Remove the return type of the innermost
150   // function pointer type.
151   //
152   // Leaves us with:
153   //   (*(*func(long))(int))(float)
154   Bookmark start_position = SetBookmark();
155   if (expect_return_type) {
156     // Consume return type.
157     if (!ConsumeTypename())
158       return std::nullopt;
159   }
160 
161   // Step 2:
162   //
163   // Skip a pointer and parenthesis pair.
164   //
165   // Leaves us with:
166   //   (*func(long))(int))(float)
167   if (!ConsumeToken(tok::l_paren))
168     return std::nullopt;
169   if (!ConsumePtrsAndRefs())
170     return std::nullopt;
171 
172   // Step 3:
173   //
174   // Consume inner function name. This will fail unless
175   // we stripped all the pointers on the left hand side
176   // of the funciton name.
177   {
178     Bookmark before_inner_function_pos = SetBookmark();
179     auto maybe_inner_function_name = ParseFunctionImpl(false);
180     if (maybe_inner_function_name)
181       if (ConsumeToken(tok::r_paren))
182         if (ConsumeArguments()) {
183           SkipFunctionQualifiers();
184           start_position.Remove();
185           before_inner_function_pos.Remove();
186           return maybe_inner_function_name;
187         }
188   }
189 
190   // Step 4:
191   //
192   // Parse the remaining string as a function pointer again.
193   // This time don't consume the inner-most typename since
194   // we're left with pointers only. This will strip another
195   // layer of pointers until we're left with the innermost
196   // function name/argument. I.e., func(long))(int))(float)
197   //
198   // Once we successfully stripped all pointers and gotten
199   // the innermost function name from ParseFunctionImpl above,
200   // we consume a single ')' and the arguments '(...)' that follows.
201   //
202   // Leaves us with:
203   //   )(float)
204   //
205   // This is the remnant of the outer function pointers' arguments.
206   // Unwinding the recursive calls will remove the remaining
207   // arguments.
208   auto maybe_inner_function_ptr_name = ParseFuncPtr(false);
209   if (maybe_inner_function_ptr_name)
210     if (ConsumeToken(tok::r_paren))
211       if (ConsumeArguments()) {
212         SkipFunctionQualifiers();
213         start_position.Remove();
214         return maybe_inner_function_ptr_name;
215       }
216 
217   return std::nullopt;
218 }
219 
220 bool CPlusPlusNameParser::ConsumeArguments() {
221   return ConsumeBrackets(tok::l_paren, tok::r_paren);
222 }
223 
224 bool CPlusPlusNameParser::ConsumeTemplateArgs() {
225   Bookmark start_position = SetBookmark();
226   if (!HasMoreTokens() || Peek().getKind() != tok::less)
227     return false;
228   Advance();
229 
230   // Consuming template arguments is a bit trickier than consuming function
231   // arguments, because '<' '>' brackets are not always trivially balanced. In
232   // some rare cases tokens '<' and '>' can appear inside template arguments as
233   // arithmetic or shift operators not as template brackets. Examples:
234   // std::enable_if<(10u)<(64), bool>
235   //           f<A<operator<(X,Y)::Subclass>>
236   // Good thing that compiler makes sure that really ambiguous cases of '>'
237   // usage should be enclosed within '()' brackets.
238   int template_counter = 1;
239   bool can_open_template = false;
240   while (HasMoreTokens() && template_counter > 0) {
241     tok::TokenKind kind = Peek().getKind();
242     switch (kind) {
243     case tok::greatergreater:
244       template_counter -= 2;
245       can_open_template = false;
246       Advance();
247       break;
248     case tok::greater:
249       --template_counter;
250       can_open_template = false;
251       Advance();
252       break;
253     case tok::less:
254       // '<' is an attempt to open a subteamplte
255       // check if parser is at the point where it's actually possible,
256       // otherwise it's just a part of an expression like 'sizeof(T)<(10)'. No
257       // need to do the same for '>' because compiler actually makes sure that
258       // '>' always surrounded by brackets to avoid ambiguity.
259       if (can_open_template)
260         ++template_counter;
261       can_open_template = false;
262       Advance();
263       break;
264     case tok::kw_operator: // C++ operator overloading.
265       if (!ConsumeOperator())
266         return false;
267       can_open_template = true;
268       break;
269     case tok::raw_identifier:
270       can_open_template = true;
271       Advance();
272       break;
273     case tok::l_square:
274       // Handle templates tagged with an ABI tag.
275       // An example demangled/prettified version is:
276       //   func[abi:tag1][abi:tag2]<type[abi:tag3]>(int)
277       if (ConsumeAbiTag())
278         can_open_template = true;
279       else if (ConsumeBrackets(tok::l_square, tok::r_square))
280         can_open_template = false;
281       else
282         return false;
283       break;
284     case tok::l_paren:
285       if (!ConsumeArguments())
286         return false;
287       can_open_template = false;
288       break;
289     default:
290       can_open_template = false;
291       Advance();
292       break;
293     }
294   }
295 
296   if (template_counter != 0) {
297     return false;
298   }
299   start_position.Remove();
300   return true;
301 }
302 
303 bool CPlusPlusNameParser::ConsumeAbiTag() {
304   Bookmark start_position = SetBookmark();
305   if (!ConsumeToken(tok::l_square))
306     return false;
307 
308   if (HasMoreTokens() && Peek().is(tok::raw_identifier) &&
309       Peek().getRawIdentifier() == "abi")
310     Advance();
311   else
312     return false;
313 
314   if (!ConsumeToken(tok::colon))
315     return false;
316 
317   // Consume the actual tag string (and allow some special characters)
318   while (ConsumeToken(tok::raw_identifier, tok::comma, tok::period,
319                       tok::numeric_constant))
320     ;
321 
322   if (!ConsumeToken(tok::r_square))
323     return false;
324 
325   start_position.Remove();
326   return true;
327 }
328 
329 bool CPlusPlusNameParser::ConsumeAnonymousNamespace() {
330   Bookmark start_position = SetBookmark();
331   if (!ConsumeToken(tok::l_paren)) {
332     return false;
333   }
334   constexpr llvm::StringLiteral g_anonymous("anonymous");
335   if (HasMoreTokens() && Peek().is(tok::raw_identifier) &&
336       Peek().getRawIdentifier() == g_anonymous) {
337     Advance();
338   } else {
339     return false;
340   }
341 
342   if (!ConsumeToken(tok::kw_namespace)) {
343     return false;
344   }
345 
346   if (!ConsumeToken(tok::r_paren)) {
347     return false;
348   }
349   start_position.Remove();
350   return true;
351 }
352 
353 bool CPlusPlusNameParser::ConsumeLambda() {
354   Bookmark start_position = SetBookmark();
355   if (!ConsumeToken(tok::l_brace)) {
356     return false;
357   }
358   constexpr llvm::StringLiteral g_lambda("lambda");
359   if (HasMoreTokens() && Peek().is(tok::raw_identifier) &&
360       Peek().getRawIdentifier() == g_lambda) {
361     // Put the matched brace back so we can use ConsumeBrackets
362     TakeBack();
363   } else {
364     return false;
365   }
366 
367   if (!ConsumeBrackets(tok::l_brace, tok::r_brace)) {
368     return false;
369   }
370 
371   start_position.Remove();
372   return true;
373 }
374 
375 bool CPlusPlusNameParser::ConsumeBrackets(tok::TokenKind left,
376                                           tok::TokenKind right) {
377   Bookmark start_position = SetBookmark();
378   if (!HasMoreTokens() || Peek().getKind() != left)
379     return false;
380   Advance();
381 
382   int counter = 1;
383   while (HasMoreTokens() && counter > 0) {
384     tok::TokenKind kind = Peek().getKind();
385     if (kind == right)
386       --counter;
387     else if (kind == left)
388       ++counter;
389     Advance();
390   }
391 
392   assert(counter >= 0);
393   if (counter > 0) {
394     return false;
395   }
396   start_position.Remove();
397   return true;
398 }
399 
400 bool CPlusPlusNameParser::ConsumeOperator() {
401   Bookmark start_position = SetBookmark();
402   if (!ConsumeToken(tok::kw_operator))
403     return false;
404 
405   if (!HasMoreTokens()) {
406     return false;
407   }
408 
409   const auto &token = Peek();
410 
411   // When clang generates debug info it adds template parameters to names.
412   // Since clang doesn't add a space between the name and the template parameter
413   // in some cases we are not generating valid C++ names e.g.:
414   //
415   //   operator<<A::B>
416   //
417   // In some of these cases we will not parse them correctly. This fixes the
418   // issue by detecting this case and inserting tok::less in place of
419   // tok::lessless and returning successfully that we consumed the operator.
420   if (token.getKind() == tok::lessless) {
421     // Make sure we have more tokens before attempting to look ahead one more.
422     if (m_next_token_index + 1 < m_tokens.size()) {
423       // Look ahead two tokens.
424       clang::Token n_token = m_tokens[m_next_token_index + 1];
425       // If we find ( or < then this is indeed operator<< no need for fix.
426       if (n_token.getKind() != tok::l_paren && n_token.getKind() != tok::less) {
427         clang::Token tmp_tok;
428         tmp_tok.startToken();
429         tmp_tok.setLength(1);
430         tmp_tok.setLocation(token.getLocation().getLocWithOffset(1));
431         tmp_tok.setKind(tok::less);
432 
433         m_tokens[m_next_token_index] = tmp_tok;
434 
435         start_position.Remove();
436         return true;
437       }
438     }
439   }
440 
441   switch (token.getKind()) {
442   case tok::kw_new:
443   case tok::kw_delete:
444     // This is 'new' or 'delete' operators.
445     Advance();
446     // Check for array new/delete.
447     if (HasMoreTokens() && Peek().is(tok::l_square)) {
448       // Consume the '[' and ']'.
449       if (!ConsumeBrackets(tok::l_square, tok::r_square))
450         return false;
451     }
452     break;
453 
454 #define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly)  \
455   case tok::Token:                                                             \
456     Advance();                                                                 \
457     break;
458 #define OVERLOADED_OPERATOR_MULTI(Name, Spelling, Unary, Binary, MemberOnly)
459 #include "clang/Basic/OperatorKinds.def"
460 #undef OVERLOADED_OPERATOR
461 #undef OVERLOADED_OPERATOR_MULTI
462 
463   case tok::l_paren:
464     // Call operator consume '(' ... ')'.
465     if (ConsumeBrackets(tok::l_paren, tok::r_paren))
466       break;
467     return false;
468 
469   case tok::l_square:
470     // This is a [] operator.
471     // Consume the '[' and ']'.
472     if (ConsumeBrackets(tok::l_square, tok::r_square))
473       break;
474     return false;
475 
476   default:
477     // This might be a cast operator.
478     if (ConsumeTypename())
479       break;
480     return false;
481   }
482   start_position.Remove();
483   return true;
484 }
485 
486 void CPlusPlusNameParser::SkipTypeQualifiers() {
487   while (ConsumeToken(tok::kw_const, tok::kw_volatile))
488     ;
489 }
490 
491 void CPlusPlusNameParser::SkipFunctionQualifiers() {
492   while (ConsumeToken(tok::kw_const, tok::kw_volatile, tok::amp, tok::ampamp))
493     ;
494 }
495 
496 bool CPlusPlusNameParser::ConsumeBuiltinType() {
497   bool result = false;
498   bool continue_parsing = true;
499   // Built-in types can be made of a few keywords like 'unsigned long long
500   // int'. This function consumes all built-in type keywords without checking
501   // if they make sense like 'unsigned char void'.
502   while (continue_parsing && HasMoreTokens()) {
503     switch (Peek().getKind()) {
504     case tok::kw_short:
505     case tok::kw_long:
506     case tok::kw___int64:
507     case tok::kw___int128:
508     case tok::kw_signed:
509     case tok::kw_unsigned:
510     case tok::kw_void:
511     case tok::kw_char:
512     case tok::kw_int:
513     case tok::kw_half:
514     case tok::kw_float:
515     case tok::kw_double:
516     case tok::kw___float128:
517     case tok::kw_wchar_t:
518     case tok::kw_bool:
519     case tok::kw_char16_t:
520     case tok::kw_char32_t:
521       result = true;
522       Advance();
523       break;
524     default:
525       continue_parsing = false;
526       break;
527     }
528   }
529   return result;
530 }
531 
532 void CPlusPlusNameParser::SkipPtrsAndRefs() {
533   // Ignoring result.
534   ConsumePtrsAndRefs();
535 }
536 
537 bool CPlusPlusNameParser::ConsumePtrsAndRefs() {
538   bool found = false;
539   SkipTypeQualifiers();
540   while (ConsumeToken(tok::star, tok::amp, tok::ampamp, tok::kw_const,
541                       tok::kw_volatile)) {
542     found = true;
543     SkipTypeQualifiers();
544   }
545   return found;
546 }
547 
548 bool CPlusPlusNameParser::ConsumeDecltype() {
549   Bookmark start_position = SetBookmark();
550   if (!ConsumeToken(tok::kw_decltype))
551     return false;
552 
553   if (!ConsumeArguments())
554     return false;
555 
556   start_position.Remove();
557   return true;
558 }
559 
560 bool CPlusPlusNameParser::ConsumeTypename() {
561   Bookmark start_position = SetBookmark();
562   SkipTypeQualifiers();
563   if (!ConsumeBuiltinType() && !ConsumeDecltype()) {
564     if (!ParseFullNameImpl())
565       return false;
566   }
567   SkipPtrsAndRefs();
568   start_position.Remove();
569   return true;
570 }
571 
572 std::optional<CPlusPlusNameParser::ParsedNameRanges>
573 CPlusPlusNameParser::ParseFullNameImpl() {
574   // Name parsing state machine.
575   enum class State {
576     Beginning,       // start of the name
577     AfterTwoColons,  // right after ::
578     AfterIdentifier, // right after alphanumerical identifier ([a-z0-9_]+)
579     AfterTemplate,   // right after template brackets (<something>)
580     AfterOperator,   // right after name of C++ operator
581   };
582 
583   Bookmark start_position = SetBookmark();
584   State state = State::Beginning;
585   bool continue_parsing = true;
586   std::optional<size_t> last_coloncolon_position;
587 
588   while (continue_parsing && HasMoreTokens()) {
589     const auto &token = Peek();
590     switch (token.getKind()) {
591     case tok::raw_identifier: // Just a name.
592       if (state != State::Beginning && state != State::AfterTwoColons) {
593         continue_parsing = false;
594         break;
595       }
596       Advance();
597       state = State::AfterIdentifier;
598       break;
599     case tok::l_square: {
600       // Handles types or functions that were tagged
601       // with, e.g.,
602       //   [[gnu::abi_tag("tag1","tag2")]] func()
603       // and demangled/prettified into:
604       //   func[abi:tag1][abi:tag2]()
605 
606       // ABI tags only appear after a method or type name
607       const bool valid_state =
608           state == State::AfterIdentifier || state == State::AfterOperator;
609       if (!valid_state || !ConsumeAbiTag()) {
610         continue_parsing = false;
611       }
612 
613       break;
614     }
615     case tok::l_paren: {
616       if (state == State::Beginning || state == State::AfterTwoColons) {
617         // (anonymous namespace)
618         if (ConsumeAnonymousNamespace()) {
619           state = State::AfterIdentifier;
620           break;
621         }
622       }
623 
624       // Type declared inside a function 'func()::Type'
625       if (state != State::AfterIdentifier && state != State::AfterTemplate &&
626           state != State::AfterOperator) {
627         continue_parsing = false;
628         break;
629       }
630       Bookmark l_paren_position = SetBookmark();
631       // Consume the '(' ... ') [const]'.
632       if (!ConsumeArguments()) {
633         continue_parsing = false;
634         break;
635       }
636       SkipFunctionQualifiers();
637 
638       // Consume '::'
639       size_t coloncolon_position = GetCurrentPosition();
640       if (!ConsumeToken(tok::coloncolon)) {
641         continue_parsing = false;
642         break;
643       }
644       l_paren_position.Remove();
645       last_coloncolon_position = coloncolon_position;
646       state = State::AfterTwoColons;
647       break;
648     }
649     case tok::l_brace:
650       if (state == State::Beginning || state == State::AfterTwoColons) {
651         if (ConsumeLambda()) {
652           state = State::AfterIdentifier;
653           break;
654         }
655       }
656       continue_parsing = false;
657       break;
658     case tok::coloncolon: // Type nesting delimiter.
659       if (state != State::Beginning && state != State::AfterIdentifier &&
660           state != State::AfterTemplate) {
661         continue_parsing = false;
662         break;
663       }
664       last_coloncolon_position = GetCurrentPosition();
665       Advance();
666       state = State::AfterTwoColons;
667       break;
668     case tok::less: // Template brackets.
669       if (state != State::AfterIdentifier && state != State::AfterOperator) {
670         continue_parsing = false;
671         break;
672       }
673       if (!ConsumeTemplateArgs()) {
674         continue_parsing = false;
675         break;
676       }
677       state = State::AfterTemplate;
678       break;
679     case tok::kw_operator: // C++ operator overloading.
680       if (state != State::Beginning && state != State::AfterTwoColons) {
681         continue_parsing = false;
682         break;
683       }
684       if (!ConsumeOperator()) {
685         continue_parsing = false;
686         break;
687       }
688       state = State::AfterOperator;
689       break;
690     case tok::tilde: // Destructor.
691       if (state != State::Beginning && state != State::AfterTwoColons) {
692         continue_parsing = false;
693         break;
694       }
695       Advance();
696       if (ConsumeToken(tok::raw_identifier)) {
697         state = State::AfterIdentifier;
698       } else {
699         TakeBack();
700         continue_parsing = false;
701       }
702       break;
703     default:
704       continue_parsing = false;
705       break;
706     }
707   }
708 
709   if (state == State::AfterIdentifier || state == State::AfterOperator ||
710       state == State::AfterTemplate) {
711     ParsedNameRanges result;
712     if (last_coloncolon_position) {
713       result.context_range =
714           Range(start_position.GetSavedPosition(), *last_coloncolon_position);
715       result.basename_range =
716           Range(*last_coloncolon_position + 1, GetCurrentPosition());
717     } else {
718       result.basename_range =
719           Range(start_position.GetSavedPosition(), GetCurrentPosition());
720     }
721     start_position.Remove();
722     return result;
723   } else {
724     return std::nullopt;
725   }
726 }
727 
728 llvm::StringRef CPlusPlusNameParser::GetTextForRange(const Range &range) {
729   if (range.empty())
730     return llvm::StringRef();
731   assert(range.begin_index < range.end_index);
732   assert(range.begin_index < m_tokens.size());
733   assert(range.end_index <= m_tokens.size());
734   clang::Token &first_token = m_tokens[range.begin_index];
735   clang::Token &last_token = m_tokens[range.end_index - 1];
736   clang::SourceLocation start_loc = first_token.getLocation();
737   clang::SourceLocation end_loc = last_token.getLocation();
738   unsigned start_pos = start_loc.getRawEncoding();
739   unsigned end_pos = end_loc.getRawEncoding() + last_token.getLength();
740   return m_text.take_front(end_pos).drop_front(start_pos);
741 }
742 
743 static const clang::LangOptions &GetLangOptions() {
744   static clang::LangOptions g_options;
745   static llvm::once_flag g_once_flag;
746   llvm::call_once(g_once_flag, []() {
747     g_options.LineComment = true;
748     g_options.C99 = true;
749     g_options.C11 = true;
750     g_options.CPlusPlus = true;
751     g_options.CPlusPlus11 = true;
752     g_options.CPlusPlus14 = true;
753     g_options.CPlusPlus17 = true;
754   });
755   return g_options;
756 }
757 
758 static const llvm::StringMap<tok::TokenKind> &GetKeywordsMap() {
759   static llvm::StringMap<tok::TokenKind> g_map{
760 #define KEYWORD(Name, Flags) {llvm::StringRef(#Name), tok::kw_##Name},
761 #include "clang/Basic/TokenKinds.def"
762 #undef KEYWORD
763   };
764   return g_map;
765 }
766 
767 void CPlusPlusNameParser::ExtractTokens() {
768   if (m_text.empty())
769     return;
770   clang::Lexer lexer(clang::SourceLocation(), GetLangOptions(), m_text.data(),
771                      m_text.data(), m_text.data() + m_text.size());
772   const auto &kw_map = GetKeywordsMap();
773   clang::Token token;
774   for (lexer.LexFromRawLexer(token); !token.is(clang::tok::eof);
775        lexer.LexFromRawLexer(token)) {
776     if (token.is(clang::tok::raw_identifier)) {
777       auto it = kw_map.find(token.getRawIdentifier());
778       if (it != kw_map.end()) {
779         token.setKind(it->getValue());
780       }
781     }
782 
783     m_tokens.push_back(token);
784   }
785 }
786