clang-tools 24.0.0git
DumpASTTests.cpp
Go to the documentation of this file.
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/ADT/STLExtras.h"
14#include "llvm/Support/ScopedPrinter.h"
15#include "gmock/gmock.h"
16#include "gtest/gtest.h"
17
18namespace clang {
19namespace clangd {
20namespace {
21using testing::Contains;
22using testing::Not;
23using testing::SizeIs;
24
25MATCHER_P(withDetail, str, "") { return arg.detail == str; }
26
27TEST(DumpASTTests, BasicInfo) {
28 std::pair</*Code=*/std::string, /*Expected=*/std::string> Cases[] = {
29 {R"cpp(
30float root(int *x) {
31 return *x + 1;
32}
33 )cpp",
34 R"(
35declaration: Function - root
36 type: FunctionProto
37 type: Builtin - float
38 declaration: ParmVar - x
39 type: Pointer
40 type: Builtin - int
41 statement: Compound
42 statement: Return
43 expression: ImplicitCast - IntegralToFloating
44 expression: BinaryOperator - +
45 expression: ImplicitCast - LValueToRValue
46 expression: UnaryOperator - *
47 expression: ImplicitCast - LValueToRValue
48 expression: DeclRef - x
49 expression: IntegerLiteral - 1
50 )"},
51 {R"cpp(
52namespace root {
53struct S { static const int x = 0; ~S(); };
54int y = S::x + root::S().x;
55}
56 )cpp",
57 R"(
58declaration: Namespace - root
59 declaration: CXXRecord - S
60 declaration: Var - x
61 type: Qualified - const
62 type: Builtin - int
63 expression: IntegerLiteral - 0
64 declaration: CXXDestructor
65 type: Record - S
66 type: FunctionProto
67 type: Builtin - void
68 declaration: CXXConstructor
69 declaration: CXXConstructor
70 declaration: Var - y
71 type: Builtin - int
72 expression: ExprWithCleanups
73 expression: BinaryOperator - +
74 expression: ImplicitCast - LValueToRValue
75 expression: DeclRef - x
76 specifier: Type
77 type: Record - S
78 expression: ImplicitCast - LValueToRValue
79 expression: Member - x
80 expression: CXXBindTemporary
81 expression: CXXTemporaryObject - S
82 type: Record - S
83 specifier: Namespace - root::
84 )"},
85 {R"cpp(
86namespace root {
87struct S { static const int x = 0; };
88int y = S::x + root::S().x;
89}
90 )cpp",
91 R"(
92declaration: 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: Type
108 type: Record - S
109 expression: ImplicitCast - LValueToRValue
110 expression: Member - x
111 expression: CXXTemporaryObject - S
112 type: Record - S
113 specifier: Namespace - root::
114 )"},
115 {R"cpp(
116namespace root {
117template <typename T> int tmpl() {
118 (void)tmpl<unsigned>();
119 return T::value;
120}
121}
122 )cpp",
123 R"(
124declaration: Namespace - root
125 declaration: FunctionTemplate - tmpl
126 declaration: TemplateTypeParm - T
127 declaration: Function - tmpl
128 type: FunctionProto
129 type: Builtin - int
130 statement: Compound
131 expression: CStyleCast - ToVoid
132 type: Builtin - void
133 expression: Call
134 expression: ImplicitCast - FunctionToPointerDecay
135 expression: DeclRef - tmpl
136 template argument: Type
137 type: Builtin - unsigned int
138 statement: Return
139 expression: DependentScopeDeclRef - value
140 specifier: Type
141 type: TemplateTypeParm - T
142 )"},
143 {R"cpp(
144struct Foo { char operator+(int); };
145char root = Foo() + 42;
146 )cpp",
147 R"(
148declaration: Var - root
149 type: Builtin - char
150 expression: ExprWithCleanups
151 expression: CXXOperatorCall
152 expression: ImplicitCast - FunctionToPointerDecay
153 expression: DeclRef - operator+
154 expression: MaterializeTemporary - lvalue
155 expression: CXXTemporaryObject - Foo
156 type: Record - Foo
157 expression: IntegerLiteral - 42
158 )"},
159 {R"cpp(
160struct Bar {
161 int x;
162 int root() const {
163 return x;
164 }
165};
166 )cpp",
167 R"(
168declaration: CXXMethod - root
169 type: FunctionProto
170 type: Builtin - int
171 statement: Compound
172 statement: Return
173 expression: ImplicitCast - LValueToRValue
174 expression: Member - x
175 expression: CXXThis - const, implicit
176 )"},
177 };
178 for (const auto &Case : Cases) {
179 ParsedAST AST = TestTU::withCode(Case.first).build();
180 auto Node = dumpAST(DynTypedNode::create(findUnqualifiedDecl(AST, "root")),
181 AST.getTokens(), AST.getASTContext());
182 EXPECT_EQ(llvm::StringRef(Case.second).trim(),
183 llvm::StringRef(llvm::to_string(Node)).trim());
184 }
185}
186
187TEST(DumpASTTests, Range) {
188 Annotations Case("$var[[$type[[int]] x]];");
189 ParsedAST AST = TestTU::withCode(Case.code()).build();
190 auto Node = dumpAST(DynTypedNode::create(findDecl(AST, "x")), AST.getTokens(),
191 AST.getASTContext());
192 EXPECT_EQ(Node.range, Case.range("var"));
193 ASSERT_THAT(Node.children, SizeIs(1)) << "Expected one child typeloc";
194 EXPECT_EQ(Node.children.front().range, Case.range("type"));
195}
196
197TEST(DumpASTTests, NoRange) {
198 auto TU = TestTU::withHeaderCode("void funcFromHeader();");
199 TU.Code = "int varFromSource;";
200 ParsedAST AST = TU.build();
201 auto Node = dumpAST(
202 DynTypedNode::create(*AST.getASTContext().getTranslationUnitDecl()),
203 AST.getTokens(), AST.getASTContext());
204 ASSERT_THAT(Node.children, Contains(withDetail("varFromSource")));
205 ASSERT_THAT(Node.children, Not(Contains(withDetail("funcFromHeader"))));
206 EXPECT_THAT(Node.arcana, testing::StartsWith("TranslationUnitDecl "));
207 ASSERT_FALSE(Node.range) << "Expected no range for translation unit";
208}
209
210TEST(DumpASTTests, Arcana) {
211 ParsedAST AST = TestTU::withCode("int x;").build();
212 auto Node = dumpAST(DynTypedNode::create(findDecl(AST, "x")), AST.getTokens(),
213 AST.getASTContext());
214 EXPECT_THAT(Node.arcana, testing::StartsWith("VarDecl "));
215 EXPECT_THAT(Node.arcana, testing::EndsWith(" 'int' external-linkage"));
216 ASSERT_THAT(Node.children, SizeIs(1)) << "Expected one child typeloc";
217 EXPECT_THAT(Node.children.front().arcana, testing::StartsWith("QualType "));
218}
219
220TEST(DumpASTTests, UnbalancedBraces) {
221 // Test that we don't crash while trying to compute a source range for the
222 // node whose ending brace is missing, and also that the source range is
223 // not empty.
224 Annotations Case("/*error-ok*/ $func[[int main() {]]");
225 ParsedAST AST = TestTU::withCode(Case.code()).build();
226 auto Node = dumpAST(DynTypedNode::create(findDecl(AST, "main")),
227 AST.getTokens(), AST.getASTContext());
228 ASSERT_EQ(Node.range, Case.range("func"));
229}
230
231bool hasDetail(const ASTNode &Node, llvm::StringRef Detail) {
232 if (Node.detail == Detail)
233 return true;
234 return llvm::any_of(Node.children, [&Detail](const ASTNode &Child) {
235 return hasDetail(Child, Detail);
236 });
237}
238
239TEST(DumpASTTests, PackIndexedConcept) {
240 auto TU = TestTU::withCode(R"cpp(
241template <template <class> concept... CC, CC...[0] T>
242void func(T);
243 )cpp");
244 TU.ExtraArgs = {"-std=c++2d"};
245 ParsedAST AST = TU.build();
246 const ASTNode Node = dumpAST(
247 DynTypedNode::create(*AST.getASTContext().getTranslationUnitDecl()),
248 AST.getTokens(), AST.getASTContext());
249
250 EXPECT_TRUE(hasDetail(Node, "CC...[0]"));
251}
252
253TEST(DumpASTTests, NestedTemplates) {
254 // Test that we don't crash while trying to dump AST of a template function
255 // with nested template names such as Foo<V>::template Bar<W>::Value.
256 const char *Code = R"cpp(
257template <typename T>
258struct TypeA {
259 template <typename U>
260 struct TypeB {
261 static U Value;
262 };
263};
264
265template <typename V, typename W>
266auto func() {
267 return TypeA<V>::template TypeB<W>::Value;
268}
269 )cpp";
270
272 const NamedDecl &Func = findDecl(AST, [](const NamedDecl &D) {
273 return isa<FunctionDecl>(D) && D.getNameAsString() == "func";
274 });
275
276 const ASTNode Node =
277 dumpAST(DynTypedNode::create(Func), AST.getTokens(), AST.getASTContext());
278
279 EXPECT_EQ(Node.kind, "Function");
280}
281
282} // namespace
283} // namespace clangd
284} // namespace clang
Same as llvm::Annotations, but adjusts functions to LSP-specific types for positions and ranges.
Definition Annotations.h:23
Stores and provides access to parsed AST.
Definition ParsedAST.h:47
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:44
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
Definition TestTU.cpp:220
ASTNode dumpAST(const DynTypedNode &N, const syntax::TokenBuffer &Tokens, const ASTContext &Ctx)
Definition DumpAST.cpp:414
MATCHER_P(named, N, "")
TEST(BackgroundQueueTest, Priority)
const NamedDecl & findUnqualifiedDecl(ParsedAST &AST, llvm::StringRef Name)
Definition TestTU.cpp:261
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Simplified description of a clang AST node.
Definition Protocol.h:2093
ParsedAST build() const
Definition TestTU.cpp:115
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
Definition TestTU.h:42
static TestTU withCode(llvm::StringRef Code)
Definition TestTU.h:36