Compiler projects using llvm
//===- BuildTreeTest.cpp --------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file tests the syntax tree generation from the ClangAST.
//
//===----------------------------------------------------------------------===//

#include "TreeTestBase.h"

using namespace clang;
using namespace clang::syntax;

namespace {

class BuildSyntaxTreeTest : public SyntaxTreeTest {
protected:
  ::testing::AssertionResult treeDumpEqual(StringRef Code, StringRef Tree) {
    SCOPED_TRACE(llvm::join(GetParam().getCommandLineArgs(), " "));

    auto *Root = buildTree(Code, GetParam());
    auto ErrorOK = errorOK(Code);
    if (!ErrorOK)
      return ErrorOK;
    auto Actual = StringRef(Root->dump(*TM)).trim().str();
    // EXPECT_EQ shows the diff between the two strings if they are different.
    EXPECT_EQ(Tree.trim().str(), Actual);
    if (Actual != Tree.trim().str()) {
      return ::testing::AssertionFailure();
    }
    return ::testing::AssertionSuccess();
  }

  ::testing::AssertionResult
  treeDumpEqualOnAnnotations(StringRef CodeWithAnnotations,
                             ArrayRef<StringRef> TreeDumps) {
    SCOPED_TRACE(llvm::join(GetParam().getCommandLineArgs(), " "));

    auto AnnotatedCode = llvm::Annotations(CodeWithAnnotations);
    auto *Root = buildTree(AnnotatedCode.code(), GetParam());

    auto ErrorOK = errorOK(AnnotatedCode.code());
    if (!ErrorOK)
      return ErrorOK;

    auto AnnotatedRanges = AnnotatedCode.ranges();
    if (AnnotatedRanges.size() != TreeDumps.size()) {
      return ::testing::AssertionFailure()
             << "The number of annotated ranges in the source code is "
                "different "
                "to the number of their corresponding tree dumps.";
    }
    bool Failed = false;
    for (unsigned i = 0; i < AnnotatedRanges.size(); i++) {
      auto *AnnotatedNode = nodeByRange(AnnotatedRanges[i], Root);
      assert(AnnotatedNode);
      auto AnnotatedNodeDump =
          StringRef(AnnotatedNode->dump(*TM))
              .trim()
              .str();
      // EXPECT_EQ shows the diff between the two strings if they are different.
      EXPECT_EQ(TreeDumps[i].trim().str(), AnnotatedNodeDump)
          << "Dumps diverged for the code:\n"
          << AnnotatedCode.code().slice(AnnotatedRanges[i].Begin,
                                        AnnotatedRanges[i].End);
      if (AnnotatedNodeDump != TreeDumps[i].trim().str())
        Failed = true;
    }
    return Failed ? ::testing::AssertionFailure()
                  : ::testing::AssertionSuccess();
  }

private:
  ::testing::AssertionResult errorOK(StringRef RawCode) {
    if (!RawCode.contains("error-ok")) {
      if (Diags->getClient()->getNumErrors() != 0) {
        return ::testing::AssertionFailure()
               << "Source file has syntax errors (suppress with /*error-ok*/), "
                  "they were printed to the "
                  "test log";
      }
    }
    return ::testing::AssertionSuccess();
  }
};

INSTANTIATE_TEST_SUITE_P(SyntaxTreeTests, BuildSyntaxTreeTest,
                        testing::ValuesIn(allTestClangConfigs()) );

TEST_P(BuildSyntaxTreeTest, Simple) {
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
int main() {}
void foo() {}
)cpp",
      R"txt(
TranslationUnit Detached
|-SimpleDeclaration
| |-'int'
| |-DeclaratorList Declarators
| | `-SimpleDeclarator ListElement
| |   |-'main'
| |   `-ParametersAndQualifiers
| |     |-'(' OpenParen
| |     `-')' CloseParen
| `-CompoundStatement
|   |-'{' OpenParen
|   `-'}' CloseParen
`-SimpleDeclaration
  |-'void'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'foo'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     `-')' CloseParen
  `-CompoundStatement
    |-'{' OpenParen
    `-'}' CloseParen
)txt"));
}

TEST_P(BuildSyntaxTreeTest, SimpleVariable) {
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
int a;
int b = 42;
)cpp",
      R"txt(
TranslationUnit Detached
|-SimpleDeclaration
| |-'int'
| |-DeclaratorList Declarators
| | `-SimpleDeclarator ListElement
| |   `-'a'
| `-';'
`-SimpleDeclaration
  |-'int'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'b'
  |   |-'='
  |   `-IntegerLiteralExpression
  |     `-'42' LiteralToken
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, SimpleFunction) {
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
void foo(int a, int b) {}
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'void'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'foo'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     |-ParameterDeclarationList Parameters
  |     | |-SimpleDeclaration ListElement
  |     | | |-'int'
  |     | | `-DeclaratorList Declarators
  |     | |   `-SimpleDeclarator ListElement
  |     | |     `-'a'
  |     | |-',' ListDelimiter
  |     | `-SimpleDeclaration ListElement
  |     |   |-'int'
  |     |   `-DeclaratorList Declarators
  |     |     `-SimpleDeclarator ListElement
  |     |       `-'b'
  |     `-')' CloseParen
  `-CompoundStatement
    |-'{' OpenParen
    `-'}' CloseParen
)txt"));
}

TEST_P(BuildSyntaxTreeTest, Simple_BackslashInsideToken) {
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
in\
t a;
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'in\
t'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   `-'a'
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, If) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  [[if (1) {}]]
  [[if (1) {} else if (0) {}]]
}
)cpp",
      {R"txt(
IfStatement Statement
|-'if' IntroducerKeyword
|-'('
|-ExpressionStatement Condition
| `-IntegerLiteralExpression Expression
|   `-'1' LiteralToken
|-')'
`-CompoundStatement ThenStatement
  |-'{' OpenParen
  `-'}' CloseParen
  )txt",
       R"txt(
IfStatement Statement
|-'if' IntroducerKeyword
|-'('
|-ExpressionStatement Condition
| `-IntegerLiteralExpression Expression
|   `-'1' LiteralToken
|-')'
|-CompoundStatement ThenStatement
| |-'{' OpenParen
| `-'}' CloseParen
|-'else' ElseKeyword
`-IfStatement ElseStatement
  |-'if' IntroducerKeyword
  |-'('
  |-ExpressionStatement Condition
  | `-IntegerLiteralExpression Expression
  |   `-'0' LiteralToken
  |-')'
  `-CompoundStatement ThenStatement
    |-'{' OpenParen
    `-'}' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, IfDecl) {
  if (!GetParam().isCXX17OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  [[if (int a = 5) {}]]
  [[if (int a; a == 5) {}]]
}
)cpp",
      {R"txt(
IfStatement Statement
|-'if' IntroducerKeyword
|-'('
|-DeclarationStatement Condition
| `-SimpleDeclaration
|   |-'int'
|   `-DeclaratorList Declarators
|     `-SimpleDeclarator ListElement
|       |-'a'
|       |-'='
|       `-IntegerLiteralExpression
|         `-'5' LiteralToken
|-')'
`-CompoundStatement ThenStatement
  |-'{' OpenParen
  `-'}' CloseParen
      )txt",
       R"txt(
IfStatement Statement
|-'if' IntroducerKeyword
|-'('
|-DeclarationStatement
| |-SimpleDeclaration
| | |-'int'
| | `-DeclaratorList Declarators
| |   `-SimpleDeclarator ListElement
| |     `-'a'
| `-';'
|-ExpressionStatement Condition
| `-BinaryOperatorExpression Expression
|   |-IdExpression LeftHandSide
|   | `-UnqualifiedId UnqualifiedId
|   |   `-'a'
|   |-'==' OperatorToken
|   `-IntegerLiteralExpression RightHandSide
|     `-'5' LiteralToken
|-')'
`-CompoundStatement ThenStatement
  |-'{' OpenParen
  `-'}' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, For) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  [[for (;;)  {}]]
}
)cpp",
      {R"txt(
ForStatement Statement
|-'for' IntroducerKeyword
|-'('
|-';'
|-';'
|-')'
`-CompoundStatement BodyStatement
  |-'{' OpenParen
  `-'}' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, RangeBasedFor) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  int a[3];
  [[for (int x : a)
    ;]]
}
)cpp",
      {R"txt(
RangeBasedForStatement Statement
|-'for' IntroducerKeyword
|-'('
|-SimpleDeclaration
| |-'int'
| |-DeclaratorList Declarators
| | `-SimpleDeclarator ListElement
| |   `-'x'
| `-':'
|-IdExpression
| `-UnqualifiedId UnqualifiedId
|   `-'a'
|-')'
`-EmptyStatement BodyStatement
  `-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, DeclarationStatement) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  [[int a = 10;]]
}
)cpp",
      {R"txt(
DeclarationStatement Statement
|-SimpleDeclaration
| |-'int'
| `-DeclaratorList Declarators
|   `-SimpleDeclarator ListElement
|     |-'a'
|     |-'='
|     `-IntegerLiteralExpression
|       `-'10' LiteralToken
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, Switch) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  [[switch (1) {
    case 0:
    default:;
  }]]
}
)cpp",
      {R"txt(
SwitchStatement Statement
|-'switch' IntroducerKeyword
|-'('
|-IntegerLiteralExpression
| `-'1' LiteralToken
|-')'
`-CompoundStatement BodyStatement
  |-'{' OpenParen
  |-CaseStatement Statement
  | |-'case' IntroducerKeyword
  | |-IntegerLiteralExpression CaseValue
  | | `-'0' LiteralToken
  | |-':'
  | `-DefaultStatement BodyStatement
  |   |-'default' IntroducerKeyword
  |   |-':'
  |   `-EmptyStatement BodyStatement
  |     `-';'
  `-'}' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, While) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  [[while (1) { continue; break; }]]
}
)cpp",
      {R"txt(
WhileStatement Statement
|-'while' IntroducerKeyword
|-'('
|-IntegerLiteralExpression
| `-'1' LiteralToken
|-')'
`-CompoundStatement BodyStatement
  |-'{' OpenParen
  |-ContinueStatement Statement
  | |-'continue' IntroducerKeyword
  | `-';'
  |-BreakStatement Statement
  | |-'break' IntroducerKeyword
  | `-';'
  `-'}' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, UnhandledStatement) {
  // Unhandled statements should end up as 'unknown statement'.
  // This example uses a 'label statement', which does not yet have a syntax
  // counterpart.
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
int test() {
  [[foo: return 100;]]
}
)cpp",
      {R"txt(
UnknownStatement Statement
|-'foo'
|-':'
`-ReturnStatement
  |-'return' IntroducerKeyword
  |-IntegerLiteralExpression ReturnValue
  | `-'100' LiteralToken
  `-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, Expressions) {
  // expressions should be wrapped in 'ExpressionStatement' when they appear
  // in a statement position.
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
void test() {
  test();
  if (1) test(); else test();
}
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'void'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'test'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     `-')' CloseParen
  `-CompoundStatement
    |-'{' OpenParen
    |-ExpressionStatement Statement
    | |-CallExpression Expression
    | | |-IdExpression Callee
    | | | `-UnqualifiedId UnqualifiedId
    | | |   `-'test'
    | | |-'(' OpenParen
    | | `-')' CloseParen
    | `-';'
    |-IfStatement Statement
    | |-'if' IntroducerKeyword
    | |-'('
    | |-ExpressionStatement Condition
    | | `-IntegerLiteralExpression Expression
    | |   `-'1' LiteralToken
    | |-')'
    | |-ExpressionStatement ThenStatement
    | | |-CallExpression Expression
    | | | |-IdExpression Callee
    | | | | `-UnqualifiedId UnqualifiedId
    | | | |   `-'test'
    | | | |-'(' OpenParen
    | | | `-')' CloseParen
    | | `-';'
    | |-'else' ElseKeyword
    | `-ExpressionStatement ElseStatement
    |   |-CallExpression Expression
    |   | |-IdExpression Callee
    |   | | `-UnqualifiedId UnqualifiedId
    |   | |   `-'test'
    |   | |-'(' OpenParen
    |   | `-')' CloseParen
    |   `-';'
    `-'}' CloseParen
)txt"));
}

