text: //===-- DumpASTTests.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
  //
  //===----------------------------------------------------------------------===//
  
  #include "Annotations.h"
  #include "DumpAST.h"
  #include "TestTU.h"
  #include "llvm/Support/ScopedPrinter.h"
  #include "gmock/gmock.h"
  #include "gtest/gtest.h"
  
  namespace clang {
  namespace clangd {
  namespace {
  using testing::Contains;
  using testing::Not;
  using testing::SizeIs;
  
  MATCHER_P(WithDetail, str, "") { return arg.detail == str; }
  
  TEST(DumpASTTests, BasicInfo) {
    std::pair</*Code=*/std::string, /*Expected=*/std::string> Cases[] = {
        {R"cpp(
  float root(int *x) {
    return *x + 1;
  }
        )cpp",
         R"(
  declaration: Function - root
    type: FunctionProto
      type: Builtin - float
      declaration: ParmVar - x
        type: Pointer
          type: Builtin - int
    statement: Compound
      statement: Return
        expression: ImplicitCast - IntegralToFloating
          expression: BinaryOperator - +
            expression: ImplicitCast - LValueToRValue
              expression: UnaryOperator - *
                expression: ImplicitCast - LValueToRValue
                  expression: DeclRef - x
            expression: IntegerLiteral - 1
        )"},
        {R"cpp(
  namespace root {
  struct S { static const int x = 0; };
  int y = S::x + root::S().x;
  }
        )cpp",
         R"(
  declaration: Namespace - root
    declaration: CXXRecord - S
      declaration: Var - x
        type: Qualified - const
          type: Builtin - int
        expression: IntegerLiteral - 0
      declaration: CXXConstructor
      declaration: CXXConstructor
      declaration: CXXConstructor
      declaration: CXXDestructor
    declaration: Var - y
      type: Builtin - int
      expression: ExprWithCleanups
        expression: BinaryOperator - +
          expression: ImplicitCast - LValueToRValue
            expression: DeclRef - x
              specifier: TypeSpec
                type: Record - S
          expression: ImplicitCast - LValueToRValue
            expression: Member - x
              expression: MaterializeTemporary - rvalue
                expression: CXXTemporaryObject - S
                  type: Elaborated
                    specifier: Namespace - root::
                    type: Record - S
        )"},
        {R"cpp(
  namespace root {
  template <typename T> int tmpl() {
    (void)tmpl<unsigned>();
    return T::value;
  }
  }
        )cpp",
         R"(
  declaration: Namespace - root
    declaration: FunctionTemplate - tmpl
      declaration: TemplateTypeParm - T
      declaration: Function - tmpl
        type: FunctionProto
          type: Builtin - int
        statement: Compound
          expression: CStyleCast - ToVoid
            type: Builtin - void
            expression: Call
              expression: ImplicitCast - FunctionToPointerDecay
                expression: DeclRef - tmpl
                  template argument: Type
                    type: Builtin - unsigned int
          statement: Return
            expression: DependentScopeDeclRef - value
              specifier: TypeSpec
                type: TemplateTypeParm - T
        )"},
        {R"cpp(
  struct Foo { char operator+(int); };
  char root = Foo() + 42;
        )cpp",
         R"(
  declaration: Var - root
    type: Builtin - char
    expression: ExprWithCleanups
      expression: CXXOperatorCall
        expression: ImplicitCast - FunctionToPointerDecay
          expression: DeclRef - operator+
        expression: MaterializeTemporary - lvalue
          expression: CXXTemporaryObject - Foo
            type: Record - Foo
        expression: IntegerLiteral - 42
        )"},
        {R"cpp(
  struct Bar {
    int x;
    int root() const {
      return x;
    }
  };
        )cpp",
         R"(
  declaration: CXXMethod - root
    type: FunctionProto
      type: Builtin - int
    statement: Compound
      statement: Return
        expression: ImplicitCast - LValueToRValue
          expression: Member - x
            expression: CXXThis - const, implicit
        )"},
    };
    for (const auto &Case : Cases) {
      ParsedAST AST = TestTU::withCode(Case.first).build();
      auto Node = dumpAST(DynTypedNode::create(findUnqualifiedDecl(AST, "root")),
                          AST.getTokens(), AST.getASTContext());
      EXPECT_EQ(llvm::StringRef(Case.second).trim(),
                llvm::StringRef(llvm::to_string(Node)).trim());
    }
  }
  
  TEST(DumpASTTests, Range) {
    Annotations Case("$var[[$type[[int]] x]];");
    ParsedAST AST = TestTU::withCode(Case.code()).build();
    auto Node = dumpAST(DynTypedNode::create(findDecl(AST, "x")), AST.getTokens(),
                        AST.getASTContext());
    EXPECT_EQ(Node.range, Case.range("var"));
    ASSERT_THAT(Node.children, SizeIs(1)) << "Expected one child typeloc";
    EXPECT_EQ(Node.children.front().range, Case.range("type"));
  }
  
  TEST(DumpASTTests, NoRange) {
    auto TU = TestTU::withHeaderCode("void funcFromHeader();");
    TU.Code = "int varFromSource;";
    ParsedAST AST = TU.build();
    auto Node = dumpAST(
        DynTypedNode::create(*AST.getASTContext().getTranslationUnitDecl()),
        AST.getTokens(), AST.getASTContext());
    ASSERT_THAT(Node.children, Contains(WithDetail("varFromSource")));
    ASSERT_THAT(Node.children, Not(Contains(WithDetail("funcFromHeader"))));
    EXPECT_THAT(Node.arcana, testing::StartsWith("TranslationUnitDecl "));
    ASSERT_FALSE(Node.range.hasValue())
        << "Expected no range for translation unit";
  }
  
  TEST(DumpASTTests, Arcana) {
    ParsedAST AST = TestTU::withCode("int x;").build();
    auto Node = dumpAST(DynTypedNode::create(findDecl(AST, "x")), AST.getTokens(),
                        AST.getASTContext());
    EXPECT_THAT(Node.arcana, testing::StartsWith("VarDecl "));
    EXPECT_THAT(Node.arcana, testing::EndsWith(" 'int'"));
    ASSERT_THAT(Node.children, SizeIs(1)) << "Expected one child typeloc";
    EXPECT_THAT(Node.children.front().arcana, testing::StartsWith("QualType "));
  }
  
  } // namespace
  } // namespace clangd
  } // namespace clang
  