| //===-- 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 |