TEST_P(BuildSyntaxTreeTest, ConditionalOperator) {
  // FIXME: conditional expression is not modeled yet.
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  [[1?:2]];
}
)cpp",
      {R"txt(
UnknownExpression Expression
|-IntegerLiteralExpression
| `-'1' LiteralToken
|-'?'
|-':'
`-IntegerLiteralExpression
  `-'2' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, UnqualifiedId_Identifier) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test(int a) {
  [[a]];
}
)cpp",
      {R"txt(
IdExpression Expression
`-UnqualifiedId UnqualifiedId
  `-'a'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, UnqualifiedId_OperatorFunctionId) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {
  friend X operator+(const X&, const X&);
};
void test(X x) {
  [[operator+(x, x)]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-IdExpression Callee
| `-UnqualifiedId UnqualifiedId
|   |-'operator'
|   `-'+'
|-'(' OpenParen
|-CallArguments Arguments
| |-IdExpression ListElement
| | `-UnqualifiedId UnqualifiedId
| |   `-'x'
| |-',' ListDelimiter
| `-IdExpression ListElement
|   `-UnqualifiedId UnqualifiedId
|     `-'x'
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, UnqualifiedId_ConversionFunctionId) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {
  operator int();
};
void test(X x) {
  [[x.operator int()]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-MemberExpression Callee
| |-IdExpression Object
| | `-UnqualifiedId UnqualifiedId
| |   `-'x'
| |-'.' AccessToken
| `-IdExpression Member
|   `-UnqualifiedId UnqualifiedId
|     |-'operator'
|     `-'int'
|-'(' OpenParen
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, UnqualifiedId_LiteralOperatorId) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
unsigned operator "" _w(char);
void test() {
  [[operator "" _w('1')]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-IdExpression Callee
| `-UnqualifiedId UnqualifiedId
|   |-'operator'
|   |-'""'
|   `-'_w'
|-'(' OpenParen
|-CallArguments Arguments
| `-CharacterLiteralExpression ListElement
|   `-''1'' LiteralToken
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, UnqualifiedId_Destructor) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X { };
void test(X x) {
  [[x.~X()]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-MemberExpression Callee
| |-IdExpression Object
| | `-UnqualifiedId UnqualifiedId
| |   `-'x'
| |-'.' AccessToken
| `-IdExpression Member
|   `-UnqualifiedId UnqualifiedId
|     |-'~'
|     `-'X'
|-'(' OpenParen
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, UnqualifiedId_DecltypeDestructor) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X { };
void test(X x) {
  // FIXME: Make `decltype(x)` a child of `MemberExpression`. It is currently
  // not because `Expr::getSourceRange()` returns the range of `x.~` for the
  // `MemberExpr` instead of the expected `x.~decltype(x)`, this is a bug in
  // clang.
  [[x.~decltype(x)()]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-MemberExpression Callee
| |-IdExpression Object
| | `-UnqualifiedId UnqualifiedId
| |   `-'x'
| |-'.' AccessToken
| `-IdExpression Member
|   `-UnqualifiedId UnqualifiedId
|     `-'~'
|-'decltype'
|-'('
|-'x'
|-')'
|-'('
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, UnqualifiedId_TemplateId) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
template<typename T>
T f();
void test() {
  [[f<int>()]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-IdExpression Callee
| `-UnqualifiedId UnqualifiedId
|   |-'f'
|   |-'<'
|   |-'int'
|   `-'>'
|-'(' OpenParen
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, QualifiedId_NamespaceSpecifier) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
namespace n {
  struct S { };
}
void test() {
  [[::n::S s1]];
  [[n::S s2]];
}
)cpp",
      {R"txt(
SimpleDeclaration
|-NestedNameSpecifier
| |-'::' ListDelimiter
| |-IdentifierNameSpecifier ListElement
| | `-'n'
| `-'::' ListDelimiter
|-'S'
`-DeclaratorList Declarators
  `-SimpleDeclarator ListElement
    `-'s1'
)txt",
       R"txt(
SimpleDeclaration
|-NestedNameSpecifier
| |-IdentifierNameSpecifier ListElement
| | `-'n'
| `-'::' ListDelimiter
|-'S'
`-DeclaratorList Declarators
  `-SimpleDeclarator ListElement
    `-'s2'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, QualifiedId_TemplateSpecifier) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
template<typename T>
struct ST {
  struct S { };
};
void test() {
  [[::template ST<int>::S s1]];
  [[::ST<int>::S s2]];
}
)cpp",
      {R"txt(
SimpleDeclaration
|-NestedNameSpecifier
| |-'::' ListDelimiter
| |-SimpleTemplateNameSpecifier ListElement
| | |-'template'
| | |-'ST'
| | |-'<'
| | |-'int'
| | `-'>'
| `-'::' ListDelimiter
|-'S'
`-DeclaratorList Declarators
  `-SimpleDeclarator ListElement
    `-'s1'
)txt",
       R"txt(
SimpleDeclaration
|-NestedNameSpecifier
| |-'::' ListDelimiter
| |-SimpleTemplateNameSpecifier ListElement
| | |-'ST'
| | |-'<'
| | |-'int'
| | `-'>'
| `-'::' ListDelimiter
|-'S'
`-DeclaratorList Declarators
  `-SimpleDeclarator ListElement
    `-'s2'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, QualifiedId_DecltypeSpecifier) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S {
  static void f(){}
};
void test(S s) {
  [[decltype(s)::f()]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-IdExpression Callee
| |-NestedNameSpecifier Qualifier
| | |-DecltypeNameSpecifier ListElement
| | | |-'decltype'
| | | |-'('
| | | |-IdExpression
| | | | `-UnqualifiedId UnqualifiedId
| | | |   `-'s'
| | | `-')'
| | `-'::' ListDelimiter
| `-UnqualifiedId UnqualifiedId
|   `-'f'
|-'(' OpenParen
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, QualifiedId_OptionalTemplateKw) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S {
  template<typename U>
  static U f();
};
void test() {
  [[S::f<int>()]];
  [[S::template f<int>()]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-IdExpression Callee
| |-NestedNameSpecifier Qualifier
| | |-IdentifierNameSpecifier ListElement
| | | `-'S'
| | `-'::' ListDelimiter
| `-UnqualifiedId UnqualifiedId
|   |-'f'
|   |-'<'
|   |-'int'
|   `-'>'
|-'(' OpenParen
`-')' CloseParen
)txt",
       R"txt(
CallExpression Expression
|-IdExpression Callee
| |-NestedNameSpecifier Qualifier
| | |-IdentifierNameSpecifier ListElement
| | | `-'S'
| | `-'::' ListDelimiter
| |-'template' TemplateKeyword
| `-UnqualifiedId UnqualifiedId
|   |-'f'
|   |-'<'
|   |-'int'
|   `-'>'
|-'(' OpenParen
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, QualifiedId_Complex) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
namespace n {
  template<typename T>
  struct ST {
    template<typename U>
    static U f();
  };
}
void test() {
  [[::n::template ST<int>::template f<int>()]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-IdExpression Callee
| |-NestedNameSpecifier Qualifier
| | |-'::' ListDelimiter
| | |-IdentifierNameSpecifier ListElement
| | | `-'n'
| | |-'::' ListDelimiter
| | |-SimpleTemplateNameSpecifier ListElement
| | | |-'template'
| | | |-'ST'
| | | |-'<'
| | | |-'int'
| | | `-'>'
| | `-'::' ListDelimiter
| |-'template' TemplateKeyword
| `-UnqualifiedId UnqualifiedId
|   |-'f'
|   |-'<'
|   |-'int'
|   `-'>'
|-'(' OpenParen
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, QualifiedId_DependentType) {
  if (!GetParam().isCXX()) {
    return;
  }
  if (GetParam().hasDelayedTemplateParsing()) {
    // FIXME: Make this test work on Windows by generating the expected syntax
    // tree when `-fdelayed-template-parsing` is active.
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
template <typename T>
void test() {
  [[T::template U<int>::f()]];
  [[T::U::f()]];
  [[T::template f<0>()]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-IdExpression Callee
| |-NestedNameSpecifier Qualifier
| | |-IdentifierNameSpecifier ListElement
| | | `-'T'
| | |-'::' ListDelimiter
| | |-SimpleTemplateNameSpecifier ListElement
| | | |-'template'
| | | |-'U'
| | | |-'<'
| | | |-'int'
| | | `-'>'
| | `-'::' ListDelimiter
| `-UnqualifiedId UnqualifiedId
|   `-'f'
|-'(' OpenParen
`-')' CloseParen
)txt",
       R"txt(
CallExpression Expression
|-IdExpression Callee
| |-NestedNameSpecifier Qualifier
| | |-IdentifierNameSpecifier ListElement
| | | `-'T'
| | |-'::' ListDelimiter
| | |-IdentifierNameSpecifier ListElement
| | | `-'U'
| | `-'::' ListDelimiter
| `-UnqualifiedId UnqualifiedId
|   `-'f'
|-'(' OpenParen
`-')' CloseParen
)txt",
       R"txt(
CallExpression Expression
|-IdExpression Callee
| |-NestedNameSpecifier Qualifier
| | |-IdentifierNameSpecifier ListElement
| | | `-'T'
| | `-'::' ListDelimiter
| |-'template' TemplateKeyword
| `-UnqualifiedId UnqualifiedId
|   |-'f'
|   |-'<'
|   |-IntegerLiteralExpression
|   | `-'0' LiteralToken
|   `-'>'
|-'(' OpenParen
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, This_Simple) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S {
  S* test(){
    return [[this]];
  }
};
)cpp",
      {R"txt(
ThisExpression ReturnValue
`-'this' IntroducerKeyword
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, This_ExplicitMemberAccess) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S {
  int a;
  void test(){
    [[this->a]];
  }
};
)cpp",
      {R"txt(
MemberExpression Expression
|-ThisExpression Object
| `-'this' IntroducerKeyword
|-'->' AccessToken
`-IdExpression Member
  `-UnqualifiedId UnqualifiedId
    `-'a'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, This_ImplicitMemberAccess) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S {
  int a;
  void test(){
    [[a]];
  }
};
)cpp",
      {R"txt(
IdExpression Expression
`-UnqualifiedId UnqualifiedId
  `-'a'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, ParenExpr) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  [[(1)]];
  [[((1))]];
  [[(1 + (2))]];
}
)cpp",
      {R"txt(
ParenExpression Expression
|-'(' OpenParen
|-IntegerLiteralExpression SubExpression
| `-'1' LiteralToken
`-')' CloseParen
)txt",
       R"txt(
ParenExpression Expression
|-'(' OpenParen
|-ParenExpression SubExpression
| |-'(' OpenParen
| |-IntegerLiteralExpression SubExpression
| | `-'1' LiteralToken
| `-')' CloseParen
`-')' CloseParen
)txt",
       R"txt(
ParenExpression Expression
|-'(' OpenParen
|-BinaryOperatorExpression SubExpression
| |-IntegerLiteralExpression LeftHandSide
| | `-'1' LiteralToken
| |-'+' OperatorToken
| `-ParenExpression RightHandSide
|   |-'(' OpenParen
|   |-IntegerLiteralExpression SubExpression
|   | `-'2' LiteralToken
|   `-')' CloseParen
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, UserDefinedLiteral_Char) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
unsigned operator "" _c(char);
void test() {
  [['2'_c]];
}
    )cpp",
      {R"txt(
CharUserDefinedLiteralExpression Expression
`-''2'_c' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, UserDefinedLiteral_String) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
typedef decltype(sizeof(void *)) size_t;

unsigned operator "" _s(const char*, size_t);

void test() {
  [["12"_s]];
}
    )cpp",
      {R"txt(
StringUserDefinedLiteralExpression Expression
`-'"12"_s' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, UserDefinedLiteral_Integer) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
unsigned operator "" _i(unsigned long long);
unsigned operator "" _r(const char*);
template <char...>
unsigned operator "" _t();

void test() {
  [[12_i]];
  [[12_r]];
  [[12_t]];
}
    )cpp",
      {R"txt(
IntegerUserDefinedLiteralExpression Expression
`-'12_i' LiteralToken
)txt",
       R"txt(
IntegerUserDefinedLiteralExpression Expression
`-'12_r' LiteralToken
)txt",
       R"txt(
IntegerUserDefinedLiteralExpression Expression
`-'12_t' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, UserDefinedLiteral_Float) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
unsigned operator "" _f(long double);
unsigned operator "" _r(const char*);
template <char...>
unsigned operator "" _t();

void test() {
  [[1.2_f]];
  [[1.2_r]];
  [[1.2_t]];
}
    )cpp",
      {R"txt(
FloatUserDefinedLiteralExpression Expression
`-'1.2_f' LiteralToken
)txt",
       R"txt(
FloatUserDefinedLiteralExpression Expression
`-'1.2_r' LiteralToken
)txt",
       R"txt(
FloatUserDefinedLiteralExpression Expression
`-'1.2_t' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, IntegerLiteral_LongLong) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  [[12ll]];
  [[12ull]];
}
)cpp",
      {R"txt(
IntegerLiteralExpression Expression
`-'12ll' LiteralToken
)txt",
       R"txt(
IntegerLiteralExpression Expression
`-'12ull' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, IntegerLiteral_Binary) {
  if (!GetParam().isCXX14OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  [[0b1100]];
}
)cpp",
      {R"txt(
IntegerLiteralExpression Expression
`-'0b1100' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, IntegerLiteral_WithDigitSeparators) {
  if (!GetParam().isCXX14OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  [[1'2'0ull]];
}
)cpp",
      {R"txt(
IntegerLiteralExpression Expression
`-'1'2'0ull' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, CharacterLiteral) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  [['a']];
  [['\n']];
  [['\x20']];
  [['\0']];
  [[L'a']];
  [[L'α']];
}
)cpp",
      {R"txt(
CharacterLiteralExpression Expression
`-''a'' LiteralToken
)txt",
       R"txt(
CharacterLiteralExpression Expression
`-''\n'' LiteralToken
)txt",
       R"txt(
CharacterLiteralExpression Expression
`-''\x20'' LiteralToken
)txt",
       R"txt(
CharacterLiteralExpression Expression
`-''\0'' LiteralToken
)txt",
       R"txt(
CharacterLiteralExpression Expression
`-'L'a'' LiteralToken
)txt",
       R"txt(
CharacterLiteralExpression Expression
`-'L'α'' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, CharacterLiteral_Utf) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  [[u'a']];
  [[u'構']];
  [[U'a']];
  [[U'🌲']];
}
)cpp",
      {R"txt(
CharacterLiteralExpression Expression
`-'u'a'' LiteralToken
)txt",
       R"txt(
CharacterLiteralExpression Expression
`-'u'構'' LiteralToken
)txt",
       R"txt(
CharacterLiteralExpression Expression
`-'U'a'' LiteralToken
)txt",
       R"txt(
CharacterLiteralExpression Expression
`-'U'🌲'' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, CharacterLiteral_Utf8) {
  if (!GetParam().isCXX17OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  [[u8'a']];
  [[u8'\x7f']];
}
)cpp",
      {R"txt(
CharacterLiteralExpression Expression
`-'u8'a'' LiteralToken
)txt",
       R"txt(
CharacterLiteralExpression Expression
`-'u8'\x7f'' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, FloatingLiteral) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  [[1e-2]];
  [[2.]];
  [[.2]];
  [[2.f]];
}
)cpp",
      {R"txt(
FloatingLiteralExpression Expression
`-'1e-2' LiteralToken
)txt",
       R"txt(
FloatingLiteralExpression Expression
`-'2.' LiteralToken
)txt",
       R"txt(
FloatingLiteralExpression Expression
`-'.2' LiteralToken
)txt",
       R"txt(
FloatingLiteralExpression Expression
`-'2.f' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, FloatingLiteral_Hexadecimal) {
  if (!GetParam().isCXX17OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  [[0xfp1]];
  [[0xf.p1]];
  [[0x.fp1]];
  [[0xf.fp1f]];
}
)cpp",
      {R"txt(
FloatingLiteralExpression Expression
`-'0xfp1' LiteralToken
)txt",
       R"txt(
FloatingLiteralExpression Expression
`-'0xf.p1' LiteralToken
)txt",
       R"txt(
FloatingLiteralExpression Expression
`-'0x.fp1' LiteralToken
)txt",
       R"txt(
FloatingLiteralExpression Expression
`-'0xf.fp1f' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, StringLiteral) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  [["a\n\0\x20"]];
  [[L"αβ"]];
}
)cpp",
      {R"txt(
StringLiteralExpression Expression
`-'"a\n\0\x20"' LiteralToken
)txt",
       R"txt(
StringLiteralExpression Expression
`-'L"αβ"' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, StringLiteral_Utf) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  [[u8"a\x1f\x05"]];
  [[u"C++抽象構文木"]];
  [[U"📖🌲\n"]];
}
)cpp",
      {R"txt(
StringLiteralExpression Expression
`-'u8"a\x1f\x05"' LiteralToken
)txt",
       R"txt(
StringLiteralExpression Expression
`-'u"C++抽象構文木"' LiteralToken
)txt",
       R"txt(
