//===- unittest/Format/TokenAnnotatorTest.cpp - Formatting unit tests -----===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "clang/Format/Format.h" #include "FormatTestUtils.h" #include "TestLexer.h" #include "gtest/gtest.h" namespace clang { namespace format { // Not really the equality, but everything we need. static bool operator==(const FormatToken &LHS, const FormatToken &RHS) noexcept { return LHS.Tok.getKind() == RHS.Tok.getKind() && LHS.getType() == RHS.getType(); } namespace { class TokenAnnotatorTest : public testing::Test { protected: TokenList annotate(StringRef Code, const FormatStyle &Style = getLLVMStyle()) { return TestLexer(Allocator, Buffers, Style).annotate(Code); } llvm::SpecificBumpPtrAllocator Allocator; std::vector> Buffers; }; #define EXPECT_TOKEN_KIND(FormatTok, Kind) \ EXPECT_EQ((FormatTok)->Tok.getKind(), Kind) << *(FormatTok) #define EXPECT_TOKEN_TYPE(FormatTok, Type) \ EXPECT_EQ((FormatTok)->getType(), Type) << *(FormatTok) #define EXPECT_TOKEN_PRECEDENCE(FormatTok, Prec) \ EXPECT_EQ((FormatTok)->getPrecedence(), Prec) << *(FormatTok) #define EXPECT_BRACE_KIND(FormatTok, Kind) \ EXPECT_EQ(FormatTok->getBlockKind(), Kind) << *(FormatTok) #define EXPECT_TOKEN(FormatTok, Kind, Type) \ do { \ EXPECT_TOKEN_KIND(FormatTok, Kind); \ EXPECT_TOKEN_TYPE(FormatTok, Type); \ } while (false) TEST_F(TokenAnnotatorTest, UnderstandsUsesOfStarAndAmp) { auto Tokens = annotate("auto x = [](const decltype(x) &ptr) {};"); ASSERT_EQ(Tokens.size(), 18u) << Tokens; EXPECT_TOKEN(Tokens[7], tok::kw_decltype, TT_Unknown); EXPECT_TOKEN(Tokens[8], tok::l_paren, TT_TypeDeclarationParen); EXPECT_TOKEN(Tokens[9], tok::identifier, TT_Unknown); EXPECT_TOKEN(Tokens[10], tok::r_paren, TT_TypeDeclarationParen); EXPECT_TOKEN(Tokens[11], tok::amp, TT_PointerOrReference); Tokens = annotate("auto x = [](const decltype(x) *ptr) {};"); ASSERT_EQ(Tokens.size(), 18u) << Tokens; EXPECT_TOKEN(Tokens[10], tok::r_paren, TT_TypeDeclarationParen); EXPECT_TOKEN(Tokens[11], tok::star, TT_PointerOrReference); Tokens = annotate("#define lambda [](const decltype(x) &ptr) {}"); ASSERT_EQ(Tokens.size(), 17u) << Tokens; EXPECT_TOKEN(Tokens[7], tok::kw_decltype, TT_Unknown); EXPECT_TOKEN(Tokens[8], tok::l_paren, TT_TypeDeclarationParen); EXPECT_TOKEN(Tokens[9], tok::identifier, TT_Unknown); EXPECT_TOKEN(Tokens[10], tok::r_paren, TT_TypeDeclarationParen); EXPECT_TOKEN(Tokens[11], tok::amp, TT_PointerOrReference); Tokens = annotate("#define lambda [](const decltype(x) *ptr) {}"); ASSERT_EQ(Tokens.size(), 17u) << Tokens; EXPECT_TOKEN(Tokens[10], tok::r_paren, TT_TypeDeclarationParen); EXPECT_TOKEN(Tokens[11], tok::star, TT_PointerOrReference); Tokens = annotate("void f() {\n" " while (p < a && *p == 'a')\n" " p++;\n" "}"); ASSERT_EQ(Tokens.size(), 21u) << Tokens; EXPECT_TOKEN(Tokens[10], tok::ampamp, TT_BinaryOperator); EXPECT_TOKEN(Tokens[11], tok::star, TT_UnaryOperator); Tokens = annotate("case *x:"); ASSERT_EQ(Tokens.size(), 5u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::star, TT_UnaryOperator); Tokens = annotate("case &x:"); ASSERT_EQ(Tokens.size(), 5u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::amp, TT_UnaryOperator); Tokens = annotate("bool b = 3 == int{3} && true;"); ASSERT_EQ(Tokens.size(), 13u) << Tokens; EXPECT_TOKEN(Tokens[9], tok::ampamp, TT_BinaryOperator); Tokens = annotate("struct {\n" "} *ptr;"); ASSERT_EQ(Tokens.size(), 7u) << Tokens; EXPECT_TOKEN(Tokens[3], tok::star, TT_PointerOrReference); Tokens = annotate("union {\n" "} *ptr;"); ASSERT_EQ(Tokens.size(), 7u) << Tokens; EXPECT_TOKEN(Tokens[3], tok::star, TT_PointerOrReference); Tokens = annotate("class {\n" "} *ptr;"); ASSERT_EQ(Tokens.size(), 7u) << Tokens; EXPECT_TOKEN(Tokens[3], tok::star, TT_PointerOrReference); Tokens = annotate("struct {\n" "} &&ptr = {};"); ASSERT_EQ(Tokens.size(), 10u) << Tokens; EXPECT_TOKEN(Tokens[3], tok::ampamp, TT_PointerOrReference); Tokens = annotate("union {\n" "} &&ptr = {};"); ASSERT_EQ(Tokens.size(), 10u) << Tokens; EXPECT_TOKEN(Tokens[3], tok::ampamp, TT_PointerOrReference); Tokens = annotate("class {\n" "} &&ptr = {};"); ASSERT_EQ(Tokens.size(), 10u) << Tokens; EXPECT_TOKEN(Tokens[3], tok::ampamp, TT_PointerOrReference); Tokens = annotate("int i = int{42} * 2;"); ASSERT_EQ(Tokens.size(), 11u) << Tokens; EXPECT_TOKEN(Tokens[7], tok::star, TT_BinaryOperator); Tokens = annotate("delete[] *ptr;"); ASSERT_EQ(Tokens.size(), 7u) << Tokens; EXPECT_TOKEN(Tokens[3], tok::star, TT_UnaryOperator); Tokens = annotate("delete[] **ptr;"); ASSERT_EQ(Tokens.size(), 8u) << Tokens; EXPECT_TOKEN(Tokens[3], tok::star, TT_UnaryOperator); EXPECT_TOKEN(Tokens[4], tok::star, TT_UnaryOperator); Tokens = annotate("delete[] *(ptr);"); ASSERT_EQ(Tokens.size(), 9u) << Tokens; EXPECT_TOKEN(Tokens[3], tok::star, TT_UnaryOperator); Tokens = annotate("void f() { void (*fnptr)(char* foo); }"); ASSERT_EQ(Tokens.size(), 18u) << Tokens; EXPECT_TOKEN(Tokens[6], tok::l_paren, TT_FunctionTypeLParen); // FIXME: The star of a function pointer probably makes more sense as // TT_PointerOrReference. EXPECT_TOKEN(Tokens[7], tok::star, TT_UnaryOperator); EXPECT_TOKEN(Tokens[12], tok::star, TT_PointerOrReference); Tokens = annotate("void f() { void (*fnptr)(t* foo); }"); ASSERT_EQ(Tokens.size(), 18u) << Tokens; EXPECT_TOKEN(Tokens[6], tok::l_paren, TT_FunctionTypeLParen); EXPECT_TOKEN(Tokens[7], tok::star, TT_UnaryOperator); EXPECT_TOKEN(Tokens[12], tok::star, TT_PointerOrReference); Tokens = annotate("int f3() { return sizeof(Foo&); }"); ASSERT_EQ(Tokens.size(), 14u) << Tokens; EXPECT_TOKEN(Tokens[9], tok::amp, TT_PointerOrReference); Tokens = annotate("int f4() { return sizeof(Foo&&); }"); ASSERT_EQ(Tokens.size(), 14u) << Tokens; EXPECT_TOKEN(Tokens[9], tok::ampamp, TT_PointerOrReference); Tokens = annotate("void f5() { int f6(Foo&, Bar&); }"); ASSERT_EQ(Tokens.size(), 17u) << Tokens; EXPECT_TOKEN(Tokens[9], tok::amp, TT_PointerOrReference); EXPECT_TOKEN(Tokens[12], tok::amp, TT_PointerOrReference); Tokens = annotate("void f7() { int f8(Foo&&, Bar&&); }"); ASSERT_EQ(Tokens.size(), 17u) << Tokens; EXPECT_TOKEN(Tokens[9], tok::ampamp, TT_PointerOrReference); EXPECT_TOKEN(Tokens[12], tok::ampamp, TT_PointerOrReference); Tokens = annotate("Type1 &val1 = val2;"); ASSERT_EQ(Tokens.size(), 7u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::amp, TT_PointerOrReference); Tokens = annotate("Type1 *val1 = &val2;"); ASSERT_EQ(Tokens.size(), 8u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::star, TT_PointerOrReference); EXPECT_TOKEN(Tokens[4], tok::amp, TT_UnaryOperator); Tokens = annotate("val1 & val2;"); ASSERT_EQ(Tokens.size(), 5u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::amp, TT_BinaryOperator); Tokens = annotate("val1 & val2.member;"); ASSERT_EQ(Tokens.size(), 7u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::amp, TT_BinaryOperator); Tokens = annotate("val1 & val2.*member;"); ASSERT_EQ(Tokens.size(), 7u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::amp, TT_BinaryOperator); Tokens = annotate("val1.*member & val2;"); ASSERT_EQ(Tokens.size(), 7u) << Tokens; EXPECT_TOKEN(Tokens[3], tok::amp, TT_BinaryOperator); Tokens = annotate("val1 & val2->*member;"); ASSERT_EQ(Tokens.size(), 7u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::amp, TT_BinaryOperator); Tokens = annotate("val1->member & val2;"); ASSERT_EQ(Tokens.size(), 7u) << Tokens; EXPECT_TOKEN(Tokens[3], tok::amp, TT_BinaryOperator); Tokens = annotate("val1 & val2 & val3;"); ASSERT_EQ(Tokens.size(), 7u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::amp, TT_BinaryOperator); EXPECT_TOKEN(Tokens[3], tok::amp, TT_BinaryOperator); Tokens = annotate("val1 & val2 // comment\n" " & val3;"); ASSERT_EQ(Tokens.size(), 8u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::amp, TT_BinaryOperator); EXPECT_TOKEN(Tokens[4], tok::amp, TT_BinaryOperator); Tokens = annotate("val1 & val2.member & val3.member() & val4 & val5->member;"); ASSERT_EQ(Tokens.size(), 19u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::amp, TT_BinaryOperator); EXPECT_TOKEN(Tokens[5], tok::amp, TT_BinaryOperator); EXPECT_TOKEN(Tokens[11], tok::amp, TT_BinaryOperator); EXPECT_TOKEN(Tokens[13], tok::amp, TT_BinaryOperator); Tokens = annotate("class c {\n" " void func(type &a) { a & member; }\n" " anotherType &member;\n" "}"); ASSERT_EQ(Tokens.size(), 22u) << Tokens; EXPECT_TOKEN(Tokens[7], tok::amp, TT_PointerOrReference); EXPECT_TOKEN(Tokens[12], tok::amp, TT_BinaryOperator); EXPECT_TOKEN(Tokens[17], tok::amp, TT_PointerOrReference); Tokens = annotate("struct S {\n" " auto Mem = C & D;\n" "}"); ASSERT_EQ(Tokens.size(), 12u) << Tokens; EXPECT_TOKEN(Tokens[7], tok::amp, TT_BinaryOperator); Tokens = annotate("template void swap() noexcept(Bar && Foo);"); ASSERT_EQ(Tokens.size(), 23u) << Tokens; EXPECT_TOKEN(Tokens[15], tok::ampamp, TT_BinaryOperator); Tokens = annotate("template struct S {\n" " explicit(Bar && Foo) S(const S &);\n" "};"); ASSERT_EQ(Tokens.size(), 30u) << Tokens; EXPECT_TOKEN(Tokens[14], tok::ampamp, TT_BinaryOperator); Tokens = annotate("template struct S {};"); ASSERT_EQ(Tokens.size(), 15u) << Tokens; EXPECT_TOKEN(Tokens[6], tok::ampamp, TT_BinaryOperator); Tokens = annotate("template struct S {};"); ASSERT_EQ(Tokens.size(), 18u) << Tokens; EXPECT_TOKEN(Tokens[9], tok::ampamp, TT_BinaryOperator); Tokens = annotate("template struct S {};"); ASSERT_EQ(Tokens.size(), 17u) << Tokens; EXPECT_TOKEN(Tokens[9], tok::ampamp, TT_PointerOrReference); Tokens = annotate("template struct S {};"); ASSERT_EQ(Tokens.size(), 19u) << Tokens; EXPECT_TOKEN(Tokens[6], tok::l_paren, TT_FunctionTypeLParen); EXPECT_TOKEN(Tokens[7], tok::star, TT_PointerOrReference); Tokens = annotate("Foo a = {};"); ASSERT_EQ(Tokens.size(), 12u) << Tokens; EXPECT_TOKEN(Tokens[3], tok::ampamp, TT_BinaryOperator); Tokens = annotate("Foo a = {};"); ASSERT_EQ(Tokens.size(), 11u) << Tokens; EXPECT_TOKEN(Tokens[3], tok::ampamp, TT_PointerOrReference); Tokens = annotate("template \n" "enable_if_t, bool> // comment\n" "operator~(T &a);"); ASSERT_EQ(Tokens.size(), 24u) << Tokens; EXPECT_TOKEN(Tokens[19], tok::amp, TT_PointerOrReference); Tokens = annotate("template * = nullptr> void f();"); ASSERT_EQ(Tokens.size(), 19u) << Tokens; EXPECT_TOKEN(Tokens[5], tok::ampamp, TT_BinaryOperator); Tokens = annotate("auto foo() noexcept(noexcept(bar()) && " "trait> && noexcept(baz())) {}"); ASSERT_EQ(Tokens.size(), 38u) << Tokens; EXPECT_TOKEN(Tokens[12], tok::ampamp, TT_BinaryOperator); EXPECT_TOKEN(Tokens[27], tok::ampamp, TT_BinaryOperator); Tokens = annotate("foo = *i < *j && *j > *k;"); ASSERT_EQ(Tokens.size(), 15u) << Tokens; EXPECT_TOKEN(Tokens[4], tok::less, TT_BinaryOperator); EXPECT_TOKEN(Tokens[7], tok::ampamp, TT_BinaryOperator); EXPECT_TOKEN(Tokens[10], tok::greater, TT_BinaryOperator); FormatStyle Style = getLLVMStyle(); Style.TypeNames.push_back("MYI"); Tokens = annotate("if (MYI *p{nullptr})", Style); ASSERT_EQ(Tokens.size(), 10u) << Tokens; EXPECT_TOKEN(Tokens[2], tok::identifier, TT_TypeName); EXPECT_TOKEN(Tokens[3], tok::star, TT_PointerOrReference); Style.TypeNames.push_back("Class"); Tokens = annotate("if (Class *obj {getObj()})", Style); ASSERT_EQ(Tokens.size(), 12u) << Tokens; EXPECT_TOKEN(Tokens[2], tok::identifier, TT_TypeName); EXPECT_TOKEN(Tokens[3], tok::star, TT_PointerOrReference); Tokens = annotate("class Foo {\n" " void operator<() {}\n" " Foo &f;\n" "};"); ASSERT_EQ(Tokens.size(), 17u) << Tokens; EXPECT_TOKEN(Tokens[4], tok::kw_operator, TT_FunctionDeclarationName); EXPECT_TOKEN(Tokens[5], tok::less, TT_OverloadedOperator); EXPECT_TOKEN(Tokens[6], tok::l_paren, TT_OverloadedOperatorLParen); EXPECT_TOKEN(Tokens[8], tok::l_brace, TT_FunctionLBrace); EXPECT_TOKEN(Tokens[11], tok::amp, TT_PointerOrReference); Tokens = annotate("if (new && num) {\n" " new = 1;\n" "}\n" "if (!delete && num) {\n" " delete = 1;\n" "}"); ASSERT_EQ(Tokens.size(), 26u) << Tokens; EXPECT_TOKEN(Tokens[3], tok::ampamp, TT_BinaryOperator); EXPECT_TOKEN(Tokens[16], tok::ampamp, TT_BinaryOperator); } TEST_F(TokenAnnotatorTest, UnderstandsUsesOfPlusAndMinus) { auto Tokens = annotate("x - 0"); ASSERT_EQ(Tokens.size(), 4u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::minus, TT_BinaryOperator); Tokens = annotate("0 + 0"); ASSERT_EQ(Tokens.size(), 4u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::plus, TT_BinaryOperator); Tokens = annotate("x + +0"); ASSERT_EQ(Tokens.size(), 5u) << Tokens; EXPECT_TOKEN(Tokens[2], tok::plus, TT_UnaryOperator); Tokens = annotate("x ? -0 : +0"); ASSERT_EQ(Tokens.size(), 8u) << Tokens; EXPECT_TOKEN(Tokens[2], tok::minus, TT_UnaryOperator); EXPECT_TOKEN(Tokens[5], tok::plus, TT_UnaryOperator); Tokens = annotate("(-0)"); ASSERT_EQ(Tokens.size(), 5u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::minus, TT_UnaryOperator); Tokens = annotate("0, -0"); ASSERT_EQ(Tokens.size(), 5u) << Tokens; EXPECT_TOKEN(Tokens[2], tok::minus, TT_UnaryOperator); Tokens = annotate("for (; -1;) {\n}"); ASSERT_EQ(Tokens.size(), 10u) << Tokens; EXPECT_TOKEN(Tokens[3], tok::minus, TT_UnaryOperator); Tokens = annotate("x = -1;"); ASSERT_EQ(Tokens.size(), 6u) << Tokens; EXPECT_TOKEN(Tokens[2], tok::minus, TT_UnaryOperator); Tokens = annotate("x[-1]"); ASSERT_EQ(Tokens.size(), 6u) << Tokens; EXPECT_TOKEN(Tokens[2], tok::minus, TT_UnaryOperator); Tokens = annotate("x = {-1};"); ASSERT_EQ(Tokens.size(), 8u) << Tokens; EXPECT_TOKEN(Tokens[3], tok::minus, TT_UnaryOperator); Tokens = annotate("case -x:"); ASSERT_EQ(Tokens.size(), 5u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::minus, TT_UnaryOperator); Tokens = annotate("co_await -x;"); ASSERT_EQ(Tokens.size(), 5u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::minus, TT_UnaryOperator); Tokens = annotate("co_return -x;"); ASSERT_EQ(Tokens.size(), 5u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::minus, TT_UnaryOperator); Tokens = annotate("co_yield -x;"); ASSERT_EQ(Tokens.size(), 5u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::minus, TT_UnaryOperator); Tokens = annotate("delete -x;"); ASSERT_EQ(Tokens.size(), 5u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::minus, TT_UnaryOperator); Tokens = annotate("return -x;"); ASSERT_EQ(Tokens.size(), 5u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::minus, TT_UnaryOperator); Tokens = annotate("throw -x;"); ASSERT_EQ(Tokens.size(), 5u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::minus, TT_UnaryOperator); Tokens = annotate("sizeof -x"); ASSERT_EQ(Tokens.size(), 4u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::minus, TT_UnaryOperator); Tokens = annotate("co_await +x;"); ASSERT_EQ(Tokens.size(), 5u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::plus, TT_UnaryOperator); Tokens = annotate("co_return +x;"); ASSERT_EQ(Tokens.size(), 5u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::plus, TT_UnaryOperator); Tokens = annotate("co_yield +x;"); ASSERT_EQ(Tokens.size(), 5u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::plus, TT_UnaryOperator); Tokens = annotate("delete +x;"); ASSERT_EQ(Tokens.size(), 5u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::plus, TT_UnaryOperator); Tokens = annotate("return +x;"); ASSERT_EQ(Tokens.size(), 5u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::plus, TT_UnaryOperator); Tokens = annotate("throw +x;"); ASSERT_EQ(Tokens.size(), 5u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::plus, TT_UnaryOperator); Tokens = annotate("sizeof +x"); ASSERT_EQ(Tokens.size(), 4u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::plus, TT_UnaryOperator); Tokens = annotate("(int)-x"); ASSERT_EQ(Tokens.size(), 6u) << Tokens; EXPECT_TOKEN(Tokens[3], tok::minus, TT_UnaryOperator); Tokens = annotate("(-x)"); ASSERT_EQ(Tokens.size(), 5u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::minus, TT_UnaryOperator); Tokens = annotate("!+x"); ASSERT_EQ(Tokens.size(), 4u) << Tokens; EXPECT_TOKEN(Tokens[0], tok::exclaim, TT_UnaryOperator); EXPECT_TOKEN(Tokens[1], tok::plus, TT_UnaryOperator); } TEST_F(TokenAnnotatorTest, UnderstandsClasses) { auto Tokens = annotate("class C {};"); ASSERT_EQ(Tokens.size(), 6u) << Tokens; EXPECT_TOKEN(Tokens[2], tok::l_brace, TT_ClassLBrace); EXPECT_TOKEN(Tokens[3], tok::r_brace, TT_ClassRBrace); Tokens = annotate("const class C {} c;"); ASSERT_EQ(Tokens.size(), 8u) << Tokens; EXPECT_TOKEN(Tokens[3], tok::l_brace, TT_ClassLBrace); EXPECT_TOKEN(Tokens[4], tok::r_brace, TT_ClassRBrace); Tokens = annotate("const class {} c;"); ASSERT_EQ(Tokens.size(), 7u) << Tokens; EXPECT_TOKEN(Tokens[2], tok::l_brace, TT_ClassLBrace); EXPECT_TOKEN(Tokens[3], tok::r_brace, TT_ClassRBrace); Tokens = annotate("class [[deprecated(\"\")]] C { int i; };"); ASSERT_EQ(Tokens.size(), 17u) << Tokens; EXPECT_TOKEN(Tokens[10], tok::l_brace, TT_ClassLBrace); EXPECT_TOKEN(Tokens[14], tok::r_brace, TT_ClassRBrace); } TEST_F(TokenAnnotatorTest, UnderstandsStructs) { auto Tokens = annotate("struct S {};"); ASSERT_EQ(Tokens.size(), 6u) << Tokens; EXPECT_TOKEN(Tokens[2], tok::l_brace, TT_StructLBrace); EXPECT_TOKEN(Tokens[3], tok::r_brace, TT_StructRBrace); Tokens = annotate("struct macro(a) S {};"); ASSERT_EQ(Tokens.size(), 10u) << Tokens; EXPECT_TOKEN(Tokens[6], tok::l_brace, TT_StructLBrace); EXPECT_TOKEN(Tokens[7], tok::r_brace, TT_StructRBrace); Tokens = annotate("struct EXPORT_MACRO [[nodiscard]] C { int i; };"); ASSERT_EQ(Tokens.size(), 15u) << Tokens; EXPECT_TOKEN(Tokens[8], tok::l_brace, TT_StructLBrace); EXPECT_TOKEN(Tokens[12], tok::r_brace, TT_StructRBrace); Tokens = annotate("struct [[deprecated]] [[nodiscard]] C { int i; };"); ASSERT_EQ(Tokens.size(), 19u) << Tokens; EXPECT_TOKEN(Tokens[12], tok::l_brace, TT_StructLBrace); EXPECT_TOKEN(Tokens[16], tok::r_brace, TT_StructRBrace); Tokens = annotate("struct macro(a) S {\n" " void f(T &t);\n" "};"); ASSERT_EQ(Tokens.size(), 18u) << Tokens; EXPECT_TOKEN(Tokens[6], tok::l_brace, TT_StructLBrace); EXPECT_TOKEN(Tokens[11], tok::amp, TT_PointerOrReference); EXPECT_TOKEN(Tokens[15], tok::r_brace, TT_StructRBrace); Tokens = annotate("template struct S {};"); ASSERT_EQ(Tokens.size(), 18u) << Tokens; EXPECT_TOKEN(Tokens[7], tok::less, TT_TemplateOpener); EXPECT_TOKEN(Tokens[10], tok::l_square, TT_ArraySubscriptLSquare); EXPECT_TOKEN(Tokens[13], tok::greater, TT_TemplateCloser); EXPECT_TOKEN(Tokens[14], tok::l_brace, TT_StructLBrace); EXPECT_TOKEN(Tokens[15], tok::r_brace, TT_StructRBrace); Tokens = annotate("template struct S {};"); ASSERT_EQ(Tokens.size(), 18u) << Tokens; EXPECT_TOKEN(Tokens[7], tok::less, TT_TemplateOpener); EXPECT_TOKEN(Tokens[10], tok::l_square, TT_ArraySubscriptLSquare); EXPECT_TOKEN(Tokens[13], tok::greater, TT_TemplateCloser); EXPECT_TOKEN(Tokens[14], tok::l_brace, TT_StructLBrace); EXPECT_TOKEN(Tokens[15], tok::r_brace, TT_StructRBrace); Tokens = annotate("template struct S {\n" " void f(T const (&a)[n]);\n" "};"); ASSERT_EQ(Tokens.size(), 35u) << Tokens; EXPECT_TOKEN(Tokens[10], tok::less, TT_TemplateOpener); EXPECT_TOKEN(Tokens[13], tok::l_square, TT_ArraySubscriptLSquare); EXPECT_TOKEN(Tokens[16], tok::greater, TT_TemplateCloser); EXPECT_TOKEN(Tokens[17], tok::l_brace, TT_StructLBrace); EXPECT_TOKEN(Tokens[23], tok::l_paren, TT_FunctionTypeLParen); EXPECT_TOKEN(Tokens[24], tok::amp, TT_UnaryOperator); EXPECT_TOKEN(Tokens[27], tok::l_square, TT_ArraySubscriptLSquare); EXPECT_TOKEN(Tokens[32], tok::r_brace, TT_StructRBrace); Tokens = annotate("template struct S {};"); ASSERT_EQ(Tokens.size(), 15u) << Tokens; EXPECT_TOKEN(Tokens[11], tok::l_brace, TT_StructLBrace); auto Style = getLLVMStyle(); Style.AttributeMacros.push_back("EXPORT"); Tokens = annotate("struct EXPORT StructName {};", Style); ASSERT_EQ(Tokens.size(), 7u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::identifier, TT_AttributeMacro); EXPECT_TOKEN(Tokens[3], tok::l_brace, TT_StructLBrace); EXPECT_TOKEN(Tokens[4], tok::r_brace, TT_StructRBrace); } TEST_F(TokenAnnotatorTest, UnderstandsUnions) { auto Tokens = annotate("union U {};"); ASSERT_EQ(Tokens.size(), 6u) << Tokens; EXPECT_TOKEN(Tokens[2], tok::l_brace, TT_UnionLBrace); EXPECT_TOKEN(Tokens[3], tok::r_brace, TT_UnionRBrace); Tokens = annotate("union U { void f() { return; } };"); ASSERT_EQ(Tokens.size(), 14u) << Tokens; EXPECT_TOKEN(Tokens[2], tok::l_brace, TT_UnionLBrace); EXPECT_TOKEN(Tokens[7], tok::l_brace, TT_FunctionLBrace); EXPECT_TOKEN(Tokens[11], tok::r_brace, TT_UnionRBrace); } TEST_F(TokenAnnotatorTest, UnderstandsEnums) { auto Tokens = annotate("enum E {};"); ASSERT_EQ(Tokens.size(), 6u) << Tokens; EXPECT_TOKEN(Tokens[2], tok::l_brace, TT_EnumLBrace); EXPECT_TOKEN(Tokens[3], tok::r_brace, TT_EnumRBrace); } TEST_F(TokenAnnotatorTest, UnderstandsDefaultedAndDeletedFunctions) { auto Tokens = annotate("auto operator<=>(const T &) const & = default;"); ASSERT_EQ(Tokens.size(), 14u) << Tokens; EXPECT_TOKEN(Tokens[9], tok::amp, TT_PointerOrReference); Tokens = annotate("template void F(T) && = delete;"); ASSERT_EQ(Tokens.size(), 15u) << Tokens; EXPECT_TOKEN(Tokens[10], tok::ampamp, TT_PointerOrReference); } TEST_F(TokenAnnotatorTest, UnderstandsVariables) { auto Tokens = annotate("inline bool var = is_integral_v && is_signed_v;"); ASSERT_EQ(Tokens.size(), 15u) << Tokens; EXPECT_TOKEN(Tokens[8], tok::ampamp, TT_BinaryOperator); } TEST_F(TokenAnnotatorTest, UnderstandsVariableTemplates) { auto Tokens = annotate("template " "inline bool var = is_integral_v && is_signed_v;"); ASSERT_EQ(Tokens.size(), 20u) << Tokens; EXPECT_TOKEN(Tokens[13], tok::ampamp, TT_BinaryOperator); } TEST_F(TokenAnnotatorTest, UnderstandsTemplatesInMacros) { auto Tokens = annotate("#define FOO(typeName) \\\n" " { #typeName, foo(new foo(#typeName)) }"); ASSERT_EQ(Tokens.size(), 27u) << Tokens; EXPECT_TOKEN(Tokens[11], tok::less, TT_TemplateOpener); EXPECT_TOKEN(Tokens[13], tok::greater, TT_TemplateCloser); EXPECT_TOKEN(Tokens[17], tok::less, TT_TemplateOpener); EXPECT_TOKEN(Tokens[19], tok::greater, TT_TemplateCloser); } TEST_F(TokenAnnotatorTest, UnderstandsGreaterAfterTemplateCloser) { auto Tokens = annotate("if (std::tuple_size_v > 0)"); ASSERT_EQ(Tokens.size(), 12u) << Tokens; EXPECT_TOKEN(Tokens[5], tok::less, TT_TemplateOpener); EXPECT_TOKEN(Tokens[7], tok::greater, TT_TemplateCloser); EXPECT_TOKEN(Tokens[8], tok::greater, TT_BinaryOperator); } TEST_F(TokenAnnotatorTest, UnderstandsTernaryInTemplate) { // IsExpression = false auto Tokens = annotate("foo();"); ASSERT_EQ(Tokens.size(), 12u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::less, TT_TemplateOpener); EXPECT_TOKEN(Tokens[3], tok::question, TT_ConditionalExpr); EXPECT_TOKEN(Tokens[5], tok::colon, TT_ConditionalExpr); EXPECT_TOKEN(Tokens[7], tok::greater, TT_TemplateCloser); // IsExpression = true Tokens = annotate("return foo();"); ASSERT_EQ(Tokens.size(), 13u) << Tokens; EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener); EXPECT_TOKEN(Tokens[4], tok::question, TT_ConditionalExpr); EXPECT_TOKEN(Tokens[6], tok::colon, TT_ConditionalExpr); EXPECT_TOKEN(Tokens[8], tok::greater, TT_TemplateCloser); Tokens = annotate("return foo{};"); ASSERT_EQ(Tokens.size(), 13u) << Tokens; EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener); EXPECT_TOKEN(Tokens[4], tok::question, TT_ConditionalExpr); EXPECT_TOKEN(Tokens[6], tok::colon, TT_ConditionalExpr); EXPECT_TOKEN(Tokens[8], tok::greater, TT_TemplateCloser); } TEST_F(TokenAnnotatorTest, UnderstandsNonTemplateAngleBrackets) { auto Tokens = annotate("return a < b && c > d;"); ASSERT_EQ(Tokens.size(), 10u) << Tokens; EXPECT_TOKEN(Tokens[2], tok::less, TT_BinaryOperator); EXPECT_TOKEN(Tokens[6], tok::greater, TT_BinaryOperator); Tokens = annotate("a < 0 ? b : a > 0 ? c : d;"); ASSERT_EQ(Tokens.size(), 15u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::less, TT_BinaryOperator); EXPECT_TOKEN(Tokens[7], tok::greater, TT_BinaryOperator); Tokens = annotate("return A < B ? true : A > B;"); ASSERT_EQ(Tokens.size(), 12u) << Tokens; EXPECT_TOKEN(Tokens[2], tok::less, TT_BinaryOperator); EXPECT_TOKEN(Tokens[8], tok::greater, TT_BinaryOperator); Tokens = annotate("return A < B ? true : A > B ? false : false;"); ASSERT_EQ(Tokens.size(), 16u) << Tokens; EXPECT_TOKEN(Tokens[2], tok::less, TT_BinaryOperator); EXPECT_TOKEN(Tokens[8], tok::greater, TT_BinaryOperator); Tokens = annotate("return A < B ^ A > B;"); ASSERT_EQ(Tokens.size(), 10u) << Tokens; EXPECT_TOKEN(Tokens[2], tok::less, TT_BinaryOperator); EXPECT_TOKEN(Tokens[6], tok::greater, TT_BinaryOperator); Tokens = annotate("ratio{-1, 2} < ratio{-1, 3} == -1 / 3 > -1 / 2;"); ASSERT_EQ(Tokens.size(), 27u) << Tokens; EXPECT_TOKEN(Tokens[7], tok::less, TT_BinaryOperator); EXPECT_TOKEN(Tokens[20], tok::greater, TT_BinaryOperator); } TEST_F(TokenAnnotatorTest, UnderstandsTemplateTemplateParameters) { auto Tokens = annotate("template