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