StringLiteralExpression Expression
`-'U"📖🌲\n"' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, StringLiteral_Raw) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  // This test uses regular string literals instead of raw string literals to
  // hold source code and expected output because of a bug in MSVC up to MSVC
  // 2019 16.2:
  // https://developercommunity.visualstudio.com/content/problem/67300/stringifying-raw-string-literal.html
  EXPECT_TRUE(treeDumpEqual( //
      "void test() {\n"
      "  R\"SyntaxTree(\n"
      "  Hello \"Syntax\" \\\"\n"
      "  )SyntaxTree\";\n"
      "}\n",
      "TranslationUnit Detached\n"
      "`-SimpleDeclaration\n"
      "  |-'void'\n"
      "  |-DeclaratorList Declarators\n"
      "  | `-SimpleDeclarator ListElement\n"
      "  |   |-'test'\n"
      "  |   `-ParametersAndQualifiers\n"
      "  |     |-'(' OpenParen\n"
      "  |     `-')' CloseParen\n"
      "  `-CompoundStatement\n"
      "    |-'{' OpenParen\n"
      "    |-ExpressionStatement Statement\n"
      "    | |-StringLiteralExpression Expression\n"
      "    | | `-'R\"SyntaxTree(\n"
      "  Hello \"Syntax\" \\\"\n"
      "  )SyntaxTree\"' LiteralToken\n"
      "    | `-';'\n"
      "    `-'}' CloseParen\n"));
}

TEST_P(BuildSyntaxTreeTest, BoolLiteral) {
  if (GetParam().isC()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  [[true]];
  [[false]];
}
)cpp",
      {R"txt(
BoolLiteralExpression Expression
`-'true' LiteralToken
)txt",
       R"txt(
BoolLiteralExpression Expression
`-'false' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, CxxNullPtrLiteral) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  [[nullptr]];
}
)cpp",
      {R"txt(
CxxNullPtrExpression Expression
`-'nullptr' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, PostfixUnaryOperator) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test(int a) {
  [[a++]];
  [[a--]];
}
)cpp",
      {R"txt(
PostfixUnaryOperatorExpression Expression
|-IdExpression Operand
| `-UnqualifiedId UnqualifiedId
|   `-'a'
`-'++' OperatorToken
)txt",
       R"txt(
PostfixUnaryOperatorExpression Expression
|-IdExpression Operand
| `-UnqualifiedId UnqualifiedId
|   `-'a'
`-'--' OperatorToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, PrefixUnaryOperator) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test(int a, int *ap) {
  [[--a]]; [[++a]];
  [[~a]];
  [[-a]];
  [[+a]];
  [[&a]];
  [[*ap]];
  [[!a]];
  [[__real a]]; [[__imag a]];
}
)cpp",
      {R"txt(
PrefixUnaryOperatorExpression Expression
|-'--' OperatorToken
`-IdExpression Operand
  `-UnqualifiedId UnqualifiedId
    `-'a'
)txt",
       R"txt(
PrefixUnaryOperatorExpression Expression
|-'++' OperatorToken
`-IdExpression Operand
  `-UnqualifiedId UnqualifiedId
    `-'a'
)txt",
       R"txt(
PrefixUnaryOperatorExpression Expression
|-'~' OperatorToken
`-IdExpression Operand
  `-UnqualifiedId UnqualifiedId
    `-'a'
)txt",
       R"txt(
PrefixUnaryOperatorExpression Expression
|-'-' OperatorToken
`-IdExpression Operand
  `-UnqualifiedId UnqualifiedId
    `-'a'
)txt",
       R"txt(
PrefixUnaryOperatorExpression Expression
|-'+' OperatorToken
`-IdExpression Operand
  `-UnqualifiedId UnqualifiedId
    `-'a'
)txt",
       R"txt(
PrefixUnaryOperatorExpression Expression
|-'&' OperatorToken
`-IdExpression Operand
  `-UnqualifiedId UnqualifiedId
    `-'a'
)txt",
       R"txt(
PrefixUnaryOperatorExpression Expression
|-'*' OperatorToken
`-IdExpression Operand
  `-UnqualifiedId UnqualifiedId
    `-'ap'
)txt",
       R"txt(
PrefixUnaryOperatorExpression Expression
|-'!' OperatorToken
`-IdExpression Operand
  `-UnqualifiedId UnqualifiedId
    `-'a'
)txt",
       R"txt(
PrefixUnaryOperatorExpression Expression
|-'__real' OperatorToken
`-IdExpression Operand
  `-UnqualifiedId UnqualifiedId
    `-'a'
)txt",
       R"txt(
PrefixUnaryOperatorExpression Expression
|-'__imag' OperatorToken
`-IdExpression Operand
  `-UnqualifiedId UnqualifiedId
    `-'a'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, PrefixUnaryOperatorCxx) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test(int a, bool b) {
  [[compl a]];
  [[not b]];
}
)cpp",
      {R"txt(
PrefixUnaryOperatorExpression Expression
|-'compl' OperatorToken
`-IdExpression Operand
  `-UnqualifiedId UnqualifiedId
    `-'a'
)txt",
       R"txt(
PrefixUnaryOperatorExpression Expression
|-'not' OperatorToken
`-IdExpression Operand
  `-UnqualifiedId UnqualifiedId
    `-'b'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, BinaryOperator) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test(int a) {
  [[1 - 2]];
  [[1 == 2]];
  [[a = 1]];
  [[a <<= 1]];
  [[1 || 0]];
  [[1 & 2]];
  [[a != 3]];
}
)cpp",
      {R"txt(
BinaryOperatorExpression Expression
|-IntegerLiteralExpression LeftHandSide
| `-'1' LiteralToken
|-'-' OperatorToken
`-IntegerLiteralExpression RightHandSide
  `-'2' LiteralToken
)txt",
       R"txt(
BinaryOperatorExpression Expression
|-IntegerLiteralExpression LeftHandSide
| `-'1' LiteralToken
|-'==' OperatorToken
`-IntegerLiteralExpression RightHandSide
  `-'2' LiteralToken
)txt",
       R"txt(
BinaryOperatorExpression Expression
|-IdExpression LeftHandSide
| `-UnqualifiedId UnqualifiedId
|   `-'a'
|-'=' OperatorToken
`-IntegerLiteralExpression RightHandSide
  `-'1' LiteralToken
)txt",
       R"txt(
BinaryOperatorExpression Expression
|-IdExpression LeftHandSide
| `-UnqualifiedId UnqualifiedId
|   `-'a'
|-'<<=' OperatorToken
`-IntegerLiteralExpression RightHandSide
  `-'1' LiteralToken
)txt",
       R"txt(
BinaryOperatorExpression Expression
|-IntegerLiteralExpression LeftHandSide
| `-'1' LiteralToken
|-'||' OperatorToken
`-IntegerLiteralExpression RightHandSide
  `-'0' LiteralToken
)txt",
       R"txt(
BinaryOperatorExpression Expression
|-IntegerLiteralExpression LeftHandSide
| `-'1' LiteralToken
|-'&' OperatorToken
`-IntegerLiteralExpression RightHandSide
  `-'2' LiteralToken
)txt",
       R"txt(
BinaryOperatorExpression Expression
|-IdExpression LeftHandSide
| `-UnqualifiedId UnqualifiedId
|   `-'a'
|-'!=' OperatorToken
`-IntegerLiteralExpression RightHandSide
  `-'3' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, BinaryOperatorCxx) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test(int a) {
  [[true || false]];
  [[true or false]];
  [[1 bitand 2]];
  [[a xor_eq 3]];
}
)cpp",
      {R"txt(
BinaryOperatorExpression Expression
|-BoolLiteralExpression LeftHandSide
| `-'true' LiteralToken
|-'||' OperatorToken
`-BoolLiteralExpression RightHandSide
  `-'false' LiteralToken
)txt",
       R"txt(
BinaryOperatorExpression Expression
|-BoolLiteralExpression LeftHandSide
| `-'true' LiteralToken
|-'or' OperatorToken
`-BoolLiteralExpression RightHandSide
  `-'false' LiteralToken
)txt",
       R"txt(
BinaryOperatorExpression Expression
|-IntegerLiteralExpression LeftHandSide
| `-'1' LiteralToken
|-'bitand' OperatorToken
`-IntegerLiteralExpression RightHandSide
  `-'2' LiteralToken
)txt",
       R"txt(
BinaryOperatorExpression Expression
|-IdExpression LeftHandSide
| `-UnqualifiedId UnqualifiedId
|   `-'a'
|-'xor_eq' OperatorToken
`-IntegerLiteralExpression RightHandSide
  `-'3' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, BinaryOperator_NestedWithParenthesis) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  [[(1 + 2) * (4 / 2)]];
}
)cpp",
      {R"txt(
BinaryOperatorExpression Expression
|-ParenExpression LeftHandSide
| |-'(' OpenParen
| |-BinaryOperatorExpression SubExpression
| | |-IntegerLiteralExpression LeftHandSide
| | | `-'1' LiteralToken
| | |-'+' OperatorToken
| | `-IntegerLiteralExpression RightHandSide
| |   `-'2' LiteralToken
| `-')' CloseParen
|-'*' OperatorToken
`-ParenExpression RightHandSide
  |-'(' OpenParen
  |-BinaryOperatorExpression SubExpression
  | |-IntegerLiteralExpression LeftHandSide
  | | `-'4' LiteralToken
  | |-'/' OperatorToken
  | `-IntegerLiteralExpression RightHandSide
  |   `-'2' LiteralToken
  `-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, BinaryOperator_Associativity) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test(int a, int b) {
  [[a + b + 42]];
  [[a = b = 42]];
}
)cpp",
      {R"txt(
BinaryOperatorExpression Expression
|-BinaryOperatorExpression LeftHandSide
| |-IdExpression LeftHandSide
| | `-UnqualifiedId UnqualifiedId
| |   `-'a'
| |-'+' OperatorToken
| `-IdExpression RightHandSide
|   `-UnqualifiedId UnqualifiedId
|     `-'b'
|-'+' OperatorToken
`-IntegerLiteralExpression RightHandSide
  `-'42' LiteralToken
)txt",
       R"txt(
BinaryOperatorExpression Expression
|-IdExpression LeftHandSide
| `-UnqualifiedId UnqualifiedId
|   `-'a'
|-'=' OperatorToken
`-BinaryOperatorExpression RightHandSide
  |-IdExpression LeftHandSide
  | `-UnqualifiedId UnqualifiedId
  |   `-'b'
  |-'=' OperatorToken
  `-IntegerLiteralExpression RightHandSide
    `-'42' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, BinaryOperator_Precedence) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void test() {
  [[1 + 2 * 3 + 4]];
  [[1 % 2 + 3 * 4]];
}
)cpp",
      {R"txt(
BinaryOperatorExpression Expression
|-BinaryOperatorExpression LeftHandSide
| |-IntegerLiteralExpression LeftHandSide
| | `-'1' LiteralToken
| |-'+' OperatorToken
| `-BinaryOperatorExpression RightHandSide
|   |-IntegerLiteralExpression LeftHandSide
|   | `-'2' LiteralToken
|   |-'*' OperatorToken
|   `-IntegerLiteralExpression RightHandSide
|     `-'3' LiteralToken
|-'+' OperatorToken
`-IntegerLiteralExpression RightHandSide
  `-'4' LiteralToken
)txt",
       R"txt(
