diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -55,7 +55,13 @@ llvm::StringRef GetArguments(); llvm::StringRef GetQualifiers(); - + + /// Returns the methods return-type. + /// + /// Currently returns an empty llvm::StringRef + /// if the return-type is a function pointer. + llvm::StringRef GetReturnType(); + bool ContainsPath(llvm::StringRef path); private: @@ -78,12 +84,13 @@ bool TrySimplifiedParse(); ConstString m_full; // Full name: - // "lldb::SBTarget::GetBreakpointAtIndex(unsigned int) - // const" - llvm::StringRef m_basename; // Basename: "GetBreakpointAtIndex" - llvm::StringRef m_context; // Decl context: "lldb::SBTarget" - llvm::StringRef m_arguments; // Arguments: "(unsigned int)" - llvm::StringRef m_qualifiers; // Qualifiers: "const" + // "size_t lldb::SBTarget::GetBreakpointAtIndex(unsigned + // int) const" + llvm::StringRef m_basename; // Basename: "GetBreakpointAtIndex" + llvm::StringRef m_context; // Decl context: "lldb::SBTarget" + llvm::StringRef m_arguments; // Arguments: "(unsigned int)" + llvm::StringRef m_qualifiers; // Qualifiers: "const" + llvm::StringRef m_return_type; // Return type: "size_t" bool m_parsed = false; bool m_parse_error = false; }; diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -109,6 +109,7 @@ m_context = llvm::StringRef(); m_arguments = llvm::StringRef(); m_qualifiers = llvm::StringRef(); + m_return_type = llvm::StringRef(); m_parsed = false; m_parse_error = false; } @@ -206,6 +207,7 @@ m_basename = llvm::StringRef(); m_arguments = llvm::StringRef(); m_qualifiers = llvm::StringRef(); + m_return_type = llvm::StringRef(); return false; } } @@ -223,6 +225,7 @@ m_context = function.value().name.context; m_arguments = function.value().arguments; m_qualifiers = function.value().qualifiers; + m_return_type = function.value().return_type; m_parse_error = false; } else { m_parse_error = true; @@ -256,6 +259,12 @@ return m_qualifiers; } +llvm::StringRef CPlusPlusLanguage::MethodName::GetReturnType() { + if (!m_parsed) + Parse(); + return m_return_type; +} + std::string CPlusPlusLanguage::MethodName::GetScopeQualifiedName() { if (!m_parsed) Parse(); diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h @@ -33,6 +33,7 @@ ParsedName name; llvm::StringRef arguments; llvm::StringRef qualifiers; + llvm::StringRef return_type; }; // Treats given text as a function definition and parses it. diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp @@ -105,10 +105,16 @@ Optional CPlusPlusNameParser::ParseFunctionImpl(bool expect_return_type) { Bookmark start_position = SetBookmark(); + + ParsedFunction result; if (expect_return_type) { + size_t return_start = GetCurrentPosition(); // Consume return type if it's expected. if (!ConsumeToken(tok::kw_auto) && !ConsumeTypename()) return None; + + size_t return_end = GetCurrentPosition(); + result.return_type = GetTextForRange(Range(return_start, return_end)); } auto maybe_name = ParseFullNameImpl(); @@ -125,7 +131,6 @@ SkipFunctionQualifiers(); size_t end_position = GetCurrentPosition(); - ParsedFunction result; result.name.basename = GetTextForRange(maybe_name.value().basename_range); result.name.context = GetTextForRange(maybe_name.value().context_range); result.arguments = GetTextForRange(Range(argument_start, qualifiers_start)); @@ -136,6 +141,16 @@ Optional CPlusPlusNameParser::ParseFuncPtr(bool expect_return_type) { + // This function parses a function definition + // that returns a pointer type. + // E.g., double (*(*func(long))(int))(float) + + // Step 1: + // Remove the return type of the innermost + // function pointer type. + // + // Leaves us with: + // (*(*func(long))(int))(float) Bookmark start_position = SetBookmark(); if (expect_return_type) { // Consume return type. @@ -143,11 +158,22 @@ return None; } + // Step 2: + // + // Skip a pointer and parenthesis pair. + // + // Leaves us with: + // (*func(long))(int))(float) if (!ConsumeToken(tok::l_paren)) return None; if (!ConsumePtrsAndRefs()) return None; + // Step 3: + // + // Consume inner function name. This will fail unless + // we stripped all the pointers on the left hand side + // of the funciton name. { Bookmark before_inner_function_pos = SetBookmark(); auto maybe_inner_function_name = ParseFunctionImpl(false); @@ -161,6 +187,24 @@ } } + // Step 4: + // + // Parse the remaining string as a function pointer again. + // This time don't consume the inner-most typename since + // we're left with pointers only. This will strip another + // layer of pointers until we're left with the innermost + // function name/argument. I.e., func(long))(int))(float) + // + // Once we successfully stripped all pointers and gotten + // the innermost function name from ParseFunctionImpl above, + // we consume a single ')' and the arguments '(...)' that follows. + // + // Leaves us with: + // )(float) + // + // This is the remnant of the outer function pointers' arguments. + // Unwinding the recursive calls will remove the remaining + // arguments. auto maybe_inner_function_ptr_name = ParseFuncPtr(false); if (maybe_inner_function_ptr_name) if (ConsumeToken(tok::r_paren)) @@ -169,6 +213,7 @@ start_position.Remove(); return maybe_inner_function_ptr_name; } + return None; } diff --git a/lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp b/lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp --- a/lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp +++ b/lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp @@ -17,130 +17,136 @@ TEST(CPlusPlusLanguage, MethodNameParsing) { struct TestCase { std::string input; - std::string context, basename, arguments, qualifiers, scope_qualified_name; + std::string return_type, context, basename, arguments, qualifiers, + scope_qualified_name; }; TestCase test_cases[] = { - {"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"}, + {"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)", "void", "", "f", "(int)", "", "f"}, // Operators {"std::basic_ostream >& " - "std::operator<< >" - "(std::basic_ostream >&, char const*)", - "std", "operator<< >", + "std::operator<< >(std::basic_ostream >&, char const*)", + "std::basic_ostream >&", "std", + "operator<< >", "(std::basic_ostream >&, char const*)", "", "std::operator<< >"}, {"operator delete[](void*, clang::ASTContext const&, unsigned long)", "", - "operator delete[]", "(void*, clang::ASTContext const&, unsigned long)", - "", "operator delete[]"}, - {"llvm::Optional::operator bool() const", + "", "operator delete[]", + "(void*, clang::ASTContext const&, unsigned long)", "", + "operator delete[]"}, + {"llvm::Optional::operator bool() const", "", "llvm::Optional", "operator bool", "()", "const", "llvm::Optional::operator bool"}, - {"(anonymous namespace)::FactManager::operator[](unsigned short)", + {"(anonymous namespace)::FactManager::operator[](unsigned short)", "", "(anonymous namespace)::FactManager", "operator[]", "(unsigned short)", "", "(anonymous namespace)::FactManager::operator[]"}, {"const int& std::map>::operator[](short) const", - "std::map>", "operator[]", "(short)", "const", - "std::map>::operator[]"}, - {"CompareInsn::operator()(llvm::StringRef, InsnMatchEntry const&)", + "const int&", "std::map>", "operator[]", "(short)", + "const", "std::map>::operator[]"}, + {"CompareInsn::operator()(llvm::StringRef, InsnMatchEntry const&)", "", "CompareInsn", "operator()", "(llvm::StringRef, InsnMatchEntry const&)", "", "CompareInsn::operator()"}, - {"llvm::Optional::operator*() const &", + {"llvm::Optional::operator*() const &", "", "llvm::Optional", "operator*", "()", "const &", "llvm::Optional::operator*"}, {"auto std::__1::ranges::__begin::__fn::operator()[abi:v160000](char const (&) [18ul]) const", - "std::__1::ranges::__begin::__fn", + "auto", "std::__1::ranges::__begin::__fn", "operator()[abi:v160000]", "(char const (&) [18ul])", "const", "std::__1::ranges::__begin::__fn::operator()[abi:v160000]"}, // Internal classes - {"operator<<(Cls, Cls)::Subclass::function()", + {"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", "(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", "()", "const", "XX::(anonymous namespace)::anon_class::anon_func"}, // Lambda {"main::{lambda()#1}::operator()() const::{lambda()#1}::operator()() " "const", - "main::{lambda()#1}::operator()() const::{lambda()#1}", "operator()", + "", "main::{lambda()#1}::operator()() const::{lambda()#1}", "operator()", "()", "const", "main::{lambda()#1}::operator()() const::{lambda()#1}::operator()"}, // Function pointers - {"string (*f(vector&&))(float)", "", "f", "(vector&&)", "", - "f"}, - {"void (*&std::_Any_data::_M_access())()", "std::_Any_data", - "_M_access", "()", "", + {"string (*f(vector&&))(float)", "", "", "f", + "(vector&&)", "", "f"}, + {"void (*&std::_Any_data::_M_access())()", "", + "std::_Any_data", "_M_access", "()", "", "std::_Any_data::_M_access"}, {"void (*(*(*(*(*(*(*(* const&func1(int))())())())())())())())()", "", - "func1", "(int)", "", "func1"}, + "", "func1", "(int)", "", "func1"}, // Decltype {"decltype(nullptr)&& std::forward" "(std::remove_reference::type&)", - "std", "forward", + "decltype(nullptr)&&", "std", "forward", "(std::remove_reference::type&)", "", "std::forward"}, // Templates {"void llvm::PM>::" "addPass(llvm::VP)", - "llvm::PM>", "addPass", - "(llvm::VP)", "", + "void", "llvm::PM>", + "addPass", "(llvm::VP)", "", "llvm::PM>::" "addPass"}, {"void std::vector >" "::_M_emplace_back_aux(Class const&)", - "std::vector >", + "void", "std::vector >", "_M_emplace_back_aux", "(Class const&)", "", "std::vector >::" "_M_emplace_back_aux"}, {"unsigned long llvm::countTrailingOnes" "(unsigned int, llvm::ZeroBehavior)", - "llvm", "countTrailingOnes", + "unsigned long", "llvm", "countTrailingOnes", "(unsigned int, llvm::ZeroBehavior)", "", "llvm::countTrailingOnes"}, {"std::enable_if<(10u)<(64), bool>::type llvm::isUInt<10u>(unsigned " "long)", - "llvm", "isUInt<10u>", "(unsigned long)", "", "llvm::isUInt<10u>"}, - {"f, sizeof(B)()", "", + "std::enable_if<(10u)<(64), bool>::type", "llvm", "isUInt<10u>", + "(unsigned long)", "", "llvm::isUInt<10u>"}, + {"f, sizeof(B)()", "", "", "f, sizeof(B)", "()", "", "f, sizeof(B)"}, - {"llvm::Optional::operator*() const volatile &&", + {"llvm::Optional::operator*() const volatile &&", "", "llvm::Optional", "operator*", "()", "const volatile &&", "llvm::Optional::operator*"}, - {"void foo>()", "", "foo>", "()", "", - "foo>"}, - {"void foo[10]>>()", "", "foo[10]>>", "()", "", - "foo[10]>>"}, - {"void foo()", "", "foo", "()", "", + {"void foo>()", "void", "", "foo>", + "()", "", "foo>"}, + {"void foo[10]>>()", "void", "", "foo[10]>>", + "()", "", "foo[10]>>"}, + {"void foo()", "void", "", "foo", "()", "", "foo"}, - {"void foo()", "", "foo", "()", "", - "foo"}, + {"void foo()", "void", "", "foo", "()", "", "foo"}, // auto return type - {"auto std::test_return_auto() const", "std", + {"auto std::test_return_auto() const", "auto", "std", "test_return_auto", "()", "const", "std::test_return_auto"}, - {"decltype(auto) std::test_return_auto(int) const", "std", - "test_return_auto", "(int)", "const", "std::test_return_auto"}, + {"decltype(auto) std::test_return_auto(int) const", "decltype(auto)", + "std", "test_return_auto", "(int)", "const", + "std::test_return_auto"}, // abi_tag on class method {"v1::v2::Dummy[abi:c1][abi:c2]> " "v1::v2::Dummy[abi:c1][abi:c2]>" "::method2>>(int, v1::v2::Dummy) const &&", + // Return type + "v1::v2::Dummy[abi:c1][abi:c2]>", // Context "v1::v2::Dummy[abi:c1][abi:c2]>", // Basename @@ -158,6 +164,8 @@ "v1::v2::with_tag_in_ns[abi:f1][abi:f2]>>(int, v1::v2::Dummy) const " "&&", + // Return type + "v1::v2::Dummy[abi:c1][abi:c2]>", // Context "v1::v2", // Basename @@ -173,6 +181,8 @@ {"auto ns::with_tag_in_ns[abi:special tag,0.0][abi:special " "tag,1.0]>" "(float) const &&", + // Return type + "auto", // Context "ns", // Basename @@ -184,15 +194,15 @@ "tag,1.0]>"}, // abi_tag on operator overloads - {"std::__1::error_code::operator bool[abi:v160000]() const", + {"std::__1::error_code::operator bool[abi:v160000]() const", "", "std::__1::error_code", "operator bool[abi:v160000]", "()", "const", "std::__1::error_code::operator bool[abi:v160000]"}, - {"auto ns::foo::operator[][abi:v160000](size_t) const", "ns::foo", + {"auto ns::foo::operator[][abi:v160000](size_t) const", "auto", "ns::foo", "operator[][abi:v160000]", "(size_t)", "const", "ns::foo::operator[][abi:v160000]"}, - {"auto Foo[abi:abc]::operator<<>(int) &", + {"auto Foo[abi:abc]::operator<<>(int) &", "auto", "Foo[abi:abc]", "operator<<>", "(int)", "&", "Foo[abi:abc]::operator<<>"}}; @@ -200,6 +210,7 @@ CPlusPlusLanguage::MethodName method(ConstString(test.input)); EXPECT_TRUE(method.IsValid()) << test.input; if (method.IsValid()) { + EXPECT_EQ(test.return_type, method.GetReturnType().str()); EXPECT_EQ(test.context, method.GetContext().str()); EXPECT_EQ(test.basename, method.GetBasename().str()); EXPECT_EQ(test.arguments, method.GetArguments().str());