[Lldb-commits] [lldb] r299374 - New C++ function name parsing logic

Eugene Zemtsov via lldb-commits lldb-commits at lists.llvm.org
Mon Apr 3 11:59:34 PDT 2017


Author: eugene
Date: Mon Apr  3 13:59:34 2017
New Revision: 299374

URL: http://llvm.org/viewvc/llvm-project?rev=299374&view=rev
Log:
New C++ function name parsing logic

Current implementation of CPlusPlusLanguage::MethodName::Parse() doesn't
get anywhere close to covering full extent of possible function declarations.
It causes incorrect behavior in avoid-stepping and sometimes messes
printing of thread backtrace.

This change implements more methodical parsing logic based on clang
lexer and simple recursive parser.

Examples:
void std::vector<Class, std::allocator<Class>>::_M_emplace_back_aux<Class const&>(Class const&)
void (*&std::_Any_data::_M_access<void (*)()>())()

Differential Revision: https://reviews.llvm.org/D31451

Added:
    lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp
    lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h
Modified:
    lldb/trunk/source/Plugins/Language/CPlusPlus/CMakeLists.txt
    lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
    lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
    lldb/trunk/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp

Modified: lldb/trunk/source/Plugins/Language/CPlusPlus/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Language/CPlusPlus/CMakeLists.txt?rev=299374&r1=299373&r2=299374&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Language/CPlusPlus/CMakeLists.txt (original)
+++ lldb/trunk/source/Plugins/Language/CPlusPlus/CMakeLists.txt Mon Apr  3 13:59:34 2017
@@ -1,6 +1,7 @@
 add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
   BlockPointer.cpp
   CPlusPlusLanguage.cpp
+  CPlusPlusNameParser.cpp
   CxxStringTypes.cpp
   LibCxx.cpp
   LibCxxAtomic.cpp

Modified: lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp?rev=299374&r1=299373&r2=299374&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp (original)
+++ lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp Mon Apr  3 13:59:34 2017
@@ -21,7 +21,6 @@
 
 // Other libraries and framework includes
 #include "llvm/ADT/StringRef.h"
-#include "llvm/Support/Threading.h"
 
 // Project includes
 #include "lldb/Core/PluginManager.h"
@@ -36,6 +35,7 @@
 #include "lldb/Utility/RegularExpression.h"
 
 #include "BlockPointer.h"
+#include "CPlusPlusNameParser.h"
 #include "CxxStringTypes.h"
 #include "LibCxx.h"
 #include "LibCxxAtomic.h"
@@ -85,15 +85,14 @@ void CPlusPlusLanguage::MethodName::Clea
   m_context = llvm::StringRef();
   m_arguments = llvm::StringRef();
   m_qualifiers = llvm::StringRef();
-  m_type = eTypeInvalid;
   m_parsed = false;
   m_parse_error = false;
 }
 