BinaryOperatorExpression Expression
|-BinaryOperatorExpression LeftHandSide
| |-IntegerLiteralExpression LeftHandSide
| | `-'1' LiteralToken
| |-'%' OperatorToken
| `-IntegerLiteralExpression RightHandSide
|   `-'2' LiteralToken
|-'+' OperatorToken
`-BinaryOperatorExpression RightHandSide
  |-IntegerLiteralExpression LeftHandSide
  | `-'3' LiteralToken
  |-'*' OperatorToken
  `-IntegerLiteralExpression RightHandSide
    `-'4' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, OverloadedOperator_Assignment) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {
  X& operator=(const X&);
};
void test(X x, X y) {
  [[x = y]];
}
)cpp",
      {R"txt(
BinaryOperatorExpression Expression
|-IdExpression LeftHandSide
| `-UnqualifiedId UnqualifiedId
|   `-'x'
|-'=' OperatorToken
`-IdExpression RightHandSide
  `-UnqualifiedId UnqualifiedId
    `-'y'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, OverloadedOperator_Plus) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {
  friend X operator+(X, const X&);
};
void test(X x, X y) {
  [[x + y]];
}
)cpp",
      {R"txt(
BinaryOperatorExpression Expression
|-IdExpression LeftHandSide
| `-UnqualifiedId UnqualifiedId
|   `-'x'
|-'+' OperatorToken
`-IdExpression RightHandSide
  `-UnqualifiedId UnqualifiedId
    `-'y'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, OverloadedOperator_Less) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {
  friend bool operator<(const X&, const X&);
};
void test(X x, X y) {
  [[x < y]];
}
)cpp",
      {R"txt(
BinaryOperatorExpression Expression
|-IdExpression LeftHandSide
| `-UnqualifiedId UnqualifiedId
|   `-'x'
|-'<' OperatorToken
`-IdExpression RightHandSide
  `-UnqualifiedId UnqualifiedId
    `-'y'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, OverloadedOperator_LeftShift) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {
  friend X operator<<(X&, const X&);
};
void test(X x, X y) {
  [[x << y]];
}
)cpp",
      {R"txt(
BinaryOperatorExpression Expression
|-IdExpression LeftHandSide
| `-UnqualifiedId UnqualifiedId
|   `-'x'
|-'<<' OperatorToken
`-IdExpression RightHandSide
  `-UnqualifiedId UnqualifiedId
    `-'y'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, OverloadedOperator_Comma) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {
  X operator,(X&);
};
void test(X x, X y) {
  [[x, y]];
}
)cpp",
      {R"txt(
BinaryOperatorExpression Expression
|-IdExpression LeftHandSide
| `-UnqualifiedId UnqualifiedId
|   `-'x'
|-',' OperatorToken
`-IdExpression RightHandSide
  `-UnqualifiedId UnqualifiedId
    `-'y'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, OverloadedOperator_PointerToMember) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {
  X operator->*(int);
};
void test(X* xp, int X::* pmi) {
  [[xp->*pmi]];
}
)cpp",
      {R"txt(
BinaryOperatorExpression Expression
|-IdExpression LeftHandSide
| `-UnqualifiedId UnqualifiedId
|   `-'xp'
|-'->*' OperatorToken
`-IdExpression RightHandSide
  `-UnqualifiedId UnqualifiedId
    `-'pmi'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, OverloadedOperator_Negation) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {
  bool operator!();
};
void test(X x) {
  [[!x]];
}
)cpp",
      {R"txt(
PrefixUnaryOperatorExpression Expression
|-'!' OperatorToken
`-IdExpression Operand
  `-UnqualifiedId UnqualifiedId
    `-'x'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, OverloadedOperator_AddressOf) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {
  X* operator&();
};
void test(X x) {
  [[&x]];
}
)cpp",
      {R"txt(
PrefixUnaryOperatorExpression Expression
|-'&' OperatorToken
`-IdExpression Operand
  `-UnqualifiedId UnqualifiedId
    `-'x'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, OverloadedOperator_PrefixIncrement) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {
  X operator++();
};
void test(X x) {
  [[++x]];
}
)cpp",
      {R"txt(
PrefixUnaryOperatorExpression Expression
|-'++' OperatorToken
`-IdExpression Operand
  `-UnqualifiedId UnqualifiedId
    `-'x'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, OverloadedOperator_PostfixIncrement) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {
  X operator++(int);
};
void test(X x) {
  [[x++]];
}
)cpp",
      {R"txt(
PostfixUnaryOperatorExpression Expression
|-IdExpression Operand
| `-UnqualifiedId UnqualifiedId
|   `-'x'
`-'++' OperatorToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, MemberExpression_SimpleWithDot) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S {
  int a;
};
void test(struct S s) {
  [[s.a]];
}
)cpp",
      {R"txt(
MemberExpression Expression
|-IdExpression Object
| `-UnqualifiedId UnqualifiedId
|   `-'s'
|-'.' AccessToken
`-IdExpression Member
  `-UnqualifiedId UnqualifiedId
    `-'a'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, MemberExpression_StaticDataMember) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S {
  static int a;
};
void test(S s) {
  [[s.a]];
}
)cpp",
      {R"txt(
MemberExpression Expression
|-IdExpression Object
| `-UnqualifiedId UnqualifiedId
|   `-'s'
|-'.' AccessToken
`-IdExpression Member
  `-UnqualifiedId UnqualifiedId
    `-'a'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, MemberExpression_SimpleWithArrow) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S {
  int a;
};
void test(struct S* sp) {
  [[sp->a]];
}
)cpp",
      {R"txt(
MemberExpression Expression
|-IdExpression Object
| `-UnqualifiedId UnqualifiedId
|   `-'sp'
|-'->' AccessToken
`-IdExpression Member
  `-UnqualifiedId UnqualifiedId
    `-'a'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, MemberExpression_Chaining) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S {
  struct S* next;
};
void test(struct S s){
  [[s.next->next]];
}
)cpp",
      {R"txt(
MemberExpression Expression
|-MemberExpression Object
| |-IdExpression Object
| | `-UnqualifiedId UnqualifiedId
| |   `-'s'
| |-'.' AccessToken
| `-IdExpression Member
|   `-UnqualifiedId UnqualifiedId
|     `-'next'
|-'->' AccessToken
`-IdExpression Member
  `-UnqualifiedId UnqualifiedId
    `-'next'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, MemberExpression_OperatorFunction) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S {
  bool operator!();
};
void test(S s) {
  [[s.operator!()]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-MemberExpression Callee
| |-IdExpression Object
| | `-UnqualifiedId UnqualifiedId
| |   `-'s'
| |-'.' AccessToken
| `-IdExpression Member
|   `-UnqualifiedId UnqualifiedId
|     |-'operator'
|     `-'!'
|-'(' OpenParen
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, MemberExpression_VariableTemplate) {
  if (!GetParam().isCXX14OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S {
  template<typename T>
  static constexpr T x = 42;
};
// FIXME: `<int>` should be a child of `MemberExpression` and `;` of
// `ExpressionStatement`. This is a bug in clang, in `getSourceRange` methods.
void test(S s) [[{
  s.x<int>;
}]]
)cpp",
      {R"txt(
CompoundStatement
|-'{' OpenParen
|-ExpressionStatement Statement
| `-MemberExpression Expression
|   |-IdExpression Object
|   | `-UnqualifiedId UnqualifiedId
|   |   `-'s'
|   |-'.' AccessToken
|   `-IdExpression Member
|     `-UnqualifiedId UnqualifiedId
|       `-'x'
|-'<'
|-'int'
|-'>'
|-';'
`-'}' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, MemberExpression_FunctionTemplate) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S {
  template<typename T>
  T f();
};
void test(S* sp){
  [[sp->f<int>()]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-MemberExpression Callee
| |-IdExpression Object
| | `-UnqualifiedId UnqualifiedId
| |   `-'sp'
| |-'->' AccessToken
| `-IdExpression Member
|   `-UnqualifiedId UnqualifiedId
|     |-'f'
|     |-'<'
|     |-'int'
|     `-'>'
|-'(' OpenParen
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest,
       MemberExpression_FunctionTemplateWithTemplateKeyword) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S {
  template<typename T>
  T f();
};
void test(S s){
  [[s.template f<int>()]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-MemberExpression Callee
| |-IdExpression Object
| | `-UnqualifiedId UnqualifiedId
| |   `-'s'
| |-'.' AccessToken
| |-'template'
| `-IdExpression Member
|   `-UnqualifiedId UnqualifiedId
|     |-'f'
|     |-'<'
|     |-'int'
|     `-'>'
|-'(' OpenParen
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, MemberExpression_WithQualifier) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct Base {
  void f();
};
struct S : public Base {};
void test(S s){
  [[s.Base::f()]];
  [[s.::S::~S()]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-MemberExpression Callee
| |-IdExpression Object
| | `-UnqualifiedId UnqualifiedId
| |   `-'s'
| |-'.' AccessToken
| `-IdExpression Member
|   |-NestedNameSpecifier Qualifier
|   | |-IdentifierNameSpecifier ListElement
|   | | `-'Base'
|   | `-'::' ListDelimiter
|   `-UnqualifiedId UnqualifiedId
|     `-'f'
|-'(' OpenParen
`-')' CloseParen
      )txt",
       R"txt(
CallExpression Expression
|-MemberExpression Callee
| |-IdExpression Object
| | `-UnqualifiedId UnqualifiedId
| |   `-'s'
| |-'.' AccessToken
| `-IdExpression Member
|   |-NestedNameSpecifier Qualifier
|   | |-'::' ListDelimiter
|   | |-IdentifierNameSpecifier ListElement
|   | | `-'S'
|   | `-'::' ListDelimiter
|   `-UnqualifiedId UnqualifiedId
|     |-'~'
|     `-'S'
|-'(' OpenParen
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, MemberExpression_Complex) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
template<typename T>
struct U {
  template<typename U>
  U f();
};
struct S {
  U<int> getU();
};
void test(S* sp) {
  // FIXME: The first 'template' keyword is a child of `NestedNameSpecifier`,
  // but it should be a child of `MemberExpression` according to the grammar.
  // However one might argue that the 'template' keyword fits better inside
  // `NestedNameSpecifier` because if we change `U<int>` to `UI` we would like
  // equally to change the `NameSpecifier` `template U<int>` to just `UI`.
  [[sp->getU().template U<int>::template f<int>()]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-MemberExpression Callee
| |-CallExpression Object
| | |-MemberExpression Callee
| | | |-IdExpression Object
| | | | `-UnqualifiedId UnqualifiedId
| | | |   `-'sp'
| | | |-'->' AccessToken
| | | `-IdExpression Member
| | |   `-UnqualifiedId UnqualifiedId
| | |     `-'getU'
| | |-'(' OpenParen
| | `-')' CloseParen
| |-'.' AccessToken
| `-IdExpression Member
|   |-NestedNameSpecifier Qualifier
|   | |-SimpleTemplateNameSpecifier ListElement
|   | | |-'template'
|   | | |-'U'
|   | | |-'<'
|   | | |-'int'
|   | | `-'>'
|   | `-'::' ListDelimiter
|   |-'template' TemplateKeyword
|   `-UnqualifiedId UnqualifiedId
|     |-'f'
|     |-'<'
|     |-'int'
|     `-'>'
|-'(' OpenParen
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, CallExpression_Callee_Member) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S{
  void f();
};
void test(S s) {
  [[s.f()]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-MemberExpression Callee
| |-IdExpression Object
| | `-UnqualifiedId UnqualifiedId
| |   `-'s'
| |-'.' AccessToken
| `-IdExpression Member
|   `-UnqualifiedId UnqualifiedId
|     `-'f'
|-'(' OpenParen
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, CallExpression_Callee_OperatorParens) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S {
  void operator()();
};
void test(S s) {
  [[s()]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-IdExpression Callee
| `-UnqualifiedId UnqualifiedId
|   `-'s'
|-'(' OpenParen
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, CallExpression_Callee_OperatorParensChaining) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S {
  S operator()();
};
void test(S s) {
  [[s()()]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-CallExpression Callee
| |-IdExpression Callee
| | `-UnqualifiedId UnqualifiedId
| |   `-'s'
| |-'(' OpenParen
| `-')' CloseParen
|-'(' OpenParen
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, CallExpression_Callee_MemberWithThis) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct Base {
  void f();
};
struct S: public Base {
  void f();
  void test() {
    [[this->f()]];
    [[f()]];
    [[this->Base::f()]];
  }
};
)cpp",
      {R"txt(
CallExpression Expression
|-MemberExpression Callee
| |-ThisExpression Object
| | `-'this' IntroducerKeyword
| |-'->' AccessToken
| `-IdExpression Member
|   `-UnqualifiedId UnqualifiedId
|     `-'f'
|-'(' OpenParen
`-')' CloseParen
      )txt",
       R"txt(
CallExpression Expression
|-IdExpression Callee
| `-UnqualifiedId UnqualifiedId
|   `-'f'
|-'(' OpenParen
`-')' CloseParen
      )txt",
       R"txt(
CallExpression Expression
|-MemberExpression Callee
| |-ThisExpression Object
| | `-'this' IntroducerKeyword
| |-'->' AccessToken
| `-IdExpression Member
|   |-NestedNameSpecifier Qualifier
|   | |-IdentifierNameSpecifier ListElement
|   | | `-'Base'
|   | `-'::' ListDelimiter
|   `-UnqualifiedId UnqualifiedId
|     `-'f'
|-'(' OpenParen
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, CallExpression_Callee_FunctionPointer) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void (*pf)();
void test() {
  [[pf()]];
  [[(*pf)()]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-IdExpression Callee
| `-UnqualifiedId UnqualifiedId
|   `-'pf'
|-'(' OpenParen
`-')' CloseParen
)txt",
       R"txt(
CallExpression Expression
|-ParenExpression Callee
| |-'(' OpenParen
| |-PrefixUnaryOperatorExpression SubExpression
| | |-'*' OperatorToken
| | `-IdExpression Operand
| |   `-UnqualifiedId UnqualifiedId
| |     `-'pf'
| `-')' CloseParen
|-'(' OpenParen
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, CallExpression_Callee_MemberFunctionPointer) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S {
  void f();
};
void test(S s) {
  void (S::*pmf)();
  pmf = &S::f;
  [[(s.*pmf)()]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-ParenExpression Callee
| |-'(' OpenParen
| |-BinaryOperatorExpression SubExpression
| | |-IdExpression LeftHandSide
| | | `-UnqualifiedId UnqualifiedId
| | |   `-'s'
| | |-'.*' OperatorToken
| | `-IdExpression RightHandSide
| |   `-UnqualifiedId UnqualifiedId
| |     `-'pmf'
| `-')' CloseParen
|-'(' OpenParen
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, CallExpression_Arguments_Zero) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void f();
void test() {
  [[f();]]
}
)cpp",
      {R"txt(
ExpressionStatement Statement
|-CallExpression Expression
| |-IdExpression Callee
| | `-UnqualifiedId UnqualifiedId
| |   `-'f'
| |-'(' OpenParen
| `-')' CloseParen
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, CallExpression_Arguments_One) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void f(int);
void test() {
  [[f(1);]]
}
)cpp",
      {R"txt(
ExpressionStatement Statement
|-CallExpression Expression
| |-IdExpression Callee
| | `-UnqualifiedId UnqualifiedId
| |   `-'f'
| |-'(' OpenParen
| |-CallArguments Arguments
| | `-IntegerLiteralExpression ListElement
| |   `-'1' LiteralToken
| `-')' CloseParen
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, CallExpression_Arguments_Multiple) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void f(int, char, float);
void test() {
  [[f(1, '2', 3.);]]
}
)cpp",
      {R"txt(
ExpressionStatement Statement
|-CallExpression Expression
| |-IdExpression Callee
| | `-UnqualifiedId UnqualifiedId
| |   `-'f'
| |-'(' OpenParen
| |-CallArguments Arguments
| | |-IntegerLiteralExpression ListElement
| | | `-'1' LiteralToken
| | |-',' ListDelimiter
| | |-CharacterLiteralExpression ListElement
| | | `-''2'' LiteralToken
| | |-',' ListDelimiter
| | `-FloatingLiteralExpression ListElement
| |   `-'3.' LiteralToken
| `-')' CloseParen
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, CallExpression_Arguments_Assignment) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void f(int);
void test(int a) {
  [[f(a = 1);]]
}
)cpp",
      {R"txt(
ExpressionStatement Statement
|-CallExpression Expression
| |-IdExpression Callee
| | `-UnqualifiedId UnqualifiedId
| |   `-'f'
| |-'(' OpenParen
| |-CallArguments Arguments
| | `-BinaryOperatorExpression ListElement
| |   |-IdExpression LeftHandSide
| |   | `-UnqualifiedId UnqualifiedId
| |   |   `-'a'
| |   |-'=' OperatorToken
| |   `-IntegerLiteralExpression RightHandSide
| |     `-'1' LiteralToken
| `-')' CloseParen
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, CallExpression_Arguments_BracedInitList_Empty) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void f(int[]);
void test() {
  [[f({});]]
}
)cpp",
      {R"txt(
ExpressionStatement Statement
|-CallExpression Expression
| |-IdExpression Callee
| | `-UnqualifiedId UnqualifiedId
| |   `-'f'
| |-'(' OpenParen
| |-CallArguments Arguments
| | `-UnknownExpression ListElement
| |   `-UnknownExpression
| |     |-'{'
| |     `-'}'
| `-')' CloseParen
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, CallExpression_Arguments_BracedInitList_Simple) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct TT {};
struct T{
  int a;
  TT b;
};
void f(T);
void test() {
  [[f({1, {}});]]
}
)cpp",
      {R"txt(
ExpressionStatement Statement
|-CallExpression Expression
| |-IdExpression Callee
| | `-UnqualifiedId UnqualifiedId
| |   `-'f'
| |-'(' OpenParen
| |-CallArguments Arguments
| | `-UnknownExpression ListElement
| |   `-UnknownExpression
| |     |-'{'
| |     |-IntegerLiteralExpression
| |     | `-'1' LiteralToken
| |     |-','
| |     |-UnknownExpression
| |     | `-UnknownExpression
| |     |   |-'{'
| |     |   `-'}'
| |     `-'}'
| `-')' CloseParen
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest,
       CallExpression_Arguments_BracedInitList_Designated) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct TT {};
struct T{
  int a;
  TT b;
};
void f(T);
void test() {
  [[f({.a = 1, .b {}});]]
}
)cpp",
      {R"txt(
ExpressionStatement Statement
|-CallExpression Expression
| |-IdExpression Callee
| | `-UnqualifiedId UnqualifiedId
| |   `-'f'
| |-'(' OpenParen
| |-CallArguments Arguments
| | `-UnknownExpression ListElement
| |   `-UnknownExpression
| |     |-'{'
| |     |-UnknownExpression
| |     | |-'.'
| |     | |-'a'
| |     | |-'='
| |     | `-IntegerLiteralExpression
| |     |   `-'1' LiteralToken
| |     |-','
| |     |-UnknownExpression
| |     | |-'.'
| |     | |-'b'
| |     | `-UnknownExpression
| |     |   `-UnknownExpression
| |     |     |-'{'
| |     |     `-'}'
| |     `-'}'
| `-')' CloseParen
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, CallExpression_Arguments_ParameterPack) {
  if (!GetParam().isCXX11OrLater() || GetParam().hasDelayedTemplateParsing()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
template<typename T, typename... Args>
void test(T t, Args... args) {
  [[test(args...)]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-UnknownExpression Callee
| `-'test'
|-'(' OpenParen
|-CallArguments Arguments
| `-UnknownExpression ListElement
|   |-IdExpression
|   | `-UnqualifiedId UnqualifiedId
|   |   `-'args'
|   `-'...'
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, CallExpression_DefaultArguments) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
void f(int i = 1, char c = '2');
void test() {
  [[f()]];
  [[f(1)]];
  [[f(1, '2')]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-IdExpression Callee
| `-UnqualifiedId UnqualifiedId
|   `-'f'
|-'(' OpenParen
`-')' CloseParen
      )txt",
       R"txt(
CallExpression Expression
|-IdExpression Callee
| `-UnqualifiedId UnqualifiedId
|   `-'f'
|-'(' OpenParen
|-CallArguments Arguments
| `-IntegerLiteralExpression ListElement
|   `-'1' LiteralToken
`-')' CloseParen
      )txt",
       R"txt(
CallExpression Expression
|-IdExpression Callee
| `-UnqualifiedId UnqualifiedId
|   `-'f'
|-'(' OpenParen
|-CallArguments Arguments
| |-IntegerLiteralExpression ListElement
| | `-'1' LiteralToken
| |-',' ListDelimiter
| `-CharacterLiteralExpression ListElement
|   `-''2'' LiteralToken
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, MultipleDeclaratorsGrouping) {
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
int *a, b;
int *c, d;
)cpp",
      R"txt(
TranslationUnit Detached
|-SimpleDeclaration
| |-'int'
| |-DeclaratorList Declarators
| | |-SimpleDeclarator ListElement
| | | |-'*'
| | | `-'a'
| | |-',' ListDelimiter
| | `-SimpleDeclarator ListElement
| |   `-'b'
| `-';'
`-SimpleDeclaration
  |-'int'
  |-DeclaratorList Declarators
  | |-SimpleDeclarator ListElement
  | | |-'*'
  | | `-'c'
  | |-',' ListDelimiter
  | `-SimpleDeclarator ListElement
  |   `-'d'
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, MultipleDeclaratorsGroupingTypedef) {
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
typedef int *a, b;
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'typedef'
  |-'int'
  |-DeclaratorList Declarators
  | |-SimpleDeclarator ListElement
  | | |-'*'
  | | `-'a'
  | |-',' ListDelimiter
  | `-SimpleDeclarator ListElement
  |   `-'b'
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, MultipleDeclaratorsInsideStatement) {
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
void foo() {
  int *a, b;
  typedef int *ta, tb;
}
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'void'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'foo'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     `-')' CloseParen
  `-CompoundStatement
    |-'{' OpenParen
    |-DeclarationStatement Statement
    | |-SimpleDeclaration
    | | |-'int'
    | | `-DeclaratorList Declarators
    | |   |-SimpleDeclarator ListElement
    | |   | |-'*'
    | |   | `-'a'
    | |   |-',' ListDelimiter
    | |   `-SimpleDeclarator ListElement
    | |     `-'b'
    | `-';'
    |-DeclarationStatement Statement
    | |-SimpleDeclaration
    | | |-'typedef'
    | | |-'int'
    | | `-DeclaratorList Declarators
    | |   |-SimpleDeclarator ListElement
    | |   | |-'*'
    | |   | `-'ta'
    | |   |-',' ListDelimiter
    | |   `-SimpleDeclarator ListElement
    | |     `-'tb'
    | `-';'
    `-'}' CloseParen
)txt"));
}

