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 @@ -117,6 +117,7 @@ void Advance(); void TakeBack(); bool ConsumeToken(clang::tok::TokenKind kind); + template bool ConsumeToken(Ts... kinds); Bookmark SetBookmark(); size_t GetCurrentPosition(); @@ -164,6 +165,16 @@ // Consumes full type name like 'Namespace::Class::Method()::InnerClass' bool ConsumeTypename(); + /// Consumes ABI tags enclosed within '[abi:' ... ']' + /// + /// Since there is no restriction on what the ABI tag + /// string may contain, this API supports parsing a small + /// set of special characters. + /// + /// The following regex describes the set of supported characters: + /// [A-Za-z,.\s\d]+ + bool ConsumeAbiTag(); + llvm::Optional ParseFullNameImpl(); llvm::StringRef GetTextForRange(const Range &range); 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 @@ -226,9 +226,15 @@ Advance(); break; case tok::l_square: - if (!ConsumeBrackets(tok::l_square, tok::r_square)) + // Handle templates tagged with an ABI tag. + // An example demangled/prettified version is: + // func[abi:tag1][abi:tag2](int) + if (ConsumeAbiTag()) + can_open_template = true; + else if (ConsumeBrackets(tok::l_square, tok::r_square)) + can_open_template = false; + else return false; - can_open_template = false; break; case tok::l_paren: if (!ConsumeArguments()) @@ -249,6 +255,34 @@ return true; } +bool CPlusPlusNameParser::ConsumeAbiTag() { + Bookmark start_position = SetBookmark(); + if (!ConsumeToken(tok::l_square)) { + return false; + } + + if (HasMoreTokens() && Peek().is(tok::raw_identifier) + && Peek().getRawIdentifier() == "abi") { + Advance(); + } else { + return false; + } + + if (!ConsumeToken(tok::colon)) + return false; + + // Consume the actual tag string (and allow some special characters) + while (ConsumeToken(tok::raw_identifier, tok::comma, + tok::period, tok::numeric_constant)) + ; + + if (!ConsumeToken(tok::r_square)) + return false; + + start_position.Remove(); + return true; +} + bool CPlusPlusNameParser::ConsumeAnonymousNamespace() { Bookmark start_position = SetBookmark(); if (!ConsumeToken(tok::l_paren)) { @@ -519,6 +553,19 @@ Advance(); state = State::AfterIdentifier; break; + case tok::l_square: // ABI tag + // Handles types or functions that were tagged + // with, e.g., + // [[gnu::abi_tag("tag1","tag2")]] func() + // and demangled/prettified into: + // func[abi:tag1][abi:tag2]() + if (state == State::AfterIdentifier) { + // Anything other than an ABI tag is invalid at this point. + if (!ConsumeAbiTag()) { + continue_parsing = false; + } + } + break; case tok::l_paren: { if (state == State::Beginning || state == State::AfterTwoColons) { // (anonymous namespace) diff --git a/lldb/test/API/functionalities/step-avoids-regexp/TestStepAvoidsRegexp.py b/lldb/test/API/functionalities/step-avoids-regexp/TestStepAvoidsRegexp.py --- a/lldb/test/API/functionalities/step-avoids-regexp/TestStepAvoidsRegexp.py +++ b/lldb/test/API/functionalities/step-avoids-regexp/TestStepAvoidsRegexp.py @@ -37,13 +37,10 @@ self.thread.StepInto() self.hit_correct_function("main") - @skipIfWindows - @expectedFailureAll(bugnumber="rdar://100645742") - def test_step_avoid_regex_abi_tagged_template(self): - """Tests stepping into an ABI tagged function that matches the avoid regex""" - self.build() - (_, _, self.thread, _) = lldbutil.run_to_source_breakpoint(self, "with_tag_template", lldb.SBFileSpec('main.cpp')) - # Try to step into ignore::with_tag_template self.thread.StepInto() self.hit_correct_function("main") + + # Step into with_tag_template_returns_ignore (outside 'ignore::' namespace) + self.thread.StepInto() + self.hit_correct_function("with_tag_template_returns_ignore") diff --git a/lldb/test/API/functionalities/step-avoids-regexp/main.cpp b/lldb/test/API/functionalities/step-avoids-regexp/main.cpp --- a/lldb/test/API/functionalities/step-avoids-regexp/main.cpp +++ b/lldb/test/API/functionalities/step-avoids-regexp/main.cpp @@ -1,4 +1,6 @@ namespace ignore { +struct Dummy {}; + template auto auto_ret(T x) { return 0; } [[gnu::abi_tag("test")]] int with_tag() { return 0; } template [[gnu::abi_tag("test")]] int with_tag_template() { @@ -8,9 +10,15 @@ template decltype(auto) decltype_auto_ret(T x) { return 0; } } // namespace ignore +template +[[gnu::abi_tag("test")]] ignore::Dummy with_tag_template_returns_ignore(T x) { + return {}; +} + int main() { auto v1 = ignore::auto_ret(5); auto v2 = ignore::with_tag(); auto v3 = ignore::decltype_auto_ret(5); auto v4 = ignore::with_tag_template(); + auto v5 = with_tag_template_returns_ignore(5); } 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 @@ -114,13 +114,55 @@ {"llvm::Optional::operator*() const volatile &&", "llvm::Optional", "operator*", "()", "const volatile &&", "llvm::Optional::operator*"}, + {"void foo>()", + "", "foo>", "()", "", "foo>"}, // auto return type {"auto std::test_return_auto() const", "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"}}; + "std::test_return_auto"}, + + // abi_tag on class + {"v1::v2::Dummy[abi:c1][abi:c2]> " + "v1::v2::Dummy[abi:c1][abi:c2]>" + "::method2>>(int, v1::v2::Dummy) const &&", + // Context + "v1::v2::Dummy[abi:c1][abi:c2]>", + // Basename + "method2>>", + // Args, qualifiers + "(int, v1::v2::Dummy)", "const &&", + // Full scope-qualified name without args + "v1::v2::Dummy[abi:c1][abi:c2]>" + "::method2>>"}, + + // abi_tag on free function and template argument + {"v1::v2::Dummy[abi:c1][abi:c2]> " + "v1::v2::with_tag_in_ns[abi:f1][abi:f2]>>(int, v1::v2::Dummy) const &&", + // Context + "v1::v2", + // Basename + "with_tag_in_ns[abi:f1][abi:f2]>>", + // Args, qualifiers + "(int, v1::v2::Dummy)", "const &&", + // Full scope-qualified name without args + "v1::v2::with_tag_in_ns[abi:f1][abi:f2]>>"}, + + // abi_tag with special characters + {"auto ns::with_tag_in_ns[abi:special tag,0.0][abi:special tag,1.0]>" + "(float) const &&", + // Context + "ns", + // Basename + "with_tag_in_ns[abi:special tag,0.0][abi:special tag,1.0]>", + // Args, qualifiers + "(float)", "const &&", + // Full scope-qualified name without args + "ns::with_tag_in_ns[abi:special tag,0.0][abi:special tag,1.0]>"}, + }; for (const auto &test : test_cases) { CPlusPlusLanguage::MethodName method(ConstString(test.input));