-bool ReverseFindMatchingChars(const llvm::StringRef &s,
-                              const llvm::StringRef &left_right_chars,
-                              size_t &left_pos, size_t &right_pos,
-                              size_t pos = llvm::StringRef::npos) {
+static bool ReverseFindMatchingChars(const llvm::StringRef &s,
+                                     const llvm::StringRef &left_right_chars,
+                                     size_t &left_pos, size_t &right_pos,
+                                     size_t pos = llvm::StringRef::npos) {
   assert(left_right_chars.size() == 2);
   left_pos = llvm::StringRef::npos;
   const char left_char = left_right_chars[0];
@@ -119,10 +118,9 @@ bool ReverseFindMatchingChars(const llvm
   return false;
 }
 
-static bool IsValidBasename(const llvm::StringRef &basename) {
-  // Check that the basename matches with the following regular expression or is
-  // an operator name:
-  // "^~?([A-Za-z_][A-Za-z_0-9]*)(<.*>)?$"
+static bool IsTrivialBasename(const llvm::StringRef &basename) {
+  // Check that the basename matches with the following regular expression
+  // "^~?([A-Za-z_][A-Za-z_0-9]*)$"
   // We are using a hand written implementation because it is significantly more
   // efficient then
   // using the general purpose regular expression library.
@@ -149,100 +147,69 @@ static bool IsValidBasename(const llvm::
   if (idx == basename.size())
     return true;
 
-  // Check for basename with template arguments
-  // TODO: Improve the quality of the validation with validating the template
-  // arguments
-  if (basename[idx] == '<' && basename.back() == '>')
-    return true;
+  return false;
+}
 
-  // Check if the basename is a vaild C++ operator name
-  if (!basename.startswith("operator"))
-    return false;
+bool CPlusPlusLanguage::MethodName::TrySimplifiedParse() {
+  // This method tries to parse simple method definitions
+  // which are presumably most comman in user programs.
+  // Definitions that can be parsed by this function don't have return types
+  // and templates in the name.
+  // A::B::C::fun(std::vector<T> &) const
+  size_t arg_start, arg_end;
+  llvm::StringRef full(m_full.GetCString());
+  llvm::StringRef parens("()", 2);
+  if (ReverseFindMatchingChars(full, parens, arg_start, arg_end)) {
+    m_arguments = full.substr(arg_start, arg_end - arg_start + 1);
+    if (arg_end + 1 < full.size())
+      m_qualifiers = full.substr(arg_end + 1).ltrim();
 
-  static RegularExpression g_operator_regex(
-      llvm::StringRef("^(operator)( "
-                      "?)([A-Za-z_][A-Za-z_0-9]*|\\(\\)|"
-                      "\\[\\]|[\\^<>=!\\/"
-                      "*+-]+)(<.*>)?(\\[\\])?$"));
-  std::string basename_str(basename.str());
-  return g_operator_regex.Execute(basename_str, nullptr);
+    if (arg_start == 0)
+      return false;
+    size_t basename_end = arg_start;
+    size_t context_start = 0;
+    size_t context_end = full.rfind(':', basename_end);
+    if (context_end == llvm::StringRef::npos)
+      m_basename = full.substr(0, basename_end);
+    else {
+      if (context_start < context_end)
+        m_context = full.substr(context_start, context_end - 1 - context_start);
+      const size_t basename_begin = context_end + 1;
+      m_basename = full.substr(basename_begin, basename_end - basename_begin);
+    }
+
+    if (IsTrivialBasename(m_basename)) {
+      return true;
+    } else {
+      // The C++ basename doesn't match our regular expressions so this can't
+      // be a valid C++ method, clear everything out and indicate an error
+      m_context = llvm::StringRef();
+      m_basename = llvm::StringRef();
+      m_arguments = llvm::StringRef();
+      m_qualifiers = llvm::StringRef();
+      return false;
+    }
+  }
+  return false;
 }
 
 void CPlusPlusLanguage::MethodName::Parse() {
   if (!m_parsed && m_full) {
-    //        ConstString mangled;
-    //        m_full.GetMangledCounterpart(mangled);
-    //        printf ("\n   parsing = '%s'\n", m_full.GetCString());
-    //        if (mangled)
-    //            printf ("   mangled = '%s'\n", mangled.GetCString());
-    m_parse_error = false;
-    m_parsed = true;
-    llvm::StringRef full(m_full.GetCString());
-
-    size_t arg_start, arg_end;
-    llvm::StringRef parens("()", 2);
-    if (ReverseFindMatchingChars(full, parens, arg_start, arg_end)) {
-      m_arguments = full.substr(arg_start, arg_end - arg_start + 1);
-      if (arg_end + 1 < full.size())
-        m_qualifiers = full.substr(arg_end + 1);
-      if (arg_start > 0) {
-        size_t basename_end = arg_start;
-        size_t context_start = 0;
-        size_t context_end = llvm::StringRef::npos;
-        if (basename_end > 0 && full[basename_end - 1] == '>') {
-          // TODO: handle template junk...
-          // Templated function
-          size_t template_start, template_end;
-          llvm::StringRef lt_gt("<>", 2);
-          if (ReverseFindMatchingChars(full, lt_gt, template_start,
-                                       template_end, basename_end)) {
-            // Check for templated functions that include return type like:
-            // 'void foo<Int>()'
-            context_start = full.rfind(' ', template_start);
-            if (context_start == llvm::StringRef::npos)
-              context_start = 0;
-            else
-              ++context_start;
-
-            context_end = full.rfind(':', template_start);
-            if (context_end == llvm::StringRef::npos ||
-                context_end < context_start)
-              context_end = context_start;
-          } else {
-            context_end = full.rfind(':', basename_end);
-          }
-        } else if (context_end == llvm::StringRef::npos) {
-          context_end = full.rfind(':', basename_end);
-        }
-
-        if (context_end == llvm::StringRef::npos)
-          m_basename = full.substr(0, basename_end);
-        else {
-          if (context_start < context_end)
-            m_context =
-                full.substr(context_start, context_end - 1 - context_start);
-          const size_t basename_begin = context_end + 1;
-          m_basename =
-              full.substr(basename_begin, basename_end - basename_begin);
-        }
-        m_type = eTypeUnknownMethod;
+    if (TrySimplifiedParse()) {
+      m_parse_error = false;
+    } else {
+      CPlusPlusNameParser parser(m_full.GetStringRef());
+      if (auto function = parser.ParseAsFunctionDefinition()) {
+        m_basename = function.getValue().name.basename;
+        m_context = function.getValue().name.context;
+        m_arguments = function.getValue().arguments;
+        m_qualifiers = function.getValue().qualifiers;
+        m_parse_error = false;
       } else {
         m_parse_error = true;
-        return;
-      }
-
-      if (!IsValidBasename(m_basename)) {
-        // The C++ basename doesn't match our regular expressions so this can't
-        // be a valid C++ method, clear everything out and indicate an error
-        m_context = llvm::StringRef();
-        m_basename = llvm::StringRef();
-        m_arguments = llvm::StringRef();
-        m_qualifiers = llvm::StringRef();
-        m_parse_error = true;
       }
-    } else {
-      m_parse_error = true;
     }
+    m_parsed = true;
   }
 }
 
@@ -273,14 +240,13 @@ llvm::StringRef CPlusPlusLanguage::Metho
 std::string CPlusPlusLanguage::MethodName::GetScopeQualifiedName() {
   if (!m_parsed)
     Parse();
-  if (m_basename.empty() || m_context.empty())
-    return std::string();
+  if (m_context.empty())
+    return m_basename;
 
   std::string res;
   res += m_context;
   res += "::";
   res += m_basename;
-
   return res;
 }
 
@@ -296,13 +262,10 @@ bool CPlusPlusLanguage::IsCPPMangledName
 
 bool CPlusPlusLanguage::ExtractContextAndIdentifier(
     const char *name, llvm::StringRef &context, llvm::StringRef &identifier) {
-  static RegularExpression g_basename_regex(llvm::StringRef(
-      "^(([A-Za-z_][A-Za-z_0-9]*::)*)(~?[A-Za-z_~][A-Za-z_0-9]*)$"));
-  RegularExpression::Match match(4);
-  if (g_basename_regex.Execute(llvm::StringRef::withNullAsEmpty(name),
-                               &match)) {
-    match.GetMatchAtIndex(name, 1, context);
-    match.GetMatchAtIndex(name, 3, identifier);
+  CPlusPlusNameParser parser(name);
+  if (auto full_name = parser.ParseAsFullName()) {
+    identifier = full_name.getValue().basename;
+    context = full_name.getValue().context;
     return true;
   }
   return false;

Modified: lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h?rev=299374&r1=299373&r2=299374&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h (original)
+++ lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h Mon Apr  3 13:59:34 2017
@@ -29,20 +29,13 @@ class CPlusPlusLanguage : public Languag
 public:
   class MethodName {
   public:
-    enum Type {
-      eTypeInvalid,
-      eTypeUnknownMethod,
-      eTypeClassMethod,
-      eTypeInstanceMethod
-    };
-
     MethodName()
         : m_full(), m_basename(), m_context(), m_arguments(), m_qualifiers(),
-          m_type(eTypeInvalid), m_parsed(false), m_parse_error(false) {}
+          m_parsed(false), m_parse_error(false) {}
 
     MethodName(const ConstString &s)
         : m_full(s), m_basename(), m_context(), m_arguments(), m_qualifiers(),
-          m_type(eTypeInvalid), m_parsed(false), m_parse_error(false) {}
+          m_parsed(false), m_parse_error(false) {}
 
     void Clear();
 
@@ -51,13 +44,9 @@ public:
         Parse();
       if (m_parse_error)
         return false;
-      if (m_type == eTypeInvalid)
-        return false;
       return (bool)m_full;
     }
 
-    Type GetType() const { return m_type; }
-
     const ConstString &GetFullName() const { return m_full; }
 
     std::string GetScopeQualifiedName();
@@ -72,6 +61,7 @@ public:
 
   protected:
     void Parse();
+    bool TrySimplifiedParse();
 
     ConstString m_full; // Full name:
                         // "lldb::SBTarget::GetBreakpointAtIndex(unsigned int)
@@ -80,7 +70,6 @@ public:
     llvm::StringRef m_context;    // Decl context: "lldb::SBTarget"
     llvm::StringRef m_arguments;  // Arguments:    "(unsigned int)"
     llvm::StringRef m_qualifiers; // Qualifiers:   "const"
-    Type m_type;
     bool m_parsed;
     bool m_parse_error;
   };
@@ -121,7 +110,7 @@ public:
   // If the name is a lone C identifier (e.g. C) or a qualified C identifier
   // (e.g. A::B::C) it will return true,
   // and identifier will be the identifier (C and C respectively) and the
-  // context will be "" and "A::B::" respectively.
+  // context will be "" and "A::B" respectively.
   // If the name fails the heuristic matching for a qualified or unqualified
   // C/C++ identifier, then it will return false
   // and identifier and context will be unchanged.

Added: lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp?rev=299374&view=auto
==============================================================================
--- lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp (added)
+++ lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp Mon Apr  3 13:59:34 2017
@@ -0,0 +1,614 @@
+//===-- CPlusPlusNameParser.cpp ---------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CPlusPlusNameParser.h"
+
+#include "clang/Basic/IdentifierTable.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Threading.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using llvm::Optional;
+using llvm::None;
+using ParsedFunction = lldb_private::CPlusPlusNameParser::ParsedFunction;
+using ParsedName = lldb_private::CPlusPlusNameParser::ParsedName;
+namespace tok = clang::tok;
+
+Optional<ParsedFunction> CPlusPlusNameParser::ParseAsFunctionDefinition() {
+  m_next_token_index = 0;
+  Optional<ParsedFunction> result(None);
+
+  // Try to parse the name as function without a return type specified
+  // e.g. main(int, char*[])
+  {
+    Bookmark start_position = SetBookmark();
+    result = ParseFunctionImpl(false);
+    if (result && !HasMoreTokens())
+      return result;
+  }
+
+  // Try to parse the name as function with function pointer return type
+  // e.g. void (*get_func(const char*))()
+  result = ParseFuncPtr(true);
+  if (result)
+    return result;
+
+  // Finally try to parse the name as a function with non-function return type
+  // e.g. int main(int, char*[])
+  result = ParseFunctionImpl(true);
+  return result;
+}
+
+Optional<ParsedName> CPlusPlusNameParser::ParseAsFullName() {
+  m_next_token_index = 0;
+  Optional<ParsedNameRanges> name_ranges = ParseFullNameImpl();
+  if (!name_ranges)
+    return None;
+  ParsedName result;
+  result.basename = GetTextForRange(name_ranges.getValue().basename_range);
+  result.context = GetTextForRange(name_ranges.getValue().context_range);
+  return result;
+}
+
+bool CPlusPlusNameParser::HasMoreTokens() {
+  return m_next_token_index < m_tokens.size();
+}
+
+void CPlusPlusNameParser::Advance() { ++m_next_token_index; }
+
+void CPlusPlusNameParser::TakeBack() { --m_next_token_index; }
+
+bool CPlusPlusNameParser::ConsumeToken(tok::TokenKind kind) {
+  if (!HasMoreTokens())
+    return false;
+
+  if (!Peek().is(kind))
+    return false;
+
+  Advance();
+  return true;
+}
+
+template <typename... Ts> bool CPlusPlusNameParser::ConsumeToken(Ts... kinds) {
+  if (!HasMoreTokens())
+    return false;
+
+  if (!Peek().isOneOf(kinds...))
+    return false;
+
+  Advance();
+  return true;
+}
+
+CPlusPlusNameParser::Bookmark CPlusPlusNameParser::SetBookmark() {
+  return Bookmark(m_next_token_index);
+}
+
+size_t CPlusPlusNameParser::GetCurrentPosition() { return m_next_token_index; }
+
+clang::Token &CPlusPlusNameParser::Peek() {
+  assert(HasMoreTokens());
+  return m_tokens[m_next_token_index];
+}
+
+Optional<ParsedFunction>
+CPlusPlusNameParser::ParseFunctionImpl(bool expect_return_type) {
+  Bookmark start_position = SetBookmark();
+  if (expect_return_type) {
+    // Consume return type if it's expected.
+    if (!ConsumeTypename())
+      return None;
+  }
+
+  auto maybe_name = ParseFullNameImpl();
+  if (!maybe_name) {
+    return None;
+  }
+
+  size_t argument_start = GetCurrentPosition();
+  if (!ConsumeArguments()) {
+    return None;
+  }
+
+  size_t qualifiers_start = GetCurrentPosition();
+  SkipFunctionQualifiers();
+  size_t end_position = GetCurrentPosition();
+
+  ParsedFunction result;
+  result.name.basename = GetTextForRange(maybe_name.getValue().basename_range);
+  result.name.context = GetTextForRange(maybe_name.getValue().context_range);
+  result.arguments = GetTextForRange(Range(argument_start, qualifiers_start));
+  result.qualifiers = GetTextForRange(Range(qualifiers_start, end_position));
+  start_position.Remove();
+  return result;
+}
+
+Optional<ParsedFunction>
+CPlusPlusNameParser::ParseFuncPtr(bool expect_return_type) {
+  Bookmark start_position = SetBookmark();
+  if (expect_return_type) {
+    // Consume return type.
+    if (!ConsumeTypename())
+      return None;
+  }
+
+  if (!ConsumeToken(tok::l_paren))
+    return None;
+  if (!ConsumePtrsAndRefs())
+    return None;
+
+  {
+    Bookmark before_inner_function_pos = SetBookmark();
+    auto maybe_inner_function_name = ParseFunctionImpl(false);
+    if (maybe_inner_function_name)
+      if (ConsumeToken(tok::r_paren))
+        if (ConsumeArguments()) {
+          SkipFunctionQualifiers();
+          start_position.Remove();
+          before_inner_function_pos.Remove();
+          return maybe_inner_function_name;
+        }
+  }
+
+  auto maybe_inner_function_ptr_name = ParseFuncPtr(false);
+  if (maybe_inner_function_ptr_name)
+    if (ConsumeToken(tok::r_paren))
+      if (ConsumeArguments()) {
+        SkipFunctionQualifiers();
+        start_position.Remove();
+        return maybe_inner_function_ptr_name;
+      }
+  return None;
+}
+
+bool CPlusPlusNameParser::ConsumeArguments() {
+  return ConsumeBrackets(tok::l_paren, tok::r_paren);
+}
+
+bool CPlusPlusNameParser::ConsumeTemplateArgs() {
+  Bookmark start_position = SetBookmark();
+  if (!HasMoreTokens() || Peek().getKind() != tok::less)
+    return false;
+  Advance();
+
+  // Consuming template arguments is a bit trickier than consuming function
+  // arguments, because '<' '>' brackets are not always trivially balanced.
+  // In some rare cases tokens '<' and '>' can appear inside template arguments
+  // as arithmetic or shift operators not as template brackets.
+  // Examples: std::enable_if<(10u)<(64), bool>
+  //           f<A<operator<(X,Y)::Subclass>>
+  // Good thing that compiler makes sure that really ambiguous cases of
+  // '>' usage should be enclosed within '()' brackets.
+  int template_counter = 1;
+  bool can_open_template = false;
+  while (HasMoreTokens() && template_counter > 0) {
+    tok::TokenKind kind = Peek().getKind();
+    switch (kind) {
+    case tok::greatergreater:
+      template_counter -= 2;
+      can_open_template = false;
+      Advance();
+      break;
+    case tok::greater:
+      --template_counter;
+      can_open_template = false;
+      Advance();
+      break;
+    case tok::less:
+      // '<' is an attempt to open a subteamplte
+      // check if parser is at the point where it's actually possible,
+      // otherwise it's just a part of an expression like 'sizeof(T)<(10)'.
+      // No need to do the same for '>' because compiler actually makes sure
+      // that '>' always surrounded by brackets to avoid ambiguity.
+      if (can_open_template)
+        ++template_counter;
+      can_open_template = false;
+      Advance();
+      break;
+    case tok::kw_operator: // C++ operator overloading.
+      if (!ConsumeOperator())
+        return false;
+      can_open_template = true;
+      break;
+    case tok::raw_identifier:
+      can_open_template = true;
+      Advance();
+      break;
+    case tok::l_square:
+      if (!ConsumeBrackets(tok::l_square, tok::r_square))
+        return false;
+      can_open_template = false;
+      break;
+    case tok::l_paren:
+      if (!ConsumeArguments())
+        return false;
+      can_open_template = false;
+      break;
+    default:
+      can_open_template = false;
+      Advance();
+      break;
+    }
+  }
+
+  assert(template_counter >= 0);
+  if (template_counter > 0) {
+    return false;
+  }
+  start_position.Remove();
+  return true;
+}
+
+bool CPlusPlusNameParser::ConsumeAnonymousNamespace() {
+  Bookmark start_position = SetBookmark();
+  if (!ConsumeToken(tok::l_paren)) {
+    return false;
+  }
+  constexpr llvm::StringLiteral g_anonymous("anonymous");
+  if (HasMoreTokens() && Peek().is(tok::raw_identifier) &&
+      Peek().getRawIdentifier() == g_anonymous) {
+    Advance();
+  } else {
+    return false;
+  }
+
+  if (!ConsumeToken(tok::kw_namespace)) {
+    return false;
+  }
+
+  if (!ConsumeToken(tok::r_paren)) {
+    return false;
+  }
+  start_position.Remove();
+  return true;
+}
+
+bool CPlusPlusNameParser::ConsumeBrackets(tok::TokenKind left,
+                                          tok::TokenKind right) {
+  Bookmark start_position = SetBookmark();
+  if (!HasMoreTokens() || Peek().getKind() != left)
+    return false;
+  Advance();
+
+  int counter = 1;
+  while (HasMoreTokens() && counter > 0) {
+    tok::TokenKind kind = Peek().getKind();
+    if (kind == right)
+      --counter;
+    else if (kind == left)
+      ++counter;
+    Advance();
+  }
+
+  assert(counter >= 0);
+  if (counter > 0) {
+    return false;
+  }
+  start_position.Remove();
+  return true;
+}
+
+bool CPlusPlusNameParser::ConsumeOperator() {
+  Bookmark start_position = SetBookmark();
+  if (!ConsumeToken(tok::kw_operator))
+    return false;
+
+  if (!HasMoreTokens()) {
+    return false;
+  }
+
+  const auto &token = Peek();
+  switch (token.getKind()) {
+  case tok::kw_new:
+  case tok::kw_delete:
+    // This is 'new' or 'delete' operators.
+    Advance();
+    // Check for array new/delete.
+    if (HasMoreTokens() && Peek().is(tok::l_square)) {
+      // Consume the '[' and ']'.
+      if (!ConsumeBrackets(tok::l_square, tok::r_square))
+        return false;
+    }
+    break;
+
+#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly)  \
+  case tok::Token:                                                             \
+    Advance();                                                                 \
+    break;
+#define OVERLOADED_OPERATOR_MULTI(Name, Spelling, Unary, Binary, MemberOnly)
+#include "clang/Basic/OperatorKinds.def"
+#undef OVERLOADED_OPERATOR
+#undef OVERLOADED_OPERATOR_MULTI
+
+  case tok::l_paren:
+    // Call operator consume '(' ... ')'.
+    if (ConsumeBrackets(tok::l_paren, tok::r_paren))
+      break;
+    return false;
+
+  case tok::l_square:
+    // This is a [] operator.
+    // Consume the '[' and ']'.
+    if (ConsumeBrackets(tok::l_square, tok::r_square))
+      break;
+    return false;
+
+  default:
+    // This might be a cast operator.
+    if (ConsumeTypename())
+      break;
+    return false;
+  }
+  start_position.Remove();
+  return true;
+}
+
+void CPlusPlusNameParser::SkipTypeQualifiers() {
+  while (ConsumeToken(tok::kw_const, tok::kw_volatile))
+    ;
+}
+
+void CPlusPlusNameParser::SkipFunctionQualifiers() {
+  while (ConsumeToken(tok::kw_const, tok::kw_volatile, tok::amp, tok::ampamp))
+    ;
+}
+
+bool CPlusPlusNameParser::ConsumeBuiltinType() {
+  bool result = false;
+  bool continue_parsing = true;
+  // Built-in types can be made of a few keywords
+  // like 'unsigned long long int'. This function
+  // consumes all built-in type keywords without
+  // checking if they make sense like 'unsigned char void'.
+  while (continue_parsing && HasMoreTokens()) {
+    switch (Peek().getKind()) {
+    case tok::kw_short:
+    case tok::kw_long:
+    case tok::kw___int64:
+    case tok::kw___int128:
+    case tok::kw_signed:
+    case tok::kw_unsigned:
+    case tok::kw_void:
+    case tok::kw_char:
+    case tok::kw_int:
+    case tok::kw_half:
+    case tok::kw_float:
+    case tok::kw_double:
+    case tok::kw___float128:
+    case tok::kw_wchar_t:
+    case tok::kw_bool:
+    case tok::kw_char16_t:
+    case tok::kw_char32_t:
+      result = true;
+      Advance();
+      break;
+    default:
+      continue_parsing = false;
+      break;
+    }
+  }
+  return result;
+}
+
+void CPlusPlusNameParser::SkipPtrsAndRefs() {
+  // Ignoring result.
+  ConsumePtrsAndRefs();
+}
+
+bool CPlusPlusNameParser::ConsumePtrsAndRefs() {
+  bool found = false;
+  SkipTypeQualifiers();
+  while (ConsumeToken(tok::star, tok::amp, tok::ampamp, tok::kw_const,
+                      tok::kw_volatile)) {
+    found = true;
+    SkipTypeQualifiers();
+  }
+  return found;
+}
+
+bool CPlusPlusNameParser::ConsumeTypename() {
+  Bookmark start_position = SetBookmark();
+  SkipTypeQualifiers();
+  if (!ConsumeBuiltinType()) {
+    if (!ParseFullNameImpl())
+      return false;
+  }
+  SkipPtrsAndRefs();
+  start_position.Remove();
+  return true;
+}
+
+Optional<CPlusPlusNameParser::ParsedNameRanges>
+CPlusPlusNameParser::ParseFullNameImpl() {
+  // Name parsing state machine.
+  enum class State {
+    Beginning,       // start of the name
+    AfterTwoColons,  // right after ::
+    AfterIdentifier, // right after alphanumerical identifier ([a-z0-9_]+)
+    AfterTemplate,   // right after template brackets (<something>)
+    AfterOperator,   // right after name of C++ operator
+  };
+
+  Bookmark start_position = SetBookmark();
+  State state = State::Beginning;
+  bool continue_parsing = true;
+  Optional<size_t> last_coloncolon_position = None;
+
+  while (continue_parsing && HasMoreTokens()) {
+    const auto &token = Peek();
+    switch (token.getKind()) {
+    case tok::raw_identifier: // Just a name.
+      if (state != State::Beginning && state != State::AfterTwoColons) {
+        continue_parsing = false;
+        break;
+      }
+      Advance();
+      state = State::AfterIdentifier;
+      break;
+    case tok::l_paren: {
+      if (state == State::Beginning || state == State::AfterTwoColons) {
+        // (anonymous namespace)
+        if (ConsumeAnonymousNamespace()) {
+          state = State::AfterIdentifier;
+          break;
+        }
+      }
+
+      // Type declared inside a function 'func()::Type'
+      if (state != State::AfterIdentifier && state != State::AfterTemplate &&
+          state != State::AfterOperator) {
+        continue_parsing = false;
+        break;
+      }
+      Bookmark l_paren_position = SetBookmark();
+      // Consume the '(' ... ') [const]'.
+      if (!ConsumeArguments()) {
+        continue_parsing = false;
+        break;
+      }
+      SkipFunctionQualifiers();
+
+      // Consume '::'
+      size_t coloncolon_position = GetCurrentPosition();
+      if (!ConsumeToken(tok::coloncolon)) {
+        continue_parsing = false;
+        break;
+      }
+      l_paren_position.Remove();
+      last_coloncolon_position = coloncolon_position;
+      state = State::AfterTwoColons;
+      break;
+    }
+    case tok::coloncolon: // Type nesting delimiter.
+      if (state != State::Beginning && state != State::AfterIdentifier &&
+          state != State::AfterTemplate) {
+        continue_parsing = false;
+        break;
+      }
+      last_coloncolon_position = GetCurrentPosition();
+      Advance();
+      state = State::AfterTwoColons;
+      break;
+    case tok::less: // Template brackets.
+      if (state != State::AfterIdentifier && state != State::AfterOperator) {
+        continue_parsing = false;
+        break;
+      }
+      if (!ConsumeTemplateArgs()) {
+        continue_parsing = false;
+        break;
+      }
+      state = State::AfterTemplate;
+      break;
+    case tok::kw_operator: // C++ operator overloading.
+      if (state != State::Beginning && state != State::AfterTwoColons) {
+        continue_parsing = false;
+        break;
+      }
+      if (!ConsumeOperator()) {
+        continue_parsing = false;
+        break;
+      }
+      state = State::AfterOperator;
+      break;
+    case tok::tilde: // Destructor.
+      if (state != State::Beginning && state != State::AfterTwoColons) {
+        continue_parsing = false;
+        break;
+      }
+      Advance();
+      if (ConsumeToken(tok::raw_identifier)) {
+        state = State::AfterIdentifier;
+      } else {
+        TakeBack();
+        continue_parsing = false;
+      }
+      break;
+    default:
+      continue_parsing = false;
+      break;
+    }
+  }
+
+  if (state == State::AfterIdentifier || state == State::AfterOperator ||
+      state == State::AfterTemplate) {
+    ParsedNameRanges result;
+    if (last_coloncolon_position) {
+      result.context_range = Range(start_position.GetSavedPosition(),
+                                   last_coloncolon_position.getValue());
+      result.basename_range =
+          Range(last_coloncolon_position.getValue() + 1, GetCurrentPosition());
+    } else {
+      result.basename_range =
+          Range(start_position.GetSavedPosition(), GetCurrentPosition());
+    }
+    start_position.Remove();
+    return result;
+  } else {
+    return None;
+  }
+}
+
+llvm::StringRef CPlusPlusNameParser::GetTextForRange(const Range &range) {
+  if (range.empty())
+    return llvm::StringRef();
+  assert(range.begin_index < range.end_index);
+  assert(range.begin_index < m_tokens.size());
+  assert(range.end_index <= m_tokens.size());
+  clang::Token &first_token = m_tokens[range.begin_index];
+  clang::Token &last_token = m_tokens[range.end_index - 1];
+  clang::SourceLocation start_loc = first_token.getLocation();
+  clang::SourceLocation end_loc = last_token.getLocation();
+  unsigned start_pos = start_loc.getRawEncoding();
+  unsigned end_pos = end_loc.getRawEncoding() + last_token.getLength();
+  return m_text.take_front(end_pos).drop_front(start_pos);
+}
+
+static const clang::LangOptions &GetLangOptions() {
+  static clang::LangOptions g_options;
+  static llvm::once_flag g_once_flag;
+  llvm::call_once(g_once_flag, []() {
+    g_options.LineComment = true;
+    g_options.C99 = true;
+    g_options.C11 = true;
+    g_options.CPlusPlus = true;
+    g_options.CPlusPlus11 = true;
+    g_options.CPlusPlus14 = true;
+    g_options.CPlusPlus1z = true;
+  });
+  return g_options;
+}
+
+static const llvm::StringMap<tok::TokenKind> &GetKeywordsMap() {
+  static llvm::StringMap<tok::TokenKind> g_map{
+#define KEYWORD(Name, Flags) {llvm::StringRef(#Name), tok::kw_##Name},
+#include "clang/Basic/TokenKinds.def"
+#undef KEYWORD
+  };
+  return g_map;
+}
+
+void CPlusPlusNameParser::ExtractTokens() {
+  clang::Lexer lexer(clang::SourceLocation(), GetLangOptions(), m_text.data(),
+                     m_text.data(), m_text.data() + m_text.size());
+  const auto &kw_map = GetKeywordsMap();
+  clang::Token token;
+  for (lexer.LexFromRawLexer(token); !token.is(clang::tok::eof);
+       lexer.LexFromRawLexer(token)) {
+    if (token.is(clang::tok::raw_identifier)) {
+      auto it = kw_map.find(token.getRawIdentifier());
+      if (it != kw_map.end()) {
+        token.setKind(it->getValue());
+      }
+    }
+
+    m_tokens.push_back(token);
+  }
+}

Added: lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h?rev=299374&view=auto
==============================================================================
--- lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h (added)
+++ lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h Mon Apr  3 13:59:34 2017
@@ -0,0 +1,176 @@
+//===-- CPlusPlusNameParser.h -----------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CPlusPlusNameParser_h_
+#define liblldb_CPlusPlusNameParser_h_
+
+// C Includes
+// C++ Includes
+
+// Other libraries and framework includes
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+
+// Project includes
+#include "lldb/Utility/ConstString.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+// Helps to validate and obtain various parts of C++ definitions.
+class CPlusPlusNameParser {
+public:
+  CPlusPlusNameParser(llvm::StringRef text) : m_text(text) { ExtractTokens(); }
+
+  struct ParsedName {
+    llvm::StringRef basename;
+    llvm::StringRef context;
+  };
+
+  struct ParsedFunction {
+    ParsedName name;
+    llvm::StringRef arguments;
+    llvm::StringRef qualifiers;
+  };
+
+  // Treats given text as a function definition and parses it.
+  // Function definition might or might not have a return type and this should
+  // change parsing result.
+  // Examples:
+  //    main(int, chat const*)
+  //    T fun(int, bool)
+  //    std::vector<int>::push_back(int)
+  //    int& map<int, pair<short, int>>::operator[](short) const
+  //    int (*get_function(const chat *))()
+  llvm::Optional<ParsedFunction> ParseAsFunctionDefinition();
+
+  // Treats given text as a potentially nested name of C++ entity (function,
+  // class, field) and parses it.
+  // Examples:
+  //    main
+  //    fun
+  //    std::vector<int>::push_back
+  //    map<int, pair<short, int>>::operator[]
+  //    func<C>(int, C&)::nested_class::method
+  llvm::Optional<ParsedName> ParseAsFullName();
+
+private:
+  // A C++ definition to parse.
+  llvm::StringRef m_text;
+  // Tokens extracted from m_text.
+  llvm::SmallVector<clang::Token, 30> m_tokens;
+  // Index of the next token to look at from m_tokens.
+  size_t m_next_token_index = 0;
+
+  // Range of tokens saved in m_next_token_index.
+  struct Range {
+    size_t begin_index = 0;
+    size_t end_index = 0;
+
+    Range() {}
+    Range(size_t begin, size_t end) : begin_index(begin), end_index(end) {
+      assert(end >= begin);
+    }
+
+    size_t size() const { return end_index - begin_index; }
+
+    bool empty() const { return size() == 0; }
+  };
+
+  struct ParsedNameRanges {
+    Range basename_range;
+    Range context_range;
+  };
+
+  // Bookmark automatically restores parsing position (m_next_token_index)
+  // when destructed unless it's manually removed with Remove().
+  class Bookmark {
+  public:
+    Bookmark(size_t &position)
+        : m_position(position), m_position_value(position) {}
+    Bookmark(const Bookmark &) = delete;
+    Bookmark(Bookmark &&b)
+        : m_position(b.m_position), m_position_value(b.m_position_value),
+          m_restore(b.m_restore) {
+      b.Remove();
+    }
+    Bookmark &operator=(Bookmark &&) = delete;
+    Bookmark &operator=(const Bookmark &) = delete;
+
+    void Remove() { m_restore = false; }
+    size_t GetSavedPosition() { return m_position_value; }
+    ~Bookmark() {
+      if (m_restore) {
+        m_position = m_position_value;
+      }
+    }
+
+  private:
+    size_t &m_position;
+    size_t m_position_value;
+    bool m_restore = true;
+  };
+
+  bool HasMoreTokens();
+  void Advance();
+  void TakeBack();
+  bool ConsumeToken(clang::tok::TokenKind kind);
+  template <typename... Ts> bool ConsumeToken(Ts... kinds);
+  Bookmark SetBookmark();
+  size_t GetCurrentPosition();
+  clang::Token &Peek();
+  bool ConsumeBrackets(clang::tok::TokenKind left, clang::tok::TokenKind right);
+
+  llvm::Optional<ParsedFunction> ParseFunctionImpl(bool expect_return_type);
+
+  // Parses functions returning function pointers 'string (*f(int x))(float y)'
+  llvm::Optional<ParsedFunction> ParseFuncPtr(bool expect_return_type);
+
+  // Consumes function arguments enclosed within '(' ... ')'
+  bool ConsumeArguments();
+
+  // Consumes template arguments enclosed within '<' ... '>'
+  bool ConsumeTemplateArgs();
+
+  // Consumes '(anonymous namespace)'
+  bool ConsumeAnonymousNamespace();
+
+  // Consumes operator declaration like 'operator *' or 'operator delete []'
+  bool ConsumeOperator();
+
+  // Skips 'const' and 'volatile'
+  void SkipTypeQualifiers();
+
+  // Skips 'const', 'volatile', '&', '&&' in the end of the function.
+  void SkipFunctionQualifiers();
+
+  // Consumes built-in types like 'int' or 'unsigned long long int'
+  bool ConsumeBuiltinType();
+
+  // Skips 'const' and 'volatile'
+  void SkipPtrsAndRefs();
+
+  // Consumes things like 'const * const &'
+  bool ConsumePtrsAndRefs();
+
+  // Consumes full type name like 'Namespace::Class<int>::Method()::InnerClass'
+  bool ConsumeTypename();
+
+  llvm::Optional<ParsedNameRanges> ParseFullNameImpl();
+  llvm::StringRef GetTextForRange(const Range &range);
+
+  // Populate m_tokens by calling clang lexer on m_text.
+  void ExtractTokens();
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CPlusPlusNameParser_h_

Modified: lldb/trunk/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp?rev=299374&r1=299373&r2=299374&view=diff
==============================================================================
--- lldb/trunk/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp (original)
+++ lldb/trunk/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp Mon Apr  3 13:59:34 2017
@@ -6,35 +6,139 @@
 // License. See LICENSE.TXT for details.
 //
 //===----------------------------------------------------------------------===//
-
 #include "gtest/gtest.h"
 
 #include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
 
 using namespace lldb_private;
 
-TEST(CPlusPlusLanguage, MethodName) {
+TEST(CPlusPlusLanguage, MethodNameParsing) {
   struct TestCase {
     std::string input;
     std::string context, basename, arguments, qualifiers, scope_qualified_name;
   };
 
   TestCase test_cases[] = {
-      {"foo::bar(baz)", "foo", "bar", "(baz)", "", "foo::bar"},
+      {"main(int, char *[]) ", "", "main", "(int, char *[])", "", "main"},
+      {"foo::bar(baz) const", "foo", "bar", "(baz)", "const", "foo::bar"},
+      {"foo::~bar(baz)", "foo", "~bar", "(baz)", "", "foo::~bar"},
+      {"a::b::c::d(e,f)", "a::b::c", "d", "(e,f)", "", "a::b::c::d"},
+      {"void f(int)", "", "f", "(int)", "", "f"},
+
+      // Operators
       {"std::basic_ostream<char, std::char_traits<char> >& "
        "std::operator<<<std::char_traits<char> >"
        "(std::basic_ostream<char, std::char_traits<char> >&, char const*)",
        "std", "operator<<<std::char_traits<char> >",
        "(std::basic_ostream<char, std::char_traits<char> >&, char const*)", "",
-       "std::operator<<<std::char_traits<char> >"}};
+       "std::operator<<<std::char_traits<char> >"},
+      {"operator delete[](void*, clang::ASTContext const&, unsigned long)", "",
+       "operator delete[]", "(void*, clang::ASTContext const&, unsigned long)",
+       "", "operator delete[]"},
+      {"llvm::Optional<clang::PostInitializer>::operator bool() const",
+       "llvm::Optional<clang::PostInitializer>", "operator bool", "()", "const",
+       "llvm::Optional<clang::PostInitializer>::operator bool"},
+      {"(anonymous namespace)::FactManager::operator[](unsigned short)",
+       "(anonymous namespace)::FactManager", "operator[]", "(unsigned short)",
+       "", "(anonymous namespace)::FactManager::operator[]"},
+      {"const int& std::map<int, pair<short, int>>::operator[](short) const",
+       "std::map<int, pair<short, int>>", "operator[]", "(short)", "const",
+       "std::map<int, pair<short, int>>::operator[]"},
+      {"CompareInsn::operator()(llvm::StringRef, InsnMatchEntry const&)",
+       "CompareInsn", "operator()", "(llvm::StringRef, InsnMatchEntry const&)",
+       "", "CompareInsn::operator()"},
+      {"llvm::Optional<llvm::MCFixupKind>::operator*() const &",
+       "llvm::Optional<llvm::MCFixupKind>", "operator*", "()", "const &",
+       "llvm::Optional<llvm::MCFixupKind>::operator*"},
+      // Internal classes
+      {"operator<<(Cls, Cls)::Subclass::function()",
+       "operator<<(Cls, Cls)::Subclass", "function", "()", "",
+       "operator<<(Cls, Cls)::Subclass::function"},
+      {"SAEC::checkFunction(context&) const::CallBack::CallBack(int)",
+       "SAEC::checkFunction(context&) const::CallBack", "CallBack", "(int)", "",
+       "SAEC::checkFunction(context&) const::CallBack::CallBack"},
+      // Anonymous namespace
+      {"XX::(anonymous namespace)::anon_class::anon_func() const",
+       "XX::(anonymous namespace)::anon_class", "anon_func", "()", "const",
+       "XX::(anonymous namespace)::anon_class::anon_func"},
+
+      // Function pointers
+      {"string (*f(vector<int>&&))(float)", "", "f", "(vector<int>&&)", "",
+       "f"},
+      {"void (*&std::_Any_data::_M_access<void (*)()>())()", "std::_Any_data",
+       "_M_access<void (*)()>", "()", "",
+       "std::_Any_data::_M_access<void (*)()>"},
+      {"void (*(*(*(*(*(*(*(* const&func1(int))())())())())())())())()", "",
+       "func1", "(int)", "", "func1"},
+
+      // Templates
+      {"void llvm::PM<llvm::Module, llvm::AM<llvm::Module>>::"
+       "addPass<llvm::VP>(llvm::VP)",
+       "llvm::PM<llvm::Module, llvm::AM<llvm::Module>>", "addPass<llvm::VP>",
+       "(llvm::VP)", "",
+       "llvm::PM<llvm::Module, llvm::AM<llvm::Module>>::"
+       "addPass<llvm::VP>"},
+      {"void std::vector<Class, std::allocator<Class> >"
+       "::_M_emplace_back_aux<Class const&>(Class const&)",
+       "std::vector<Class, std::allocator<Class> >",
+       "_M_emplace_back_aux<Class const&>", "(Class const&)", "",
+       "std::vector<Class, std::allocator<Class> >::"
+       "_M_emplace_back_aux<Class const&>"},
+      {"unsigned long llvm::countTrailingOnes<unsigned int>"
+       "(unsigned int, llvm::ZeroBehavior)",
+       "llvm", "countTrailingOnes<unsigned int>",
+       "(unsigned int, llvm::ZeroBehavior)", "",
+       "llvm::countTrailingOnes<unsigned int>"},
+      {"std::enable_if<(10u)<(64), bool>::type llvm::isUInt<10u>(unsigned "
+       "long)",
+       "llvm", "isUInt<10u>", "(unsigned long)", "", "llvm::isUInt<10u>"},
+      {"f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>()", "",
+       "f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>", "()", "",
+       "f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>"}};
 
   for (const auto &test : test_cases) {
     CPlusPlusLanguage::MethodName method(ConstString(test.input));
-    EXPECT_TRUE(method.IsValid());
-    EXPECT_EQ(test.context, method.GetContext());
-    EXPECT_EQ(test.basename, method.GetBasename());
-    EXPECT_EQ(test.arguments, method.GetArguments());
-    EXPECT_EQ(test.qualifiers, method.GetQualifiers());
-    EXPECT_EQ(test.scope_qualified_name, method.GetScopeQualifiedName());
+    EXPECT_TRUE(method.IsValid()) << test.input;
+    if (method.IsValid()) {
+      EXPECT_EQ(test.context, method.GetContext().str());
+      EXPECT_EQ(test.basename, method.GetBasename().str());
+      EXPECT_EQ(test.arguments, method.GetArguments().str());
+      EXPECT_EQ(test.qualifiers, method.GetQualifiers().str());
+      EXPECT_EQ(test.scope_qualified_name, method.GetScopeQualifiedName());
+    }
   }
 }
+
+TEST(CPlusPlusLanguage, ExtractContextAndIdentifier) {
+  struct TestCase {
+    std::string input;
+    std::string context, basename;
+  };
+
+  TestCase test_cases[] = {
+      {"main", "", "main"},
+      {"foo01::bar", "foo01", "bar"},
+      {"foo::~bar", "foo", "~bar"},
+      {"std::vector<int>::push_back", "std::vector<int>", "push_back"},
+      {"operator<<(Cls, Cls)::Subclass::function",
+       "operator<<(Cls, Cls)::Subclass", "function"},
+      {"std::vector<Class, std::allocator<Class>>"
+       "::_M_emplace_back_aux<Class const&>",
+       "std::vector<Class, std::allocator<Class>>",
+       "_M_emplace_back_aux<Class const&>"}};
+
+  llvm::StringRef context, basename;
+  for (const auto &test : test_cases) {
+    EXPECT_TRUE(CPlusPlusLanguage::ExtractContextAndIdentifier(
+        test.input.c_str(), context, basename));
+    EXPECT_EQ(test.context, context.str());
+    EXPECT_EQ(test.basename, basename.str());
+  }
+
+  EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier("void", context,
+                                                              basename));
+  EXPECT_FALSE(
+      CPlusPlusLanguage::ExtractContextAndIdentifier("321", context, basename));
+  EXPECT_FALSE(
+      CPlusPlusLanguage::ExtractContextAndIdentifier("", context, basename));
+}




More information about the lldb-commits mailing list