TEST_P(BuildSyntaxTreeTest, SizeTTypedef) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
typedef decltype(sizeof(void *)) size_t;
    )cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'typedef'
  |-'decltype'
  |-'('
  |-UnknownExpression
  | |-'sizeof'
  | |-'('
  | |-'void'
  | |-'*'
  | `-')'
  |-')'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   `-'size_t'
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, Namespace_Nested) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
namespace a { namespace b {} }
)cpp",
      R"txt(
TranslationUnit Detached
`-NamespaceDefinition
  |-'namespace'
  |-'a'
  |-'{'
  |-NamespaceDefinition
  | |-'namespace'
  | |-'b'
  | |-'{'
  | `-'}'
  `-'}'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, Namespace_NestedDefinition) {
  if (!GetParam().isCXX17OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
namespace a::b {}
)cpp",
      R"txt(
TranslationUnit Detached
`-NamespaceDefinition
  |-'namespace'
  |-'a'
  |-'::'
  |-'b'
  |-'{'
  `-'}'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, Namespace_Unnamed) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
namespace {}
)cpp",
      R"txt(
TranslationUnit Detached
`-NamespaceDefinition
  |-'namespace'
  |-'{'
  `-'}'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, Namespace_Alias) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
namespace a {}
[[namespace foo = a;]]
)cpp",
      {R"txt(
NamespaceAliasDefinition
|-'namespace'
|-'foo'
|-'='
|-'a'
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, UsingDirective) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
namespace ns {}
[[using namespace ::ns;]]
)cpp",
      {R"txt(
UsingNamespaceDirective
|-'using'
|-'namespace'
|-NestedNameSpecifier
| `-'::' ListDelimiter
|-'ns'
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, UsingDeclaration_Namespace) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
namespace ns { int a; }
[[using ns::a;]]
)cpp",
      {R"txt(
UsingDeclaration
|-'using'
|-NestedNameSpecifier
| |-IdentifierNameSpecifier ListElement
| | `-'ns'
| `-'::' ListDelimiter
|-'a'
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, UsingDeclaration_ClassMember) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
template <class T> struct X {
  [[using T::foo;]]
  [[using typename T::bar;]]
};
)cpp",
      {R"txt(
UsingDeclaration
|-'using'
|-NestedNameSpecifier
| |-IdentifierNameSpecifier ListElement
| | `-'T'
| `-'::' ListDelimiter
|-'foo'
`-';'
)txt",
       R"txt(
UsingDeclaration
|-'using'
|-'typename'
|-NestedNameSpecifier
| |-IdentifierNameSpecifier ListElement
| | `-'T'
| `-'::' ListDelimiter
|-'bar'
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, UsingTypeAlias) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
using type = int;
)cpp",
      R"txt(
TranslationUnit Detached
`-TypeAliasDeclaration
  |-'using'
  |-'type'
  |-'='
  |-'int'
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, FreeStandingClass_ForwardDeclaration) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
[[struct X;]]
[[struct Y *y1;]]
)cpp",
      {R"txt(
SimpleDeclaration
|-'struct'
|-'X'
`-';'
)txt",
       R"txt(
SimpleDeclaration
|-'struct'
|-'Y'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-'*'
|   `-'y1'
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, FreeStandingClasses_Definition) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
[[struct X {};]]
[[struct Y {} *y2;]]
[[struct {} *a1;]]
)cpp",
      {R"txt(
SimpleDeclaration
|-'struct'
|-'X'
|-'{'
|-'}'
`-';'
)txt",
       R"txt(
SimpleDeclaration
|-'struct'
|-'Y'
|-'{'
|-'}'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-'*'
|   `-'y2'
`-';'
)txt",
       R"txt(
SimpleDeclaration
|-'struct'
|-'{'
|-'}'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-'*'
|   `-'a1'
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, StaticMemberFunction) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S {
  [[static void f(){}]]
};
)cpp",
      {R"txt(
SimpleDeclaration
|-'static'
|-'void'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-'f'
|   `-ParametersAndQualifiers
|     |-'(' OpenParen
|     `-')' CloseParen
`-CompoundStatement
  |-'{' OpenParen
  `-'}' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, OutOfLineMemberFunctionDefinition) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S {
  void f();
};
[[void S::f(){}]]
)cpp",
      {R"txt(
SimpleDeclaration
|-'void'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-NestedNameSpecifier
|   | |-IdentifierNameSpecifier ListElement
|   | | `-'S'
|   | `-'::' ListDelimiter
|   |-'f'
|   `-ParametersAndQualifiers
|     |-'(' OpenParen
|     `-')' CloseParen
`-CompoundStatement
  |-'{' OpenParen
  `-'}' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, ConversionMemberFunction) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {
  [[operator int();]]
};
)cpp",
      {R"txt(
SimpleDeclaration
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-'operator'
|   |-'int'
|   `-ParametersAndQualifiers
|     |-'(' OpenParen
|     `-')' CloseParen
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, LiteralOperatorDeclaration) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
unsigned operator "" _c(char);
    )cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'unsigned'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'operator'
  |   |-'""'
  |   |-'_c'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     |-ParameterDeclarationList Parameters
  |     | `-SimpleDeclaration ListElement
  |     |   `-'char'
  |     `-')' CloseParen
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, NumericLiteralOperatorTemplateDeclaration) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
template <char...>
unsigned operator "" _t();
    )cpp",
      R"txt(
TranslationUnit Detached
`-TemplateDeclaration Declaration
  |-'template' IntroducerKeyword
  |-'<'
  |-SimpleDeclaration
  | `-'char'
  |-'...'
  |-'>'
  `-SimpleDeclaration
    |-'unsigned'
    |-DeclaratorList Declarators
    | `-SimpleDeclarator ListElement
    |   |-'operator'
    |   |-'""'
    |   |-'_t'
    |   `-ParametersAndQualifiers
    |     |-'(' OpenParen
    |     `-')' CloseParen
    `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, OverloadedOperatorDeclaration) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {
  [[X& operator=(const X&);]]
};
)cpp",
      {R"txt(
SimpleDeclaration
|-'X'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-'&'
|   |-'operator'
|   |-'='
|   `-ParametersAndQualifiers
|     |-'(' OpenParen
|     |-ParameterDeclarationList Parameters
|     | `-SimpleDeclaration ListElement
|     |   |-'const'
|     |   |-'X'
|     |   `-DeclaratorList Declarators
|     |     `-SimpleDeclarator ListElement
|     |       `-'&'
|     `-')' CloseParen
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, OverloadedOperatorFriendDeclaration) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {
  [[friend X operator+(X, const X&);]]
};
)cpp",
      {R"txt(
UnknownDeclaration
`-SimpleDeclaration
  |-'friend'
  |-'X'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'operator'
  |   |-'+'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     |-ParameterDeclarationList Parameters
  |     | |-SimpleDeclaration ListElement
  |     | | `-'X'
  |     | |-',' ListDelimiter
  |     | `-SimpleDeclaration ListElement
  |     |   |-'const'
  |     |   |-'X'
  |     |   `-DeclaratorList Declarators
  |     |     `-SimpleDeclarator ListElement
  |     |       `-'&'
  |     `-')' CloseParen
  `-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, ClassTemplateDeclaration) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
template<typename T>
struct ST {};
)cpp",
      R"txt(
TranslationUnit Detached
`-TemplateDeclaration Declaration
  |-'template' IntroducerKeyword
  |-'<'
  |-UnknownDeclaration
  | |-'typename'
  | `-'T'
  |-'>'
  `-SimpleDeclaration
    |-'struct'
    |-'ST'
    |-'{'
    |-'}'
    `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, FunctionTemplateDeclaration) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
template<typename T>
T f();
)cpp",
      R"txt(
TranslationUnit Detached
`-TemplateDeclaration Declaration
  |-'template' IntroducerKeyword
  |-'<'
  |-UnknownDeclaration
  | |-'typename'
  | `-'T'
  |-'>'
  `-SimpleDeclaration
    |-'T'
    |-DeclaratorList Declarators
    | `-SimpleDeclarator ListElement
    |   |-'f'
    |   `-ParametersAndQualifiers
    |     |-'(' OpenParen
    |     `-')' CloseParen
    `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, VariableTemplateDeclaration) {
  if (!GetParam().isCXX14OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
template <class T> T var = 10;
)cpp",
      R"txt(
TranslationUnit Detached
`-TemplateDeclaration Declaration
  |-'template' IntroducerKeyword
  |-'<'
  |-UnknownDeclaration
  | |-'class'
  | `-'T'
  |-'>'
  `-SimpleDeclaration
    |-'T'
    |-DeclaratorList Declarators
    | `-SimpleDeclarator ListElement
    |   |-'var'
    |   |-'='
    |   `-IntegerLiteralExpression
    |     `-'10' LiteralToken
    `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, StaticMemberFunctionTemplate) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S {
  [[template<typename U>
  static U f();]]
};
)cpp",
      {R"txt(
TemplateDeclaration Declaration
|-'template' IntroducerKeyword
|-'<'
|-UnknownDeclaration
| |-'typename'
| `-'U'
|-'>'
`-SimpleDeclaration
  |-'static'
  |-'U'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'f'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     `-')' CloseParen
  `-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, NestedTemplates) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
template <class T>
struct X {
  template <class U>
  U foo();
};
)cpp",
      R"txt(
TranslationUnit Detached
`-TemplateDeclaration Declaration
  |-'template' IntroducerKeyword
  |-'<'
  |-UnknownDeclaration
  | |-'class'
  | `-'T'
  |-'>'
  `-SimpleDeclaration
    |-'struct'
    |-'X'
    |-'{'
    |-TemplateDeclaration Declaration
    | |-'template' IntroducerKeyword
    | |-'<'
    | |-UnknownDeclaration
    | | |-'class'
    | | `-'U'
    | |-'>'
    | `-SimpleDeclaration
    |   |-'U'
    |   |-DeclaratorList Declarators
    |   | `-SimpleDeclarator ListElement
    |   |   |-'foo'
    |   |   `-ParametersAndQualifiers
    |   |     |-'(' OpenParen
    |   |     `-')' CloseParen
    |   `-';'
    |-'}'
    `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, NestedTemplatesInNamespace) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
