xref: /llvm-project/clang-tools-extra/clangd/unittests/DumpASTTests.cpp (revision db93ef14aef9c572e02bc842762bc4d0278148f9)
1 //===-- DumpASTTests.cpp --------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "Annotations.h"
10 #include "DumpAST.h"
11 #include "TestTU.h"
12 #include "clang/AST/ASTTypeTraits.h"
13 #include "llvm/Support/ScopedPrinter.h"
14 #include "gmock/gmock.h"
15 #include "gtest/gtest.h"
16 
17 namespace clang {
18 namespace clangd {
19 namespace {
20 using testing::Contains;
21 using testing::Not;
22 using testing::SizeIs;
23 
24 MATCHER_P(withDetail, str, "") { return arg.detail == str; }
25 
26 TEST(DumpASTTests, BasicInfo) {
27   std::pair</*Code=*/std::string, /*Expected=*/std::string> Cases[] = {
28       {R"cpp(
29 float root(int *x) {
30   return *x + 1;
31 }
32       )cpp",
33        R"(
34 declaration: Function - root
35   type: FunctionProto
36     type: Builtin - float
37     declaration: ParmVar - x
38       type: Pointer
39         type: Builtin - int
40   statement: Compound
41     statement: Return
42       expression: ImplicitCast - IntegralToFloating
43         expression: BinaryOperator - +
44           expression: ImplicitCast - LValueToRValue
45             expression: UnaryOperator - *
46               expression: ImplicitCast - LValueToRValue
47                 expression: DeclRef - x
48           expression: IntegerLiteral - 1
49       )"},
50       {R"cpp(
51 namespace root {
52 struct S { static const int x = 0; ~S(); };
53 int y = S::x + root::S().x;
54 }
55       )cpp",
56        R"(
57 declaration: Namespace - root
58   declaration: CXXRecord - S
59     declaration: Var - x
60       type: Qualified - const
61         type: Builtin - int
62       expression: IntegerLiteral - 0
63     declaration: CXXDestructor
64       type: Record - S
65       type: FunctionProto
66         type: Builtin - void
67     declaration: CXXConstructor
68     declaration: CXXConstructor
69   declaration: Var - y
70     type: Builtin - int
71     expression: ExprWithCleanups
72       expression: BinaryOperator - +
73         expression: ImplicitCast - LValueToRValue
74           expression: DeclRef - x
75             specifier: TypeSpec
76               type: Record - S
77         expression: ImplicitCast - LValueToRValue
78           expression: Member - x
79             expression: CXXBindTemporary
80               expression: CXXTemporaryObject - S
81                 type: Elaborated
82                   specifier: Namespace - root::
83                   type: Record - S
84       )"},
85       {R"cpp(
86 namespace root {
87 struct S { static const int x = 0; };
88 int y = S::x + root::S().x;
89 }
90       )cpp",
91        R"(
92 declaration: Namespace - root
93   declaration: CXXRecord - S
94     declaration: Var - x
95       type: Qualified - const
96         type: Builtin - int
97       expression: IntegerLiteral - 0
98     declaration: CXXConstructor
99     declaration: CXXConstructor
100     declaration: CXXConstructor
101     declaration: CXXDestructor
102   declaration: Var - y
103     type: Builtin - int
104     expression: BinaryOperator - +
105       expression: ImplicitCast - LValueToRValue
106         expression: DeclRef - x
107           specifier: TypeSpec
108             type: Record - S
109       expression: ImplicitCast - LValueToRValue
110         expression: Member - x
111           expression: CXXTemporaryObject - S
112             type: Elaborated
113               specifier: Namespace - root::
114               type: Record - S
115       )"},
116       {R"cpp(
117 namespace root {
118 template <typename T> int tmpl() {
119   (void)tmpl<unsigned>();
120   return T::value;
121 }
122 }
123       )cpp",
124        R"(
125 declaration: Namespace - root
126   declaration: FunctionTemplate - tmpl
127     declaration: TemplateTypeParm - T
128     declaration: Function - tmpl
129       type: FunctionProto
130         type: Builtin - int
131       statement: Compound
132         expression: CStyleCast - ToVoid
133           type: Builtin - void
134           expression: Call
135             expression: ImplicitCast - FunctionToPointerDecay
136               expression: DeclRef - tmpl
137                 template argument: Type
138                   type: Builtin - unsigned int
139         statement: Return
140           expression: DependentScopeDeclRef - value
141             specifier: TypeSpec
142               type: TemplateTypeParm - T
143       )"},
144       {R"cpp(
145 struct Foo { char operator+(int); };
146 char root = Foo() + 42;
147       )cpp",
148        R"(
149 declaration: Var - root
150   type: Builtin - char
151   expression: ExprWithCleanups
152     expression: CXXOperatorCall
153       expression: ImplicitCast - FunctionToPointerDecay
154         expression: DeclRef - operator+
155       expression: MaterializeTemporary - lvalue
156         expression: CXXTemporaryObject - Foo
157           type: Elaborated
158             type: Record - Foo
159       expression: IntegerLiteral - 42
160       )"},
161       {R"cpp(
162 struct Bar {
163   int x;
164   int root() const {
165     return x;
166   }
167 };
168       )cpp",
169        R"(
170 declaration: CXXMethod - root
171   type: FunctionProto
172     type: Builtin - int
173   statement: Compound
174     statement: Return
175       expression: ImplicitCast - LValueToRValue
176         expression: Member - x
177           expression: CXXThis - const, implicit
178       )"},
179   };
180   for (const auto &Case : Cases) {
181     ParsedAST AST = TestTU::withCode(Case.first).build();
182     auto Node = dumpAST(DynTypedNode::create(findUnqualifiedDecl(AST, "root")),
183                         AST.getTokens(), AST.getASTContext());
184     EXPECT_EQ(llvm::StringRef(Case.second).trim(),
185               llvm::StringRef(llvm::to_string(Node)).trim());
186   }
187 }
188 
189 TEST(DumpASTTests, Range) {
190   Annotations Case("$var[[$type[[int]] x]];");
191   ParsedAST AST = TestTU::withCode(Case.code()).build();
192   auto Node = dumpAST(DynTypedNode::create(findDecl(AST, "x")), AST.getTokens(),
193                       AST.getASTContext());
194   EXPECT_EQ(Node.range, Case.range("var"));
195   ASSERT_THAT(Node.children, SizeIs(1)) << "Expected one child typeloc";
196   EXPECT_EQ(Node.children.front().range, Case.range("type"));
197 }
198 
199 TEST(DumpASTTests, NoRange) {
200   auto TU = TestTU::withHeaderCode("void funcFromHeader();");
201   TU.Code = "int varFromSource;";
202   ParsedAST AST = TU.build();
203   auto Node = dumpAST(
204       DynTypedNode::create(*AST.getASTContext().getTranslationUnitDecl()),
205       AST.getTokens(), AST.getASTContext());
206   ASSERT_THAT(Node.children, Contains(withDetail("varFromSource")));
207   ASSERT_THAT(Node.children, Not(Contains(withDetail("funcFromHeader"))));
208   EXPECT_THAT(Node.arcana, testing::StartsWith("TranslationUnitDecl "));
209   ASSERT_FALSE(Node.range) << "Expected no range for translation unit";
210 }
211 
212 TEST(DumpASTTests, Arcana) {
213   ParsedAST AST = TestTU::withCode("int x;").build();
214   auto Node = dumpAST(DynTypedNode::create(findDecl(AST, "x")), AST.getTokens(),
215                       AST.getASTContext());
216   EXPECT_THAT(Node.arcana, testing::StartsWith("VarDecl "));
217   EXPECT_THAT(Node.arcana, testing::EndsWith(" 'int'"));
218   ASSERT_THAT(Node.children, SizeIs(1)) << "Expected one child typeloc";
219   EXPECT_THAT(Node.children.front().arcana, testing::StartsWith("QualType "));
220 }
221 
222 TEST(DumpASTTests, UnbalancedBraces) {
223   // Test that we don't crash while trying to compute a source range for the
224   // node whose ending brace is missing, and also that the source range is
225   // not empty.
226   Annotations Case("/*error-ok*/ $func[[int main() {]]");
227   ParsedAST AST = TestTU::withCode(Case.code()).build();
228   auto Node = dumpAST(DynTypedNode::create(findDecl(AST, "main")),
229                       AST.getTokens(), AST.getASTContext());
230   ASSERT_EQ(Node.range, Case.range("func"));
231 }
232 
233 } // namespace
234 } // namespace clangd
235 } // namespace clang
236