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]<type[abi:tag3]>(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())
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)) {
Advance();
state = State::AfterIdentifier;
break;
+ case tok::l_square: {
+ // 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]()
+
+ // ABI tags only appear after a method or type name
+ const bool valid_state =
+ state == State::AfterIdentifier || state == State::AfterOperator;
+ if (!valid_state || !ConsumeAbiTag()) {
+ continue_parsing = false;
+ }
+
+ break;
+ }
case tok::l_paren: {
if (state == State::Beginning || state == State::AfterTwoColons) {
// (anonymous namespace)
{"llvm::Optional<llvm::MCFixupKind>::operator*() const volatile &&",
"llvm::Optional<llvm::MCFixupKind>", "operator*", "()",
"const volatile &&", "llvm::Optional<llvm::MCFixupKind>::operator*"},
+ {"void foo<Dummy<char [10]>>()", "", "foo<Dummy<char [10]>>", "()", "",
+ "foo<Dummy<char [10]>>"},
// auto return type
{"auto std::test_return_auto<int>() const", "std",
"test_return_auto<int>", "()", "const", "std::test_return_auto<int>"},
{"decltype(auto) std::test_return_auto<int>(int) const", "std",
- "test_return_auto<int>", "(int)", "const",
- "std::test_return_auto<int>"}};
+ "test_return_auto<int>", "(int)", "const", "std::test_return_auto<int>"},
+
+ // abi_tag on class method
+ {"v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>> "
+ "v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>>"
+ "::method2<v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<"
+ "int>>>(int, v1::v2::Dummy<int>) const &&",
+ // Context
+ "v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>>",
+ // Basename
+ "method2<v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<"
+ "int>>>",
+ // Args, qualifiers
+ "(int, v1::v2::Dummy<int>)", "const &&",
+ // Full scope-qualified name without args
+ "v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>>"
+ "::method2<v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<"
+ "int>>>"},
+
+ // abi_tag on free function and template argument
+ {"v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>> "
+ "v1::v2::with_tag_in_ns[abi:f1][abi:f2]<v1::v2::Dummy[abi:c1][abi:c2]"
+ "<v1::v2::Dummy[abi:c1][abi:c2]<int>>>(int, v1::v2::Dummy<int>) const "
+ "&&",
+ // Context
+ "v1::v2",
+ // Basename
+ "with_tag_in_ns[abi:f1][abi:f2]<v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::"
+ "Dummy[abi:c1][abi:c2]<int>>>",
+ // Args, qualifiers
+ "(int, v1::v2::Dummy<int>)", "const &&",
+ // Full scope-qualified name without args
+ "v1::v2::with_tag_in_ns[abi:f1][abi:f2]<v1::v2::Dummy[abi:c1][abi:c2]<"
+ "v1::v2::Dummy[abi:c1][abi:c2]<int>>>"},
+
+ // abi_tag with special characters
+ {"auto ns::with_tag_in_ns[abi:special tag,0.0][abi:special "
+ "tag,1.0]<Dummy<int>>"
+ "(float) const &&",
+ // Context
+ "ns",
+ // Basename
+ "with_tag_in_ns[abi:special tag,0.0][abi:special tag,1.0]<Dummy<int>>",
+ // 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]<Dummy<int>>"},
+
+ // 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]"},
+
+ {"auto ns::foo::operator[][abi:v160000](size_t) const", "ns::foo",
+ "operator[][abi:v160000]", "(size_t)", "const",
+ "ns::foo::operator[][abi:v160000]"},
+
+ {"auto Foo[abi:abc]<int>::operator<<<Foo[abi:abc]<int>>(int) &",
+ "Foo[abi:abc]<int>", "operator<<<Foo[abi:abc]<int>>", "(int)", "&",
+ "Foo[abi:abc]<int>::operator<<<Foo[abi:abc]<int>>"}};
for (const auto &test : test_cases) {
CPlusPlusLanguage::MethodName method(ConstString(test.input));
}
}
+TEST(CPlusPlusLanguage, InvalidMethodNameParsing) {
+ // Tests that we correctly reject malformed function names
+
+ std::string test_cases[] = {
+ "int Foo::operator[]<[10>()",
+ "Foo::operator bool[10]()",
+ "auto A::operator<=>[abi:tag]<A::B>()",
+ "auto A::operator<<<(int)",
+ "auto A::operator>>>(int)",
+ "auto A::operator<<<Type[abi:tag]<>(int)",
+ "auto A::operator<<<Type[abi:tag]<Type<int>>(int)",
+ "auto A::foo[(int)",
+ "auto A::foo[](int)",
+ "auto A::foo[bar](int)",
+ "auto A::foo[abi](int)",
+ "auto A::foo[abi:(int)",
+ };
+
+ for (const auto &name : test_cases) {
+ CPlusPlusLanguage::MethodName method{ConstString(name)};
+ EXPECT_FALSE(method.IsValid()) << name;
+ }
+}
+
TEST(CPlusPlusLanguage, ContainsPath) {
CPlusPlusLanguage::MethodName
reference_1(ConstString("int foo::bar::func01(int a, double b)"));