namespace n {
  template<typename T>
  struct ST {
    template<typename U>
    static U f();
  };
}
)cpp",
      R"txt(
TranslationUnit Detached
`-NamespaceDefinition
  |-'namespace'
  |-'n'
  |-'{'
  |-TemplateDeclaration Declaration
  | |-'template' IntroducerKeyword
  | |-'<'
  | |-UnknownDeclaration
  | | |-'typename'
  | | `-'T'
  | |-'>'
  | `-SimpleDeclaration
  |   |-'struct'
  |   |-'ST'
  |   |-'{'
  |   |-TemplateDeclaration Declaration
  |   | |-'template' IntroducerKeyword
  |   | |-'<'
  |   | |-UnknownDeclaration
  |   | | |-'typename'
  |   | | `-'U'
  |   | |-'>'
  |   | `-SimpleDeclaration
  |   |   |-'static'
  |   |   |-'U'
  |   |   |-DeclaratorList Declarators
  |   |   | `-SimpleDeclarator ListElement
  |   |   |   |-'f'
  |   |   |   `-ParametersAndQualifiers
  |   |   |     |-'(' OpenParen
  |   |   |     `-')' CloseParen
  |   |   `-';'
  |   |-'}'
  |   `-';'
  `-'}'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, ClassTemplate_MemberClassDefinition) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
template <class T> struct X { struct Y; };
[[template <class T> struct X<T>::Y {};]]
)cpp",
      {R"txt(
TemplateDeclaration Declaration
|-'template' IntroducerKeyword
|-'<'
|-UnknownDeclaration
| |-'class'
| `-'T'
|-'>'
`-SimpleDeclaration
  |-'struct'
  |-NestedNameSpecifier
  | |-SimpleTemplateNameSpecifier ListElement
  | | |-'X'
  | | |-'<'
  | | |-'T'
  | | `-'>'
  | `-'::' ListDelimiter
  |-'Y'
  |-'{'
  |-'}'
  `-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, ExplicitClassTemplateInstantiation_Definition) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
template <class T> struct X {};
[[template struct X<double>;]]
)cpp",
      {R"txt(
ExplicitTemplateInstantiation
|-'template' IntroducerKeyword
`-SimpleDeclaration Declaration
  |-'struct'
  |-'X'
  |-'<'
  |-'double'
  |-'>'
  `-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, ExplicitClassTemplateInstantiation_Declaration) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
template <class T> struct X {};
[[extern template struct X<float>;]]
)cpp",
      {R"txt(
ExplicitTemplateInstantiation
|-'extern' ExternKeyword
|-'template' IntroducerKeyword
`-SimpleDeclaration Declaration
  |-'struct'
  |-'X'
  |-'<'
  |-'float'
  |-'>'
  `-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, ClassTemplateSpecialization_Partial) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
template <class T> struct X {};
[[template <class T> struct X<T*> {};]]
)cpp",
      {R"txt(
TemplateDeclaration Declaration
|-'template' IntroducerKeyword
|-'<'
|-UnknownDeclaration
| |-'class'
| `-'T'
|-'>'
`-SimpleDeclaration
  |-'struct'
  |-'X'
  |-'<'
  |-'T'
  |-'*'
  |-'>'
  |-'{'
  |-'}'
  `-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, ClassTemplateSpecialization_Full) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
template <class T> struct X {};
[[template <> struct X<int> {};]]
)cpp",
      {R"txt(
TemplateDeclaration Declaration
|-'template' IntroducerKeyword
|-'<'
|-'>'
`-SimpleDeclaration
  |-'struct'
  |-'X'
  |-'<'
  |-'int'
  |-'>'
  |-'{'
  |-'}'
  `-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, EmptyDeclaration) {
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
;
)cpp",
      R"txt(
TranslationUnit Detached
`-EmptyDeclaration
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, StaticAssert) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
static_assert(true, "message");
)cpp",
      R"txt(
TranslationUnit Detached
`-StaticAssertDeclaration
  |-'static_assert'
  |-'('
  |-BoolLiteralExpression Condition
  | `-'true' LiteralToken
  |-','
  |-StringLiteralExpression Message
  | `-'"message"' LiteralToken
  |-')'
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, StaticAssert_WithoutMessage) {
  if (!GetParam().isCXX17OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
static_assert(true);
)cpp",
      R"txt(
TranslationUnit Detached
`-StaticAssertDeclaration
  |-'static_assert'
  |-'('
  |-BoolLiteralExpression Condition
  | `-'true' LiteralToken
  |-')'
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, ExternC) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
extern "C" int a;
extern "C" { int b; int c; }
)cpp",
      R"txt(
TranslationUnit Detached
|-LinkageSpecificationDeclaration
| |-'extern'
| |-'"C"'
| `-SimpleDeclaration
|   |-'int'
|   |-DeclaratorList Declarators
|   | `-SimpleDeclarator ListElement
|   |   `-'a'
|   `-';'
`-LinkageSpecificationDeclaration
  |-'extern'
  |-'"C"'
  |-'{'
  |-SimpleDeclaration
  | |-'int'
  | |-DeclaratorList Declarators
  | | `-SimpleDeclarator ListElement
  | |   `-'b'
  | `-';'
  |-SimpleDeclaration
  | |-'int'
  | |-DeclaratorList Declarators
  | | `-SimpleDeclarator ListElement
  | |   `-'c'
  | `-';'
  `-'}'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, Macro_ObjectLike_Leaf) {
  // All nodes can be mutated.
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
#define OPEN {
#define CLOSE }

void test() {
  OPEN
    1;
  CLOSE

  OPEN
    2;
  }
}
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'void'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'test'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     `-')' CloseParen
  `-CompoundStatement
    |-'{' OpenParen
    |-CompoundStatement Statement
    | |-'{' OpenParen
    | |-ExpressionStatement Statement
    | | |-IntegerLiteralExpression Expression
    | | | `-'1' LiteralToken
    | | `-';'
    | `-'}' CloseParen
    |-CompoundStatement Statement
    | |-'{' OpenParen
    | |-ExpressionStatement Statement
    | | |-IntegerLiteralExpression Expression
    | | | `-'2' LiteralToken
    | | `-';'
    | `-'}' CloseParen
    `-'}' CloseParen
)txt"));
}

TEST_P(BuildSyntaxTreeTest, Macro_ObjectLike_MatchTree) {
  // Some nodes are unmodifiable, they are marked with 'unmodifiable'.
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
#define BRACES {}

void test() BRACES
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'void'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'test'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     `-')' CloseParen
  `-CompoundStatement
    |-'{' OpenParen unmodifiable
    `-'}' CloseParen unmodifiable
)txt"));
}

TEST_P(BuildSyntaxTreeTest, Macro_ObjectLike_MismatchTree) {
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
#define HALF_IF if (1+
#define HALF_IF_2 1) {}
void test() {
  HALF_IF HALF_IF_2 else {}
})cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'void'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'test'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     `-')' CloseParen
  `-CompoundStatement
    |-'{' OpenParen
    |-IfStatement Statement
    | |-'if' IntroducerKeyword unmodifiable
    | |-'(' unmodifiable
    | |-ExpressionStatement Condition unmodifiable
    | | `-BinaryOperatorExpression Expression unmodifiable
    | |   |-IntegerLiteralExpression LeftHandSide unmodifiable
    | |   | `-'1' LiteralToken unmodifiable
    | |   |-'+' OperatorToken unmodifiable
    | |   `-IntegerLiteralExpression RightHandSide unmodifiable
    | |     `-'1' LiteralToken unmodifiable
    | |-')' unmodifiable
    | |-CompoundStatement ThenStatement unmodifiable
    | | |-'{' OpenParen unmodifiable
    | | `-'}' CloseParen unmodifiable
    | |-'else' ElseKeyword
    | `-CompoundStatement ElseStatement
    |   |-'{' OpenParen
    |   `-'}' CloseParen
    `-'}' CloseParen
)txt"));
}

TEST_P(BuildSyntaxTreeTest, Macro_FunctionLike_ModifiableArguments) {
  // FIXME: Note that the substitutions for `X` and `Y` are marked modifiable.
  // However we cannot change `X` freely. Indeed if we change its substitution
  // in the condition we should also change it the then-branch.
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
#define MIN(X,Y) X < Y ? X : Y

void test() {
  MIN(1,2);
}
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'void'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'test'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     `-')' CloseParen
  `-CompoundStatement
    |-'{' OpenParen
    |-ExpressionStatement Statement
    | |-UnknownExpression Expression
    | | |-BinaryOperatorExpression unmodifiable
    | | | |-IntegerLiteralExpression LeftHandSide
    | | | | `-'1' LiteralToken
    | | | |-'<' OperatorToken unmodifiable
    | | | `-IntegerLiteralExpression RightHandSide
    | | |   `-'2' LiteralToken
    | | |-'?' unmodifiable
    | | |-IntegerLiteralExpression
    | | | `-'1' LiteralToken
    | | |-':' unmodifiable
    | | `-IntegerLiteralExpression
    | |   `-'2' LiteralToken
    | `-';'
    `-'}' CloseParen
)txt"));
}

TEST_P(BuildSyntaxTreeTest, Macro_FunctionLike_MismatchTree) {
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
#define HALF_IF(X) if (X &&
#define HALF_IF_2(Y) Y) {}
void test() {
  HALF_IF(1) HALF_IF_2(0) else {}
})cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'void'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'test'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     `-')' CloseParen
  `-CompoundStatement
    |-'{' OpenParen
    |-IfStatement Statement
    | |-'if' IntroducerKeyword unmodifiable
    | |-'(' unmodifiable
    | |-ExpressionStatement Condition unmodifiable
    | | `-BinaryOperatorExpression Expression unmodifiable
    | |   |-IntegerLiteralExpression LeftHandSide
    | |   | `-'1' LiteralToken
    | |   |-'&&' OperatorToken unmodifiable
    | |   `-IntegerLiteralExpression RightHandSide
    | |     `-'0' LiteralToken
    | |-')' unmodifiable
    | |-CompoundStatement ThenStatement unmodifiable
    | | |-'{' OpenParen unmodifiable
    | | `-'}' CloseParen unmodifiable
    | |-'else' ElseKeyword
    | `-CompoundStatement ElseStatement
    |   |-'{' OpenParen
    |   `-'}' CloseParen
    `-'}' CloseParen
)txt"));
}

TEST_P(BuildSyntaxTreeTest, Macro_FunctionLike_Variadic) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
#define CALL(F_NAME, ...) F_NAME(__VA_ARGS__)

void f(int);
void g(int, int);
void test() [[{
  CALL(f, 0);
  CALL(g, 0, 1);
}]]
)cpp",
      {R"txt(
CompoundStatement
|-'{' OpenParen
|-ExpressionStatement Statement
| |-CallExpression Expression
| | |-IdExpression Callee
| | | `-UnqualifiedId UnqualifiedId
| | |   `-'f'
| | |-'(' OpenParen unmodifiable
| | |-CallArguments Arguments
| | | `-IntegerLiteralExpression ListElement
| | |   `-'0' LiteralToken
| | `-')' CloseParen unmodifiable
| `-';'
|-ExpressionStatement Statement
| |-CallExpression Expression
| | |-IdExpression Callee
| | | `-UnqualifiedId UnqualifiedId
| | |   `-'g'
| | |-'(' OpenParen unmodifiable
| | |-CallArguments Arguments
| | | |-IntegerLiteralExpression ListElement
| | | | `-'0' LiteralToken
| | | |-',' ListDelimiter
| | | `-IntegerLiteralExpression ListElement
| | |   `-'1' LiteralToken
| | `-')' CloseParen unmodifiable
| `-';'
`-'}' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, InitDeclarator_Equal) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S { S(int);};
void test() {
  [[S s = 1]];
}
)cpp",
      {R"txt(
SimpleDeclaration
|-'S'
`-DeclaratorList Declarators
  `-SimpleDeclarator ListElement
    |-'s'
    |-'='
    `-IntegerLiteralExpression
      `-'1' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, InitDeclarator_Brace) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S { 
  S();
  S(int);
  S(int, float);
};
void test(){
  // FIXME: 's...' is a declarator and '{...}' is initializer
  [[S s0{}]];
  [[S s1{1}]];
  [[S s2{1, 2.}]];
}
)cpp",
      {R"txt(
SimpleDeclaration
|-'S'
`-DeclaratorList Declarators
  `-SimpleDeclarator ListElement
    `-UnknownExpression
      |-'s0'
      |-'{'
      `-'}'
  )txt",
       R"txt(
SimpleDeclaration
|-'S'
`-DeclaratorList Declarators
  `-SimpleDeclarator ListElement
    `-UnknownExpression
      |-'s1'
      |-'{'
      |-IntegerLiteralExpression
      | `-'1' LiteralToken
      `-'}'
  )txt",
       R"txt(
SimpleDeclaration
|-'S'
`-DeclaratorList Declarators
  `-SimpleDeclarator ListElement
    `-UnknownExpression
      |-'s2'
      |-'{'
      |-IntegerLiteralExpression
      | `-'1' LiteralToken
      |-','
      |-FloatingLiteralExpression
      | `-'2.' LiteralToken
      `-'}'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, InitDeclarator_EqualBrace) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S { 
  S();
  S(int);
  S(int, float);
};
void test() {
  // FIXME: '= {...}' is initializer
  [[S s0 = {}]];
  [[S s1 = {1}]];
  [[S s2 = {1, 2.}]];
}
)cpp",
      {R"txt(
SimpleDeclaration
|-'S'
`-DeclaratorList Declarators
  `-SimpleDeclarator ListElement
    |-'s0'
    |-'='
    `-UnknownExpression
      |-'{'
      `-'}'
  )txt",
       R"txt(
SimpleDeclaration
|-'S'
`-DeclaratorList Declarators
  `-SimpleDeclarator ListElement
    |-'s1'
    |-'='
    `-UnknownExpression
      |-'{'
      |-IntegerLiteralExpression
      | `-'1' LiteralToken
      `-'}'
  )txt",
       R"txt(
SimpleDeclaration
|-'S'
`-DeclaratorList Declarators
  `-SimpleDeclarator ListElement
    |-'s2'
    |-'='
    `-UnknownExpression
      |-'{'
      |-IntegerLiteralExpression
      | `-'1' LiteralToken
      |-','
      |-FloatingLiteralExpression
      | `-'2.' LiteralToken
      `-'}'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, InitDeclarator_Paren) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S {
  S(int);
  S(int, float);
};
// FIXME: 's...' is a declarator and '(...)' is initializer
[[S s1(1);]]
[[S s2(1, 2.);]]
)cpp",
      {R"txt(
SimpleDeclaration
|-'S'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   `-UnknownExpression
|     |-'s1'
|     |-'('
|     |-IntegerLiteralExpression
|     | `-'1' LiteralToken
|     `-')'
`-';'
  )txt",
       R"txt(
SimpleDeclaration
|-'S'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   `-UnknownExpression
|     |-'s2'
|     |-'('
|     |-IntegerLiteralExpression
|     | `-'1' LiteralToken
|     |-','
|     |-FloatingLiteralExpression
|     | `-'2.' LiteralToken
|     `-')'
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, InitDeclarator_Paren_DefaultArguments) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct S {
  S(int i = 1, float = 2.);
};
[[S s0;]]
// FIXME: 's...' is a declarator and '(...)' is initializer
[[S s1(1);]]
[[S s2(1, 2.);]]
)cpp",
      {R"txt(
SimpleDeclaration
|-'S'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   `-'s0'
`-';'
  )txt",
       R"txt(
SimpleDeclaration
|-'S'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   `-UnknownExpression
|     |-'s1'
|     |-'('
|     |-IntegerLiteralExpression
|     | `-'1' LiteralToken
|     `-')'
`-';'
  )txt",
       R"txt(
SimpleDeclaration
|-'S'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   `-UnknownExpression
|     |-'s2'
|     |-'('
|     |-IntegerLiteralExpression
|     | `-'1' LiteralToken
|     |-','
|     |-FloatingLiteralExpression
|     | `-'2.' LiteralToken
|     `-')'
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, ImplicitConversion_Argument) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {
  X(int);
};
void TakeX(const X&);
void test() {
  [[TakeX(1)]];
}
)cpp",
      {R"txt(
CallExpression Expression
|-IdExpression Callee
| `-UnqualifiedId UnqualifiedId
|   `-'TakeX'
|-'(' OpenParen
|-CallArguments Arguments
| `-IntegerLiteralExpression ListElement
|   `-'1' LiteralToken
`-')' CloseParen
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, ImplicitConversion_Return) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {
  X(int);
};
X CreateX(){
  [[return 1;]]
}
)cpp",
      {R"txt(
ReturnStatement Statement
|-'return' IntroducerKeyword
|-IntegerLiteralExpression ReturnValue
| `-'1' LiteralToken
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, ConstructorCall_ZeroArguments) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {
  X();
};
X test() {
  [[return X();]]
}
)cpp",
      {R"txt(
ReturnStatement Statement
|-'return' IntroducerKeyword
|-UnknownExpression ReturnValue
| |-'X'
| |-'('
| `-')'
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, ConstructorCall_OneArgument) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {
  X(int);
};
X test() {
  [[return X(1);]]
}
)cpp",
      {R"txt(
ReturnStatement Statement
|-'return' IntroducerKeyword
|-UnknownExpression ReturnValue
| |-'X'
| |-'('
| |-IntegerLiteralExpression
| | `-'1' LiteralToken
| `-')'
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, ConstructorCall_MultipleArguments) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {
  X(int, char);
};
X test() {
  [[return X(1, '2');]]
}
)cpp",
      {R"txt(
ReturnStatement Statement
|-'return' IntroducerKeyword
|-UnknownExpression ReturnValue
| |-'X'
| |-'('
| |-IntegerLiteralExpression
| | `-'1' LiteralToken
| |-','
| |-CharacterLiteralExpression
| | `-''2'' LiteralToken
| `-')'
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, ConstructorCall_DefaultArguments) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {
  X(int i = 1, char c = '2');
};
X test() {
  auto x0 = [[X()]];
  auto x1 = [[X(1)]];
  auto x2 = [[X(1, '2')]];
}
)cpp",
      {R"txt(
UnknownExpression
|-'X'
|-'('
`-')'
)txt",
       R"txt(
UnknownExpression
|-'X'
|-'('
|-IntegerLiteralExpression
| `-'1' LiteralToken
`-')'
)txt",
       R"txt(
UnknownExpression
|-'X'
|-'('
|-IntegerLiteralExpression
| `-'1' LiteralToken
|-','
|-CharacterLiteralExpression
| `-''2'' LiteralToken
`-')'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, TypeConversion_FunctionalNotation) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
float test() {
  [[return float(1);]]
}
)cpp",
      {R"txt(
ReturnStatement Statement
|-'return' IntroducerKeyword
|-UnknownExpression ReturnValue
| |-'float'
| |-'('
| |-IntegerLiteralExpression
| | `-'1' LiteralToken
| `-')'
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, ArrayDeclarator_Simple) {
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
int a[10];
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'int'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'a'
  |   `-ArraySubscript
  |     |-'[' OpenParen
  |     |-IntegerLiteralExpression Size
  |     | `-'10' LiteralToken
  |     `-']' CloseParen
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, ArrayDeclarator_Multidimensional) {
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
int b[1][2][3];
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'int'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'b'
  |   |-ArraySubscript
  |   | |-'[' OpenParen
  |   | |-IntegerLiteralExpression Size
  |   | | `-'1' LiteralToken
  |   | `-']' CloseParen
  |   |-ArraySubscript
  |   | |-'[' OpenParen
  |   | |-IntegerLiteralExpression Size
  |   | | `-'2' LiteralToken
  |   | `-']' CloseParen
  |   `-ArraySubscript
  |     |-'[' OpenParen
  |     |-IntegerLiteralExpression Size
  |     | `-'3' LiteralToken
  |     `-']' CloseParen
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, ArrayDeclarator_UnknownBound) {
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
int c[] = {1,2,3};
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'int'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'c'
  |   |-ArraySubscript
  |   | |-'[' OpenParen
  |   | `-']' CloseParen
  |   |-'='
  |   `-UnknownExpression
  |     `-UnknownExpression
  |       |-'{'
  |       |-IntegerLiteralExpression
  |       | `-'1' LiteralToken
  |       |-','
  |       |-IntegerLiteralExpression
  |       | `-'2' LiteralToken
  |       |-','
  |       |-IntegerLiteralExpression
  |       | `-'3' LiteralToken
  |       `-'}'
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, ArrayDeclarator_Static) {
  if (!GetParam().isC99OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
void f(int xs[static 10]);
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'void'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'f'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     |-ParameterDeclarationList Parameters
  |     | `-SimpleDeclaration ListElement
  |     |   |-'int'
  |     |   `-DeclaratorList Declarators
  |     |     `-SimpleDeclarator ListElement
  |     |       |-'xs'
  |     |       `-ArraySubscript
  |     |         |-'[' OpenParen
  |     |         |-'static'
  |     |         |-IntegerLiteralExpression Size
  |     |         | `-'10' LiteralToken
  |     |         `-']' CloseParen
  |     `-')' CloseParen
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, ParametersAndQualifiers_InFreeFunctions_Empty) {
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
int func();
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'int'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'func'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     `-')' CloseParen
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, ParametersAndQualifiers_InFreeFunctions_Named) {
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
     int func1([[int a]]);
     int func2([[int *ap]]);
     int func3([[int a, float b]]);
     int func4([[undef a]]); // error-ok: no crash on invalid type
   )cpp",
      {R"txt(
ParameterDeclarationList Parameters
`-SimpleDeclaration ListElement
  |-'int'
  `-DeclaratorList Declarators
    `-SimpleDeclarator ListElement
      `-'a'
)txt",
       R"txt(
ParameterDeclarationList Parameters
`-SimpleDeclaration ListElement
  |-'int'
  `-DeclaratorList Declarators
    `-SimpleDeclarator ListElement
      |-'*'
      `-'ap'
)txt",
       R"txt(
ParameterDeclarationList Parameters
|-SimpleDeclaration ListElement
| |-'int'
| `-DeclaratorList Declarators
|   `-SimpleDeclarator ListElement
|     `-'a'
|-',' ListDelimiter
`-SimpleDeclaration ListElement
  |-'float'
  `-DeclaratorList Declarators
    `-SimpleDeclarator ListElement
      `-'b'
)txt",
       R"txt(
ParameterDeclarationList Parameters
`-SimpleDeclaration ListElement
  |-'undef'
  `-DeclaratorList Declarators
    `-SimpleDeclarator ListElement
      `-'a'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, ParametersAndQualifiers_InFreeFunctions_Unnamed) {
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
int func1(int);
int func2(int *);
int func3(int, float);
)cpp",
      R"txt(
TranslationUnit Detached
|-SimpleDeclaration
| |-'int'
| |-DeclaratorList Declarators
| | `-SimpleDeclarator ListElement
| |   |-'func1'
| |   `-ParametersAndQualifiers
| |     |-'(' OpenParen
| |     |-ParameterDeclarationList Parameters
| |     | `-SimpleDeclaration ListElement
| |     |   `-'int'
| |     `-')' CloseParen
| `-';'
|-SimpleDeclaration
| |-'int'
| |-DeclaratorList Declarators
| | `-SimpleDeclarator ListElement
| |   |-'func2'
| |   `-ParametersAndQualifiers
| |     |-'(' OpenParen
| |     |-ParameterDeclarationList Parameters
| |     | `-SimpleDeclaration ListElement
| |     |   |-'int'
| |     |   `-DeclaratorList Declarators
| |     |     `-SimpleDeclarator ListElement
| |     |       `-'*'
| |     `-')' CloseParen
| `-';'
`-SimpleDeclaration
  |-'int'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'func3'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     |-ParameterDeclarationList Parameters
  |     | |-SimpleDeclaration ListElement
  |     | | `-'int'
  |     | |-',' ListDelimiter
  |     | `-SimpleDeclaration ListElement
  |     |   `-'float'
  |     `-')' CloseParen
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest,
       ParametersAndQualifiers_InFreeFunctions_Default_One) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
int func1([[int a = 1]]);
)cpp",
      {R"txt(
ParameterDeclarationList Parameters
`-SimpleDeclaration ListElement
  |-'int'
  `-DeclaratorList Declarators
    `-SimpleDeclarator ListElement
      |-'a'
      |-'='
      `-IntegerLiteralExpression
        `-'1' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest,
       ParametersAndQualifiers_InFreeFunctions_Default_Multiple) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
int func2([[int *ap, int a = 1, char c = '2']]);
)cpp",
      {R"txt(
ParameterDeclarationList Parameters
|-SimpleDeclaration ListElement
| |-'int'
| `-DeclaratorList Declarators
|   `-SimpleDeclarator ListElement
|     |-'*'
|     `-'ap'
|-',' ListDelimiter
|-SimpleDeclaration ListElement
| |-'int'
| `-DeclaratorList Declarators
|   `-SimpleDeclarator ListElement
|     |-'a'
|     |-'='
|     `-IntegerLiteralExpression
|       `-'1' LiteralToken
|-',' ListDelimiter
`-SimpleDeclaration ListElement
  |-'char'
  `-DeclaratorList Declarators
    `-SimpleDeclarator ListElement
      |-'c'
      |-'='
      `-CharacterLiteralExpression
        `-''2'' LiteralToken
)txt"}));
}

TEST_P(BuildSyntaxTreeTest,
       ParametersAndQualifiers_InVariadicFunctionTemplate_ParameterPack) {
  if (!GetParam().isCXX11OrLater() || GetParam().hasDelayedTemplateParsing()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
template<typename T, typename... Args>
[[void test(T , Args... );]]
)cpp",
      {R"txt(
SimpleDeclaration
|-'void'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-'test'
|   `-ParametersAndQualifiers
|     |-'(' OpenParen
|     |-ParameterDeclarationList Parameters
|     | |-SimpleDeclaration ListElement
|     | | `-'T'
|     | |-',' ListDelimiter
|     | `-SimpleDeclaration ListElement
|     |   |-'Args'
|     |   `-'...'
|     `-')' CloseParen
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest,
       ParametersAndQualifiers_InVariadicFunctionTemplate_NamedParameterPack) {
  if (!GetParam().isCXX11OrLater() || GetParam().hasDelayedTemplateParsing()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
template<typename T, typename... Args>
[[void test(T t, Args... args);]]
)cpp",
      {R"txt(
SimpleDeclaration
|-'void'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-'test'
|   `-ParametersAndQualifiers
|     |-'(' OpenParen
|     |-ParameterDeclarationList Parameters
|     | |-SimpleDeclaration ListElement
|     | | |-'T'
|     | | `-DeclaratorList Declarators
|     | |   `-SimpleDeclarator ListElement
|     | |     `-'t'
|     | |-',' ListDelimiter
|     | `-SimpleDeclaration ListElement
|     |   |-'Args'
|     |   |-'...'
|     |   `-DeclaratorList Declarators
|     |     `-SimpleDeclarator ListElement
|     |       `-'args'
|     `-')' CloseParen
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest,
       ParametersAndQualifiers_InFreeFunctions_VariadicArguments) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
void test(int , char ...);
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'void'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'test'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     |-ParameterDeclarationList Parameters
  |     | |-SimpleDeclaration ListElement
  |     | | `-'int'
  |     | |-',' ListDelimiter
  |     | `-SimpleDeclaration ListElement
  |     |   `-'char'
  |     |-'...'
  |     `-')' CloseParen
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest,
       ParametersAndQualifiers_InFreeFunctions_Cxx_CvQualifiers) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
int func(const int a, volatile int b, const volatile int c);
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'int'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'func'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     |-ParameterDeclarationList Parameters
  |     | |-SimpleDeclaration ListElement
  |     | | |-'const'
  |     | | |-'int'
  |     | | `-DeclaratorList Declarators
  |     | |   `-SimpleDeclarator ListElement
  |     | |     `-'a'
  |     | |-',' ListDelimiter
  |     | |-SimpleDeclaration ListElement
  |     | | |-'volatile'
  |     | | |-'int'
  |     | | `-DeclaratorList Declarators
  |     | |   `-SimpleDeclarator ListElement
  |     | |     `-'b'
  |     | |-',' ListDelimiter
  |     | `-SimpleDeclaration ListElement
  |     |   |-'const'
  |     |   |-'volatile'
  |     |   |-'int'
  |     |   `-DeclaratorList Declarators
  |     |     `-SimpleDeclarator ListElement
  |     |       `-'c'
  |     `-')' CloseParen
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, ParametersAndQualifiers_InFreeFunctions_Cxx_Ref) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
int func(int& a);
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'int'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'func'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     |-ParameterDeclarationList Parameters
  |     | `-SimpleDeclaration ListElement
  |     |   |-'int'
  |     |   `-DeclaratorList Declarators
  |     |     `-SimpleDeclarator ListElement
  |     |       |-'&'
  |     |       `-'a'
  |     `-')' CloseParen
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest,
       ParametersAndQualifiers_InFreeFunctions_Cxx11_RefRef) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
int func(int&& a);
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'int'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'func'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     |-ParameterDeclarationList Parameters
  |     | `-SimpleDeclaration ListElement
  |     |   |-'int'
  |     |   `-DeclaratorList Declarators
  |     |     `-SimpleDeclarator ListElement
  |     |       |-'&&'
  |     |       `-'a'
  |     `-')' CloseParen
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, ParametersAndQualifiers_InMemberFunctions_Simple) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
struct Test {
  int a();
};
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'struct'
  |-'Test'
  |-'{'
  |-SimpleDeclaration
  | |-'int'
  | |-DeclaratorList Declarators
  | | `-SimpleDeclarator ListElement
  | |   |-'a'
  | |   `-ParametersAndQualifiers
  | |     |-'(' OpenParen
  | |     `-')' CloseParen
  | `-';'
  |-'}'
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest,
       ParametersAndQualifiers_InMemberFunctions_CvQualifiers) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct Test {
  [[int b() const;]]
  [[int c() volatile;]]
  [[int d() const volatile;]]
};
)cpp",
      {R"txt(
SimpleDeclaration
|-'int'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-'b'
|   `-ParametersAndQualifiers
|     |-'(' OpenParen
|     |-')' CloseParen
|     `-'const'
`-';'
)txt",
       R"txt(
SimpleDeclaration
|-'int'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-'c'
|   `-ParametersAndQualifiers
|     |-'(' OpenParen
|     |-')' CloseParen
|     `-'volatile'
`-';'
)txt",
       R"txt(
SimpleDeclaration
|-'int'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-'d'
|   `-ParametersAndQualifiers
|     |-'(' OpenParen
|     |-')' CloseParen
|     |-'const'
|     `-'volatile'
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, ParametersAndQualifiers_InMemberFunctions_Ref) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct Test {
  [[int e() &;]]
};
)cpp",
      {R"txt(
SimpleDeclaration
|-'int'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-'e'
|   `-ParametersAndQualifiers
|     |-'(' OpenParen
|     |-')' CloseParen
|     `-'&'
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, ParametersAndQualifiers_InMemberFunctions_RefRef) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct Test {
  [[int f() &&;]]
};
)cpp",
      {R"txt(
SimpleDeclaration
|-'int'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-'f'
|   `-ParametersAndQualifiers
|     |-'(' OpenParen
|     |-')' CloseParen
|     `-'&&'
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, TrailingReturn) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
auto foo() -> int;
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'auto'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'foo'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     |-')' CloseParen
  |     `-TrailingReturnType TrailingReturn
  |       |-'->' ArrowToken
  |       `-'int'
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, DynamicExceptionSpecification) {
  if (!GetParam().supportsCXXDynamicExceptionSpecification()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct MyException1 {};
struct MyException2 {};
[[int a() throw();]]
[[int b() throw(...);]]
[[int c() throw(MyException1);]]
[[int d() throw(MyException1, MyException2);]]
)cpp",
      {R"txt(
SimpleDeclaration
|-'int'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-'a'
|   `-ParametersAndQualifiers
|     |-'(' OpenParen
|     |-')' CloseParen
|     |-'throw'
|     |-'('
|     `-')'
`-';'
)txt",
       R"txt(
SimpleDeclaration
|-'int'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-'b'
|   `-ParametersAndQualifiers
|     |-'(' OpenParen
|     |-')' CloseParen
|     |-'throw'
|     |-'('
|     |-'...'
|     `-')'
`-';'
)txt",
       R"txt(
SimpleDeclaration
|-'int'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-'c'
|   `-ParametersAndQualifiers
|     |-'(' OpenParen
|     |-')' CloseParen
|     |-'throw'
|     |-'('
|     |-'MyException1'
|     `-')'
`-';'
)txt",
       R"txt(
SimpleDeclaration
|-'int'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-'d'
|   `-ParametersAndQualifiers
|     |-'(' OpenParen
|     |-')' CloseParen
|     |-'throw'
|     |-'('
|     |-'MyException1'
|     |-','
|     |-'MyException2'
|     `-')'
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, NoexceptExceptionSpecification) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
int a() noexcept;
int b() noexcept(true);
)cpp",
      R"txt(
TranslationUnit Detached
|-SimpleDeclaration
| |-'int'
| |-DeclaratorList Declarators
| | `-SimpleDeclarator ListElement
| |   |-'a'
| |   `-ParametersAndQualifiers
| |     |-'(' OpenParen
| |     |-')' CloseParen
| |     `-'noexcept'
| `-';'
`-SimpleDeclaration
  |-'int'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'b'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     |-')' CloseParen
  |     |-'noexcept'
  |     |-'('
  |     |-BoolLiteralExpression
  |     | `-'true' LiteralToken
  |     `-')'
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, DeclaratorsInParentheses) {
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
int (a);
int *(b);
int (*c)(int);
int *(d)(int);
)cpp",
      R"txt(
TranslationUnit Detached
|-SimpleDeclaration
| |-'int'
| |-DeclaratorList Declarators
| | `-SimpleDeclarator ListElement
| |   `-ParenDeclarator
| |     |-'(' OpenParen
| |     |-'a'
| |     `-')' CloseParen
| `-';'
|-SimpleDeclaration
| |-'int'
| |-DeclaratorList Declarators
| | `-SimpleDeclarator ListElement
| |   |-'*'
| |   `-ParenDeclarator
| |     |-'(' OpenParen
| |     |-'b'
| |     `-')' CloseParen
| `-';'
|-SimpleDeclaration
| |-'int'
| |-DeclaratorList Declarators
| | `-SimpleDeclarator ListElement
| |   |-ParenDeclarator
| |   | |-'(' OpenParen
| |   | |-'*'
| |   | |-'c'
| |   | `-')' CloseParen
| |   `-ParametersAndQualifiers
| |     |-'(' OpenParen
| |     |-ParameterDeclarationList Parameters
| |     | `-SimpleDeclaration ListElement
| |     |   `-'int'
| |     `-')' CloseParen
| `-';'
`-SimpleDeclaration
  |-'int'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'*'
  |   |-ParenDeclarator
  |   | |-'(' OpenParen
  |   | |-'d'
  |   | `-')' CloseParen
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     |-ParameterDeclarationList Parameters
  |     | `-SimpleDeclaration ListElement
  |     |   `-'int'
  |     `-')' CloseParen
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, Declaration_ConstVolatileQualifiers_SimpleConst) {
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
const int west = -1;
int const east = 1;
)cpp",
      R"txt(
TranslationUnit Detached
|-SimpleDeclaration
| |-'const'
| |-'int'
| |-DeclaratorList Declarators
| | `-SimpleDeclarator ListElement
| |   |-'west'
| |   |-'='
| |   `-PrefixUnaryOperatorExpression
| |     |-'-' OperatorToken
| |     `-IntegerLiteralExpression Operand
| |       `-'1' LiteralToken
| `-';'
`-SimpleDeclaration
  |-'int'
  |-'const'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'east'
  |   |-'='
  |   `-IntegerLiteralExpression
  |     `-'1' LiteralToken
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, Declaration_ConstVolatileQualifiers_MultipleConst) {
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
const int const universal = 0;
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'const'
  |-'int'
  |-'const'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'universal'
  |   |-'='
  |   `-IntegerLiteralExpression
  |     `-'0' LiteralToken
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest,
       Declaration_ConstVolatileQualifiers_ConstAndVolatile) {
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
const int const *const *volatile b;
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'const'
  |-'int'
  |-'const'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'*'
  |   |-'const'
  |   |-'*'
  |   |-'volatile'
  |   `-'b'
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, RangesOfDeclaratorsWithTrailingReturnTypes) {
  if (!GetParam().isCXX11OrLater()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
auto foo() -> auto(*)(int) -> double*;
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'auto'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'foo'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     |-')' CloseParen
  |     `-TrailingReturnType TrailingReturn
  |       |-'->' ArrowToken
  |       |-'auto'
  |       `-SimpleDeclarator Declarator
  |         |-ParenDeclarator
  |         | |-'(' OpenParen
  |         | |-'*'
  |         | `-')' CloseParen
  |         `-ParametersAndQualifiers
  |           |-'(' OpenParen
  |           |-ParameterDeclarationList Parameters
  |           | `-SimpleDeclaration ListElement
  |           |   `-'int'
  |           |-')' CloseParen
  |           `-TrailingReturnType TrailingReturn
  |             |-'->' ArrowToken
  |             |-'double'
  |             `-SimpleDeclarator Declarator
  |               `-'*'
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, MemberPointers) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {};
[[int X::* a;]]
[[const int X::* b;]]
)cpp",
      {R"txt(
SimpleDeclaration
|-'int'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-MemberPointer
|   | |-'X'
|   | |-'::'
|   | `-'*'
|   `-'a'
`-';'
)txt",
       R"txt(
SimpleDeclaration
|-'const'
|-'int'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-MemberPointer
|   | |-'X'
|   | |-'::'
|   | `-'*'
|   `-'b'
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, MemberFunctionPointer) {
  if (!GetParam().isCXX()) {
    return;
  }
  EXPECT_TRUE(treeDumpEqualOnAnnotations(
      R"cpp(
struct X {
  struct Y {};
};
[[void (X::*xp)();]]
[[void (X::**xpp)(const int*);]]
// FIXME: Generate the right syntax tree for this type,
// i.e. create a syntax node for the outer member pointer
[[void (X::Y::*xyp)(const int*, char);]]
)cpp",
      {R"txt(
SimpleDeclaration
|-'void'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-ParenDeclarator
|   | |-'(' OpenParen
|   | |-MemberPointer
|   | | |-'X'
|   | | |-'::'
|   | | `-'*'
|   | |-'xp'
|   | `-')' CloseParen
|   `-ParametersAndQualifiers
|     |-'(' OpenParen
|     `-')' CloseParen
`-';'
)txt",
       R"txt(
SimpleDeclaration
|-'void'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-ParenDeclarator
|   | |-'(' OpenParen
|   | |-MemberPointer
|   | | |-'X'
|   | | |-'::'
|   | | `-'*'
|   | |-'*'
|   | |-'xpp'
|   | `-')' CloseParen
|   `-ParametersAndQualifiers
|     |-'(' OpenParen
|     |-ParameterDeclarationList Parameters
|     | `-SimpleDeclaration ListElement
|     |   |-'const'
|     |   |-'int'
|     |   `-DeclaratorList Declarators
|     |     `-SimpleDeclarator ListElement
|     |       `-'*'
|     `-')' CloseParen
`-';'
)txt",
       R"txt(
SimpleDeclaration
|-'void'
|-DeclaratorList Declarators
| `-SimpleDeclarator ListElement
|   |-ParenDeclarator
|   | |-'(' OpenParen
|   | |-'X'
|   | |-'::'
|   | |-MemberPointer
|   | | |-'Y'
|   | | |-'::'
|   | | `-'*'
|   | |-'xyp'
|   | `-')' CloseParen
|   `-ParametersAndQualifiers
|     |-'(' OpenParen
|     |-ParameterDeclarationList Parameters
|     | |-SimpleDeclaration ListElement
|     | | |-'const'
|     | | |-'int'
|     | | `-DeclaratorList Declarators
|     | |   `-SimpleDeclarator ListElement
|     | |     `-'*'
|     | |-',' ListDelimiter
|     | `-SimpleDeclaration ListElement
|     |   `-'char'
|     `-')' CloseParen
`-';'
)txt"}));
}

TEST_P(BuildSyntaxTreeTest, ComplexDeclarator) {
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
void x(char a, short (*b)(int));
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'void'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'x'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     |-ParameterDeclarationList Parameters
  |     | |-SimpleDeclaration ListElement
  |     | | |-'char'
  |     | | `-DeclaratorList Declarators
  |     | |   `-SimpleDeclarator ListElement
  |     | |     `-'a'
  |     | |-',' ListDelimiter
  |     | `-SimpleDeclaration ListElement
  |     |   |-'short'
  |     |   `-DeclaratorList Declarators
  |     |     `-SimpleDeclarator ListElement
  |     |       |-ParenDeclarator
  |     |       | |-'(' OpenParen
  |     |       | |-'*'
  |     |       | |-'b'
  |     |       | `-')' CloseParen
  |     |       `-ParametersAndQualifiers
  |     |         |-'(' OpenParen
  |     |         |-ParameterDeclarationList Parameters
  |     |         | `-SimpleDeclaration ListElement
  |     |         |   `-'int'
  |     |         `-')' CloseParen
  |     `-')' CloseParen
  `-';'
)txt"));
}

TEST_P(BuildSyntaxTreeTest, ComplexDeclarator2) {
  EXPECT_TRUE(treeDumpEqual(
      R"cpp(
void x(char a, short (*b)(int), long (**c)(long long));
)cpp",
      R"txt(
TranslationUnit Detached
`-SimpleDeclaration
  |-'void'
  |-DeclaratorList Declarators
  | `-SimpleDeclarator ListElement
  |   |-'x'
  |   `-ParametersAndQualifiers
  |     |-'(' OpenParen
  |     |-ParameterDeclarationList Parameters
  |     | |-SimpleDeclaration ListElement
  |     | | |-'char'
  |     | | `-DeclaratorList Declarators
  |     | |   `-SimpleDeclarator ListElement
  |     | |     `-'a'
  |     | |-',' ListDelimiter
  |     | |-SimpleDeclaration ListElement
  |     | | |-'short'
  |     | | `-DeclaratorList Declarators
  |     | |   `-SimpleDeclarator ListElement
  |     | |     |-ParenDeclarator
  |     | |     | |-'(' OpenParen
  |     | |     | |-'*'
  |     | |     | |-'b'
  |     | |     | `-')' CloseParen
  |     | |     `-ParametersAndQualifiers
  |     | |       |-'(' OpenParen
  |     | |       |-ParameterDeclarationList Parameters
  |     | |       | `-SimpleDeclaration ListElement
  |     | |       |   `-'int'
  |     | |       `-')' CloseParen
  |     | |-',' ListDelimiter
  |     | `-SimpleDeclaration ListElement
  |     |   |-'long'
  |     |   `-DeclaratorList Declarators
  |     |     `-SimpleDeclarator ListElement
  |     |       |-ParenDeclarator
  |     |       | |-'(' OpenParen
  |     |       | |-'*'
  |     |       | |-'*'
  |     |       | |-'c'
  |     |       | `-')' CloseParen
  |     |       `-ParametersAndQualifiers
  |     |         |-'(' OpenParen
  |     |         |-ParameterDeclarationList Parameters
  |     |         | `-SimpleDeclaration ListElement
  |     |         |   |-'long'
  |     |         |   `-'long'
  |     |         `-')' CloseParen
  |     `-')' CloseParen
  `-';'
)txt"));
}

} // namespace