1 | //===-- clang-doc/SerializeTest.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 "Serialize.h" |
10 | #include "ClangDocTest.h" |
11 | #include "Representation.h" |
12 | #include "clang/AST/Comment.h" |
13 | #include "clang/AST/RecursiveASTVisitor.h" |
14 | #include "gtest/gtest.h" |
15 | |
16 | namespace clang { |
17 | namespace doc { |
18 | |
19 | class ClangDocSerializeTestVisitor |
20 | : public RecursiveASTVisitor<ClangDocSerializeTestVisitor> { |
21 | |
22 | EmittedInfoList &EmittedInfos; |
23 | bool Public; |
24 | |
25 | comments::FullComment *(const NamedDecl *D) const { |
26 | if (RawComment * = |
27 | D->getASTContext().getRawCommentForDeclNoCache(D)) { |
28 | Comment->setAttached(); |
29 | return Comment->parse(Context: D->getASTContext(), PP: nullptr, D); |
30 | } |
31 | return nullptr; |
32 | } |
33 | |
34 | public: |
35 | ClangDocSerializeTestVisitor(EmittedInfoList &EmittedInfos, bool Public) |
36 | : EmittedInfos(EmittedInfos), Public(Public) {} |
37 | |
38 | template <typename T> bool mapDecl(const T *D) { |
39 | auto I = serialize::emitInfo(D, getComment(D), /*Line=*/0, |
40 | /*File=*/"test.cpp" , true, Public); |
41 | if (I.first) |
42 | EmittedInfos.emplace_back(std::move(I.first)); |
43 | if (I.second) |
44 | EmittedInfos.emplace_back(std::move(I.second)); |
45 | return true; |
46 | } |
47 | |
48 | bool VisitNamespaceDecl(const NamespaceDecl *D) { return mapDecl(D); } |
49 | |
50 | bool VisitFunctionDecl(const FunctionDecl *D) { |
51 | // Don't visit CXXMethodDecls twice |
52 | if (dyn_cast<CXXMethodDecl>(Val: D)) |
53 | return true; |
54 | return mapDecl(D); |
55 | } |
56 | |
57 | bool VisitCXXMethodDecl(const CXXMethodDecl *D) { return mapDecl(D); } |
58 | |
59 | bool VisitRecordDecl(const RecordDecl *D) { return mapDecl(D); } |
60 | |
61 | bool VisitEnumDecl(const EnumDecl *D) { return mapDecl(D); } |
62 | |
63 | bool VisitTypedefDecl(const TypedefDecl *D) { return mapDecl(D); } |
64 | |
65 | bool VisitTypeAliasDecl(const TypeAliasDecl *D) { return mapDecl(D); } |
66 | }; |
67 | |
68 | void (StringRef Code, size_t NumExpectedInfos, bool Public, |
69 | EmittedInfoList &EmittedInfos) { |
70 | auto ASTUnit = clang::tooling::buildASTFromCode(Code); |
71 | auto TU = ASTUnit->getASTContext().getTranslationUnitDecl(); |
72 | ClangDocSerializeTestVisitor Visitor(EmittedInfos, Public); |
73 | Visitor.TraverseTranslationUnitDecl(TU); |
74 | ASSERT_EQ(NumExpectedInfos, EmittedInfos.size()); |
75 | } |
76 | |
77 | void (StringRef Code, size_t NumExpectedInfos, |
78 | bool Public, EmittedInfoList &EmittedInfos, |
79 | std::vector<std::string> &Args) { |
80 | auto ASTUnit = clang::tooling::buildASTFromCodeWithArgs(Code, Args); |
81 | auto TU = ASTUnit->getASTContext().getTranslationUnitDecl(); |
82 | ClangDocSerializeTestVisitor Visitor(EmittedInfos, Public); |
83 | Visitor.TraverseTranslationUnitDecl(TU); |
84 | ASSERT_EQ(NumExpectedInfos, EmittedInfos.size()); |
85 | } |
86 | |
87 | // Constructs a comment definition as the parser would for one comment line. |
88 | /* TODO uncomment this when the missing comment is fixed in emitRecordInfo and |
89 | the code that calls this is re-enabled. |
90 | CommentInfo MakeOneLineCommentInfo(const std::string &Text) { |
91 | CommentInfo TopComment; |
92 | TopComment.Kind = "FullComment"; |
93 | TopComment.Children.emplace_back(std::make_unique<CommentInfo>()); |
94 | |
95 | CommentInfo *Brief = TopComment.Children.back().get(); |
96 | Brief->Kind = "ParagraphComment"; |
97 | |
98 | Brief->Children.emplace_back(std::make_unique<CommentInfo>()); |
99 | Brief->Children.back()->Kind = "TextComment"; |
100 | Brief->Children.back()->Name = "ParagraphComment"; |
101 | Brief->Children.back()->Text = Text; |
102 | |
103 | return TopComment; |
104 | } |
105 | */ |
106 | |
107 | // Test serialization of namespace declarations. |
108 | TEST(SerializeTest, emitNamespaceInfo) { |
109 | EmittedInfoList Infos; |
110 | ExtractInfosFromCode(Code: "namespace A { namespace B { void f() {} } }" , NumExpectedInfos: 5, |
111 | /*Public=*/false, EmittedInfos&: Infos); |
112 | |
113 | NamespaceInfo *A = InfoAsNamespace(I: Infos[0].get()); |
114 | NamespaceInfo ExpectedA(EmptySID, "A" ); |
115 | CheckNamespaceInfo(Expected: &ExpectedA, Actual: A); |
116 | |
117 | NamespaceInfo *B = InfoAsNamespace(I: Infos[2].get()); |
118 | NamespaceInfo ExpectedB(EmptySID, /*Name=*/"B" , /*Path=*/"A" ); |
119 | ExpectedB.Namespace.emplace_back(Args: EmptySID, Args: "A" , Args: InfoType::IT_namespace); |
120 | CheckNamespaceInfo(Expected: &ExpectedB, Actual: B); |
121 | |
122 | NamespaceInfo *BWithFunction = InfoAsNamespace(I: Infos[4].get()); |
123 | NamespaceInfo ExpectedBWithFunction(EmptySID); |
124 | FunctionInfo F; |
125 | F.Name = "f" ; |
126 | F.ReturnType = TypeInfo("void" ); |
127 | F.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp" }); |
128 | F.Namespace.emplace_back(Args: EmptySID, Args: "B" , Args: InfoType::IT_namespace); |
129 | F.Namespace.emplace_back(Args: EmptySID, Args: "A" , Args: InfoType::IT_namespace); |
130 | F.Access = AccessSpecifier::AS_none; |
131 | ExpectedBWithFunction.Children.Functions.emplace_back(args: std::move(F)); |
132 | CheckNamespaceInfo(Expected: &ExpectedBWithFunction, Actual: BWithFunction); |
133 | } |
134 | |
135 | TEST(SerializeTest, emitAnonymousNamespaceInfo) { |
136 | EmittedInfoList Infos; |
137 | ExtractInfosFromCode(Code: "namespace { }" , NumExpectedInfos: 2, /*Public=*/false, EmittedInfos&: Infos); |
138 | |
139 | NamespaceInfo *A = InfoAsNamespace(I: Infos[0].get()); |
140 | NamespaceInfo ExpectedA(EmptySID); |
141 | ExpectedA.Name = "@nonymous_namespace" ; |
142 | CheckNamespaceInfo(Expected: &ExpectedA, Actual: A); |
143 | } |
144 | |
145 | // Test serialization of record declarations. |
146 | TEST(SerializeTest, emitRecordInfo) { |
147 | EmittedInfoList Infos; |
148 | ExtractInfosFromCode(Code: R"raw(class E { |
149 | public: |
150 | E() {} |
151 | |
152 | // Some docs. |
153 | int value; |
154 | protected: |
155 | void ProtectedMethod(); |
156 | }; |
157 | template <typename T> |
158 | struct F { |
159 | void TemplateMethod(); |
160 | }; |
161 | template <> |
162 | void F<int>::TemplateMethod(); |
163 | typedef struct {} G;)raw" , |
164 | NumExpectedInfos: 10, /*Public=*/false, EmittedInfos&: Infos); |
165 | |
166 | RecordInfo *E = InfoAsRecord(I: Infos[0].get()); |
167 | RecordInfo ExpectedE(EmptySID, /*Name=*/"E" , /*Path=*/"GlobalNamespace" ); |
168 | ExpectedE.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
169 | Args: InfoType::IT_namespace); |
170 | ExpectedE.TagType = TagTypeKind::Class; |
171 | ExpectedE.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp" }); |
172 | ExpectedE.Members.emplace_back(Args: TypeInfo("int" ), Args: "value" , |
173 | Args: AccessSpecifier::AS_public); |
174 | // TODO the data member should have the docstring on it: |
175 | //ExpectedE.Members.back().Description.push_back(MakeOneLineCommentInfo(" Some docs")); |
176 | CheckRecordInfo(Expected: &ExpectedE, Actual: E); |
177 | |
178 | RecordInfo *RecordWithEConstructor = InfoAsRecord(I: Infos[2].get()); |
179 | RecordInfo ExpectedRecordWithEConstructor(EmptySID); |
180 | FunctionInfo EConstructor; |
181 | EConstructor.Name = "E" ; |
182 | EConstructor.Parent = Reference(EmptySID, "E" , InfoType::IT_record); |
183 | EConstructor.ReturnType = TypeInfo("void" ); |
184 | EConstructor.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp" }); |
185 | EConstructor.Namespace.emplace_back(Args: EmptySID, Args: "E" , Args: InfoType::IT_record); |
186 | EConstructor.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
187 | Args: InfoType::IT_namespace); |
188 | EConstructor.Access = AccessSpecifier::AS_public; |
189 | EConstructor.IsMethod = true; |
190 | ExpectedRecordWithEConstructor.Children.Functions.emplace_back( |
191 | args: std::move(EConstructor)); |
192 | CheckRecordInfo(Expected: &ExpectedRecordWithEConstructor, Actual: RecordWithEConstructor); |
193 | |
194 | RecordInfo *RecordWithMethod = InfoAsRecord(I: Infos[3].get()); |
195 | RecordInfo ExpectedRecordWithMethod(EmptySID); |
196 | FunctionInfo Method; |
197 | Method.Name = "ProtectedMethod" ; |
198 | Method.Parent = Reference(EmptySID, "E" , InfoType::IT_record); |
199 | Method.ReturnType = TypeInfo("void" ); |
200 | Method.Loc.emplace_back(Args: 0, Args: llvm::SmallString<16>{"test.cpp" }); |
201 | Method.Namespace.emplace_back(Args: EmptySID, Args: "E" , Args: InfoType::IT_record); |
202 | Method.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
203 | Args: InfoType::IT_namespace); |
204 | Method.Access = AccessSpecifier::AS_protected; |
205 | Method.IsMethod = true; |
206 | ExpectedRecordWithMethod.Children.Functions.emplace_back(args: std::move(Method)); |
207 | CheckRecordInfo(Expected: &ExpectedRecordWithMethod, Actual: RecordWithMethod); |
208 | |
209 | RecordInfo *F = InfoAsRecord(I: Infos[4].get()); |
210 | RecordInfo ExpectedF(EmptySID, /*Name=*/"F" , /*Path=*/"GlobalNamespace" ); |
211 | ExpectedF.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
212 | Args: InfoType::IT_namespace); |
213 | ExpectedF.TagType = TagTypeKind::Struct; |
214 | ExpectedF.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp" }); |
215 | CheckRecordInfo(Expected: &ExpectedF, Actual: F); |
216 | |
217 | RecordInfo *RecordWithTemplateMethod = InfoAsRecord(I: Infos[6].get()); |
218 | RecordInfo ExpectedRecordWithTemplateMethod(EmptySID); |
219 | FunctionInfo TemplateMethod; |
220 | TemplateMethod.Name = "TemplateMethod" ; |
221 | TemplateMethod.Parent = Reference(EmptySID, "F" , InfoType::IT_record); |
222 | TemplateMethod.ReturnType = TypeInfo("void" ); |
223 | TemplateMethod.Loc.emplace_back(Args: 0, Args: llvm::SmallString<16>{"test.cpp" }); |
224 | TemplateMethod.Namespace.emplace_back(Args: EmptySID, Args: "F" , Args: InfoType::IT_record); |
225 | TemplateMethod.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
226 | Args: InfoType::IT_namespace); |
227 | TemplateMethod.Access = AccessSpecifier::AS_public; |
228 | TemplateMethod.IsMethod = true; |
229 | ExpectedRecordWithTemplateMethod.Children.Functions.emplace_back( |
230 | args: std::move(TemplateMethod)); |
231 | CheckRecordInfo(Expected: &ExpectedRecordWithTemplateMethod, Actual: RecordWithTemplateMethod); |
232 | |
233 | RecordInfo *TemplatedRecord = InfoAsRecord(I: Infos[7].get()); |
234 | RecordInfo ExpectedTemplatedRecord(EmptySID); |
235 | FunctionInfo SpecializedTemplateMethod; |
236 | SpecializedTemplateMethod.Name = "TemplateMethod" ; |
237 | SpecializedTemplateMethod.Parent = |
238 | Reference(EmptySID, "F" , InfoType::IT_record); |
239 | SpecializedTemplateMethod.ReturnType = TypeInfo("void" ); |
240 | SpecializedTemplateMethod.Loc.emplace_back(Args: 0, |
241 | Args: llvm::SmallString<16>{"test.cpp" }); |
242 | SpecializedTemplateMethod.Namespace.emplace_back(Args: EmptySID, Args: "F" , |
243 | Args: InfoType::IT_record); |
244 | SpecializedTemplateMethod.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
245 | Args: InfoType::IT_namespace); |
246 | SpecializedTemplateMethod.Access = AccessSpecifier::AS_public; |
247 | SpecializedTemplateMethod.IsMethod = true; |
248 | ExpectedTemplatedRecord.Children.Functions.emplace_back( |
249 | args: std::move(SpecializedTemplateMethod)); |
250 | CheckRecordInfo(Expected: &ExpectedTemplatedRecord, Actual: TemplatedRecord); |
251 | |
252 | RecordInfo *G = InfoAsRecord(I: Infos[8].get()); |
253 | RecordInfo ExpectedG(EmptySID, /*Name=*/"G" , /*Path=*/"GlobalNamespace" ); |
254 | ExpectedG.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
255 | Args: InfoType::IT_namespace); |
256 | ExpectedG.TagType = TagTypeKind::Struct; |
257 | ExpectedG.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp" }); |
258 | ExpectedG.IsTypeDef = true; |
259 | CheckRecordInfo(Expected: &ExpectedG, Actual: G); |
260 | } |
261 | |
262 | // Test serialization of enum declarations. |
263 | TEST(SerializeTest, emitEnumInfo) { |
264 | EmittedInfoList Infos; |
265 | ExtractInfosFromCode(Code: "enum E { X, Y }; enum class G { A, B };" , NumExpectedInfos: 2, |
266 | /*Public=*/false, EmittedInfos&: Infos); |
267 | |
268 | NamespaceInfo *NamespaceWithEnum = InfoAsNamespace(I: Infos[0].get()); |
269 | NamespaceInfo ExpectedNamespaceWithEnum(EmptySID); |
270 | EnumInfo E; |
271 | E.Name = "E" ; |
272 | E.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp" }); |
273 | E.Members.emplace_back(Args: "X" , Args: "0" ); |
274 | E.Members.emplace_back(Args: "Y" , Args: "1" ); |
275 | ExpectedNamespaceWithEnum.Children.Enums.emplace_back(args: std::move(E)); |
276 | CheckNamespaceInfo(Expected: &ExpectedNamespaceWithEnum, Actual: NamespaceWithEnum); |
277 | |
278 | NamespaceInfo *NamespaceWithScopedEnum = InfoAsNamespace(I: Infos[1].get()); |
279 | NamespaceInfo ExpectedNamespaceWithScopedEnum(EmptySID); |
280 | EnumInfo G; |
281 | G.Name = "G" ; |
282 | G.Scoped = true; |
283 | G.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp" }); |
284 | G.Members.emplace_back(Args: "A" , Args: "0" ); |
285 | G.Members.emplace_back(Args: "B" , Args: "1" ); |
286 | ExpectedNamespaceWithScopedEnum.Children.Enums.emplace_back(args: std::move(G)); |
287 | CheckNamespaceInfo(Expected: &ExpectedNamespaceWithScopedEnum, Actual: NamespaceWithScopedEnum); |
288 | } |
289 | |
290 | TEST(SerializeTest, emitUndefinedRecordInfo) { |
291 | EmittedInfoList Infos; |
292 | ExtractInfosFromCode(Code: "class E;" , NumExpectedInfos: 2, /*Public=*/false, EmittedInfos&: Infos); |
293 | |
294 | RecordInfo *E = InfoAsRecord(I: Infos[0].get()); |
295 | RecordInfo ExpectedE(EmptySID, /*Name=*/"E" , /*Path=*/"GlobalNamespace" ); |
296 | ExpectedE.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
297 | Args: InfoType::IT_namespace); |
298 | ExpectedE.TagType = TagTypeKind::Class; |
299 | ExpectedE.Loc.emplace_back(Args: 0, Args: llvm::SmallString<16>{"test.cpp" }); |
300 | CheckRecordInfo(Expected: &ExpectedE, Actual: E); |
301 | } |
302 | |
303 | TEST(SerializeTest, emitRecordMemberInfo) { |
304 | EmittedInfoList Infos; |
305 | ExtractInfosFromCode(Code: "struct E { int I; };" , NumExpectedInfos: 2, /*Public=*/false, EmittedInfos&: Infos); |
306 | |
307 | RecordInfo *E = InfoAsRecord(I: Infos[0].get()); |
308 | RecordInfo ExpectedE(EmptySID, /*Name=*/"E" , /*Path=*/"GlobalNamespace" ); |
309 | ExpectedE.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
310 | Args: InfoType::IT_namespace); |
311 | ExpectedE.TagType = TagTypeKind::Struct; |
312 | ExpectedE.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp" }); |
313 | ExpectedE.Members.emplace_back(Args: TypeInfo("int" ), Args: "I" , |
314 | Args: AccessSpecifier::AS_public); |
315 | CheckRecordInfo(Expected: &ExpectedE, Actual: E); |
316 | } |
317 | |
318 | TEST(SerializeTest, emitInternalRecordInfo) { |
319 | EmittedInfoList Infos; |
320 | ExtractInfosFromCode(Code: "class E { class G {}; };" , NumExpectedInfos: 4, /*Public=*/false, EmittedInfos&: Infos); |
321 | |
322 | RecordInfo *E = InfoAsRecord(I: Infos[0].get()); |
323 | RecordInfo ExpectedE(EmptySID, /*Name=*/"E" , /*Path=*/"GlobalNamespace" ); |
324 | ExpectedE.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
325 | Args: InfoType::IT_namespace); |
326 | ExpectedE.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp" }); |
327 | ExpectedE.TagType = TagTypeKind::Class; |
328 | CheckRecordInfo(Expected: &ExpectedE, Actual: E); |
329 | |
330 | RecordInfo *G = InfoAsRecord(I: Infos[2].get()); |
331 | llvm::SmallString<128> ExpectedGPath("GlobalNamespace/E" ); |
332 | llvm::sys::path::native(path&: ExpectedGPath); |
333 | RecordInfo ExpectedG(EmptySID, /*Name=*/"G" , /*Path=*/ExpectedGPath); |
334 | ExpectedG.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp" }); |
335 | ExpectedG.TagType = TagTypeKind::Class; |
336 | ExpectedG.Namespace.emplace_back(Args: EmptySID, Args: "E" , Args: InfoType::IT_record); |
337 | ExpectedG.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
338 | Args: InfoType::IT_namespace); |
339 | CheckRecordInfo(Expected: &ExpectedG, Actual: G); |
340 | } |
341 | |
342 | TEST(SerializeTest, emitPublicAnonymousNamespaceInfo) { |
343 | EmittedInfoList Infos; |
344 | ExtractInfosFromCode(Code: "namespace { class A; }" , NumExpectedInfos: 0, /*Public=*/true, EmittedInfos&: Infos); |
345 | } |
346 | |
347 | TEST(SerializeTest, emitPublicFunctionInternalInfo) { |
348 | EmittedInfoList Infos; |
349 | ExtractInfosFromCode(Code: "int F() { class G {}; return 0; };" , NumExpectedInfos: 1, /*Public=*/true, |
350 | EmittedInfos&: Infos); |
351 | |
352 | NamespaceInfo *BWithFunction = InfoAsNamespace(I: Infos[0].get()); |
353 | NamespaceInfo ExpectedBWithFunction(EmptySID); |
354 | FunctionInfo F; |
355 | F.Name = "F" ; |
356 | F.ReturnType = TypeInfo("int" ); |
357 | F.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp" }); |
358 | F.Access = AccessSpecifier::AS_none; |
359 | ExpectedBWithFunction.Children.Functions.emplace_back(args: std::move(F)); |
360 | CheckNamespaceInfo(Expected: &ExpectedBWithFunction, Actual: BWithFunction); |
361 | } |
362 | |
363 | TEST(SerializeTest, emitInlinedFunctionInfo) { |
364 | EmittedInfoList Infos; |
365 | ExtractInfosFromCode(Code: "inline void F(int I) { };" , NumExpectedInfos: 1, /*Public=*/true, EmittedInfos&: Infos); |
366 | |
367 | NamespaceInfo *BWithFunction = InfoAsNamespace(I: Infos[0].get()); |
368 | NamespaceInfo ExpectedBWithFunction(EmptySID); |
369 | FunctionInfo F; |
370 | F.Name = "F" ; |
371 | F.ReturnType = TypeInfo("void" ); |
372 | F.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp" }); |
373 | F.Params.emplace_back(Args: TypeInfo("int" ), Args: "I" ); |
374 | F.Access = AccessSpecifier::AS_none; |
375 | ExpectedBWithFunction.Children.Functions.emplace_back(args: std::move(F)); |
376 | CheckNamespaceInfo(Expected: &ExpectedBWithFunction, Actual: BWithFunction); |
377 | } |
378 | |
379 | TEST(SerializeTest, emitInheritedRecordInfo) { |
380 | EmittedInfoList Infos; |
381 | ExtractInfosFromCode(Code: R"raw(class F { protected: void set(int N); }; |
382 | class G { public: int get() { return 1; } protected: int I; }; |
383 | class E : public F, virtual private G {}; |
384 | class H : private E {}; |
385 | template <typename T> |
386 | class I {} ; |
387 | class J : public I<int> {} ;)raw" , |
388 | NumExpectedInfos: 14, /*Public=*/false, EmittedInfos&: Infos); |
389 | |
390 | RecordInfo *F = InfoAsRecord(I: Infos[0].get()); |
391 | RecordInfo ExpectedF(EmptySID, /*Name=*/"F" , /*Path=*/"GlobalNamespace" ); |
392 | ExpectedF.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
393 | Args: InfoType::IT_namespace, Args: "" ); |
394 | ExpectedF.TagType = TagTypeKind::Class; |
395 | ExpectedF.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp" }); |
396 | CheckRecordInfo(Expected: &ExpectedF, Actual: F); |
397 | |
398 | RecordInfo *G = InfoAsRecord(I: Infos[3].get()); |
399 | RecordInfo ExpectedG(EmptySID, /*Name=*/"G" , /*Path=*/"GlobalNamespace" ); |
400 | ExpectedG.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
401 | Args: InfoType::IT_namespace); |
402 | ExpectedG.TagType = TagTypeKind::Class; |
403 | ExpectedG.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp" }); |
404 | ExpectedG.Members.emplace_back(Args: TypeInfo("int" ), Args: "I" , |
405 | Args: AccessSpecifier::AS_protected); |
406 | CheckRecordInfo(Expected: &ExpectedG, Actual: G); |
407 | |
408 | RecordInfo *E = InfoAsRecord(I: Infos[6].get()); |
409 | RecordInfo ExpectedE(EmptySID, /*Name=*/"E" , /*Path=*/"GlobalNamespace" ); |
410 | ExpectedE.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
411 | Args: InfoType::IT_namespace); |
412 | ExpectedE.Parents.emplace_back(Args: EmptySID, /*Name=*/Args: "F" , Args: InfoType::IT_record, |
413 | /*QualName=*/Args: "" , /*Path*=*/Args: "GlobalNamespace" ); |
414 | ExpectedE.VirtualParents.emplace_back(Args: EmptySID, /*Name=*/Args: "G" , |
415 | Args: InfoType::IT_record, /*QualName=*/Args: "G" , |
416 | /*Path*=*/Args: "GlobalNamespace" ); |
417 | ExpectedE.Bases.emplace_back(args: EmptySID, /*Name=*/args: "F" , |
418 | /*Path=*/args: "GlobalNamespace" , args: false, |
419 | args: AccessSpecifier::AS_public, args: true); |
420 | FunctionInfo FunctionSet; |
421 | FunctionSet.Name = "set" ; |
422 | FunctionSet.ReturnType = TypeInfo("void" ); |
423 | FunctionSet.Loc.emplace_back(); |
424 | FunctionSet.Params.emplace_back(Args: TypeInfo("int" ), Args: "N" ); |
425 | FunctionSet.Namespace.emplace_back(Args: EmptySID, Args: "F" , Args: InfoType::IT_record); |
426 | FunctionSet.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
427 | Args: InfoType::IT_namespace); |
428 | FunctionSet.Access = AccessSpecifier::AS_protected; |
429 | FunctionSet.IsMethod = true; |
430 | ExpectedE.Bases.back().Children.Functions.emplace_back( |
431 | args: std::move(FunctionSet)); |
432 | ExpectedE.Bases.emplace_back(args: EmptySID, /*Name=*/args: "G" , |
433 | /*Path=*/args: "GlobalNamespace" , args: true, |
434 | args: AccessSpecifier::AS_private, args: true); |
435 | FunctionInfo FunctionGet; |
436 | FunctionGet.Name = "get" ; |
437 | FunctionGet.ReturnType = TypeInfo("int" ); |
438 | FunctionGet.DefLoc = Location(); |
439 | FunctionGet.Namespace.emplace_back(Args: EmptySID, Args: "G" , Args: InfoType::IT_record); |
440 | FunctionGet.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
441 | Args: InfoType::IT_namespace); |
442 | FunctionGet.Access = AccessSpecifier::AS_private; |
443 | FunctionGet.IsMethod = true; |
444 | ExpectedE.Bases.back().Children.Functions.emplace_back( |
445 | args: std::move(FunctionGet)); |
446 | ExpectedE.Bases.back().Members.emplace_back(Args: TypeInfo("int" ), Args: "I" , |
447 | Args: AccessSpecifier::AS_private); |
448 | ExpectedE.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp" }); |
449 | ExpectedE.TagType = TagTypeKind::Class; |
450 | CheckRecordInfo(Expected: &ExpectedE, Actual: E); |
451 | |
452 | RecordInfo *H = InfoAsRecord(I: Infos[8].get()); |
453 | RecordInfo ExpectedH(EmptySID, /*Name=*/"H" , /*Path=*/"GlobalNamespace" ); |
454 | ExpectedH.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
455 | Args: InfoType::IT_namespace); |
456 | ExpectedH.TagType = TagTypeKind::Class; |
457 | ExpectedH.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp" }); |
458 | ExpectedH.Parents.emplace_back(Args: EmptySID, /*Name=*/Args: "E" , Args: InfoType::IT_record, |
459 | /*QualName=*/Args: "E" , /*Path=*/Args: "GlobalNamespace" ); |
460 | ExpectedH.VirtualParents.emplace_back(Args: EmptySID, /*Name=*/Args: "G" , |
461 | Args: InfoType::IT_record, /*QualName=*/Args: "G" , |
462 | /*Path=*/Args: "GlobalNamespace" ); |
463 | ExpectedH.Bases.emplace_back(args: EmptySID, /*Name=*/args: "E" , |
464 | /*Path=*/args: "GlobalNamespace" , args: false, |
465 | args: AccessSpecifier::AS_private, args: true); |
466 | ExpectedH.Bases.emplace_back(args: EmptySID, /*Name=*/args: "F" , |
467 | /*Path=*/args: "GlobalNamespace" , args: false, |
468 | args: AccessSpecifier::AS_private, args: false); |
469 | FunctionInfo FunctionSetNew; |
470 | FunctionSetNew.Name = "set" ; |
471 | FunctionSetNew.ReturnType = TypeInfo("void" ); |
472 | FunctionSetNew.Loc.emplace_back(); |
473 | FunctionSetNew.Params.emplace_back(Args: TypeInfo("int" ), Args: "N" ); |
474 | FunctionSetNew.Namespace.emplace_back(Args: EmptySID, Args: "F" , Args: InfoType::IT_record); |
475 | FunctionSetNew.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
476 | Args: InfoType::IT_namespace); |
477 | FunctionSetNew.Access = AccessSpecifier::AS_private; |
478 | FunctionSetNew.IsMethod = true; |
479 | ExpectedH.Bases.back().Children.Functions.emplace_back( |
480 | args: std::move(FunctionSetNew)); |
481 | ExpectedH.Bases.emplace_back(args: EmptySID, /*Name=*/args: "G" , |
482 | /*Path=*/args: "GlobalNamespace" , args: true, |
483 | args: AccessSpecifier::AS_private, args: false); |
484 | FunctionInfo FunctionGetNew; |
485 | FunctionGetNew.Name = "get" ; |
486 | FunctionGetNew.ReturnType = TypeInfo("int" ); |
487 | FunctionGetNew.DefLoc = Location(); |
488 | FunctionGetNew.Namespace.emplace_back(Args: EmptySID, Args: "G" , Args: InfoType::IT_record); |
489 | FunctionGetNew.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
490 | Args: InfoType::IT_namespace); |
491 | FunctionGetNew.Access = AccessSpecifier::AS_private; |
492 | FunctionGetNew.IsMethod = true; |
493 | ExpectedH.Bases.back().Children.Functions.emplace_back( |
494 | args: std::move(FunctionGetNew)); |
495 | ExpectedH.Bases.back().Members.emplace_back(Args: TypeInfo("int" ), Args: "I" , |
496 | Args: AccessSpecifier::AS_private); |
497 | CheckRecordInfo(Expected: &ExpectedH, Actual: H); |
498 | |
499 | RecordInfo *I = InfoAsRecord(I: Infos[10].get()); |
500 | RecordInfo ExpectedI(EmptySID, /*Name=*/"I" , /*Path=*/"GlobalNamespace" ); |
501 | ExpectedI.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
502 | Args: InfoType::IT_namespace); |
503 | ExpectedI.TagType = TagTypeKind::Class; |
504 | ExpectedI.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp" }); |
505 | CheckRecordInfo(Expected: &ExpectedI, Actual: I); |
506 | |
507 | RecordInfo *J = InfoAsRecord(I: Infos[12].get()); |
508 | RecordInfo ExpectedJ(EmptySID, /*Name=*/"J" , /*Path=*/"GlobalNamespace" ); |
509 | ExpectedJ.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
510 | Args: InfoType::IT_namespace); |
511 | ExpectedJ.Parents.emplace_back(Args: EmptySID, /*Name=*/Args: "I<int>" , |
512 | Args: InfoType::IT_record); |
513 | ExpectedJ.Bases.emplace_back(args: EmptySID, /*Name=*/args: "I<int>" , |
514 | /*Path=*/args: "GlobalNamespace" , args: false, |
515 | args: AccessSpecifier::AS_public, args: true); |
516 | ExpectedJ.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp" }); |
517 | ExpectedJ.TagType = TagTypeKind::Class; |
518 | CheckRecordInfo(Expected: &ExpectedJ, Actual: J); |
519 | } |
520 | |
521 | TEST(SerializeTest, emitModulePublicLFunctions) { |
522 | EmittedInfoList Infos; |
523 | std::vector<std::string> Args; |
524 | Args.push_back(x: "-fmodules-ts" ); |
525 | ExtractInfosFromCodeWithArgs(Code: R"raw(export module M; |
526 | int moduleFunction(int x, double d = 3.2 - 1.0); |
527 | static int staticModuleFunction(int x); |
528 | export double exportedModuleFunction(double y);)raw" , |
529 | NumExpectedInfos: 2, /*Public=*/true, EmittedInfos&: Infos, Args); |
530 | |
531 | NamespaceInfo *BWithFunction = InfoAsNamespace(I: Infos[0].get()); |
532 | NamespaceInfo ExpectedBWithFunction(EmptySID); |
533 | FunctionInfo F; |
534 | F.Name = "moduleFunction" ; |
535 | F.ReturnType = TypeInfo("int" ); |
536 | F.Loc.emplace_back(Args: 0, Args: llvm::SmallString<16>{"test.cpp" }); |
537 | F.Params.emplace_back(Args: TypeInfo("int" ), Args: "x" ); |
538 | F.Params.emplace_back(Args: TypeInfo("double" ), Args: "d" ); |
539 | F.Params.back().DefaultValue = "3.2 - 1.0" ; |
540 | F.Access = AccessSpecifier::AS_none; |
541 | ExpectedBWithFunction.Children.Functions.emplace_back(args: std::move(F)); |
542 | CheckNamespaceInfo(Expected: &ExpectedBWithFunction, Actual: BWithFunction); |
543 | |
544 | NamespaceInfo *BWithExportedFunction = InfoAsNamespace(I: Infos[1].get()); |
545 | NamespaceInfo ExpectedBWithExportedFunction(EmptySID); |
546 | FunctionInfo ExportedF; |
547 | ExportedF.Name = "exportedModuleFunction" ; |
548 | ExportedF.ReturnType = |
549 | TypeInfo(Reference(EmptySID, "double" , InfoType::IT_default)); |
550 | ExportedF.Loc.emplace_back(Args: 0, Args: llvm::SmallString<16>{"test.cpp" }); |
551 | ExportedF.Params.emplace_back(Args: TypeInfo("double" ), Args: "y" ); |
552 | ExportedF.Access = AccessSpecifier::AS_none; |
553 | ExpectedBWithExportedFunction.Children.Functions.emplace_back( |
554 | args: std::move(ExportedF)); |
555 | CheckNamespaceInfo(Expected: &ExpectedBWithExportedFunction, Actual: BWithExportedFunction); |
556 | } |
557 | |
558 | // Test serialization of child records in namespaces and other records |
559 | TEST(SerializeTest, emitChildRecords) { |
560 | EmittedInfoList Infos; |
561 | ExtractInfosFromCode(Code: "class A { class B {}; }; namespace { class C {}; } " , NumExpectedInfos: 8, |
562 | /*Public=*/false, EmittedInfos&: Infos); |
563 | |
564 | NamespaceInfo *ParentA = InfoAsNamespace(I: Infos[1].get()); |
565 | NamespaceInfo ExpectedParentA(EmptySID); |
566 | ExpectedParentA.Children.Records.emplace_back( |
567 | args: EmptySID, args: "A" , args: InfoType::IT_record, args: "A" , args: "GlobalNamespace" ); |
568 | CheckNamespaceInfo(Expected: &ExpectedParentA, Actual: ParentA); |
569 | |
570 | RecordInfo *ParentB = InfoAsRecord(I: Infos[3].get()); |
571 | RecordInfo ExpectedParentB(EmptySID); |
572 | llvm::SmallString<128> ExpectedParentBPath("GlobalNamespace/A" ); |
573 | llvm::sys::path::native(path&: ExpectedParentBPath); |
574 | ExpectedParentB.Children.Records.emplace_back( |
575 | args: EmptySID, args: "B" , args: InfoType::IT_record, args: "A::B" , args&: ExpectedParentBPath); |
576 | CheckRecordInfo(Expected: &ExpectedParentB, Actual: ParentB); |
577 | |
578 | NamespaceInfo *ParentC = InfoAsNamespace(I: Infos[7].get()); |
579 | NamespaceInfo ExpectedParentC(EmptySID); |
580 | ExpectedParentC.Children.Records.emplace_back( |
581 | args: EmptySID, args: "C" , args: InfoType::IT_record, args: "C" , args: "@nonymous_namespace" ); |
582 | CheckNamespaceInfo(Expected: &ExpectedParentC, Actual: ParentC); |
583 | } |
584 | |
585 | // Test serialization of child namespaces |
586 | TEST(SerializeTest, emitChildNamespaces) { |
587 | EmittedInfoList Infos; |
588 | ExtractInfosFromCode(Code: "namespace A { namespace B { } }" , NumExpectedInfos: 4, /*Public=*/false, |
589 | EmittedInfos&: Infos); |
590 | |
591 | NamespaceInfo *ParentA = InfoAsNamespace(I: Infos[1].get()); |
592 | NamespaceInfo ExpectedParentA(EmptySID); |
593 | ExpectedParentA.Children.Namespaces.emplace_back(args: EmptySID, args: "A" , |
594 | args: InfoType::IT_namespace); |
595 | CheckNamespaceInfo(Expected: &ExpectedParentA, Actual: ParentA); |
596 | |
597 | NamespaceInfo *ParentB = InfoAsNamespace(I: Infos[3].get()); |
598 | NamespaceInfo ExpectedParentB(EmptySID); |
599 | ExpectedParentB.Children.Namespaces.emplace_back( |
600 | args: EmptySID, args: "B" , args: InfoType::IT_namespace, args: "A::B" , args: "A" ); |
601 | CheckNamespaceInfo(Expected: &ExpectedParentB, Actual: ParentB); |
602 | } |
603 | |
604 | TEST(SerializeTests, emitTypedefs) { |
605 | EmittedInfoList Infos; |
606 | ExtractInfosFromCode(Code: "typedef int MyInt; using MyDouble = double;" , NumExpectedInfos: 2, |
607 | /*Public=*/false, EmittedInfos&: Infos); |
608 | |
609 | // First info will be the global namespace with the typedef in it. |
610 | NamespaceInfo *GlobalNS1 = InfoAsNamespace(I: Infos[0].get()); |
611 | ASSERT_EQ(1u, GlobalNS1->Children.Typedefs.size()); |
612 | |
613 | const TypedefInfo &FirstTD = GlobalNS1->Children.Typedefs[0]; |
614 | EXPECT_EQ("MyInt" , FirstTD.Name); |
615 | EXPECT_FALSE(FirstTD.IsUsing); |
616 | EXPECT_EQ("int" , FirstTD.Underlying.Type.Name); |
617 | |
618 | // The second will be another global namespace with the using in it (the |
619 | // global namespace is duplicated because the items haven't been merged at the |
620 | // serialization phase of processing). |
621 | NamespaceInfo *GlobalNS2 = InfoAsNamespace(I: Infos[1].get()); |
622 | ASSERT_EQ(1u, GlobalNS2->Children.Typedefs.size()); |
623 | |
624 | // Second is the "using" typedef. |
625 | const TypedefInfo &SecondTD = GlobalNS2->Children.Typedefs[0]; |
626 | EXPECT_EQ("MyDouble" , SecondTD.Name); |
627 | EXPECT_TRUE(SecondTD.IsUsing); |
628 | EXPECT_EQ("double" , SecondTD.Underlying.Type.Name); |
629 | } |
630 | |
631 | TEST(SerializeTests, emitFunctionTemplate) { |
632 | EmittedInfoList Infos; |
633 | // A template and a specialization. |
634 | ExtractInfosFromCode(Code: "template<typename T = int> void GetFoo(T);\n" |
635 | "template<> void GetFoo<bool>(bool);" , |
636 | NumExpectedInfos: 2, |
637 | /*Public=*/false, EmittedInfos&: Infos); |
638 | |
639 | // First info will be the global namespace. |
640 | NamespaceInfo *GlobalNS1 = InfoAsNamespace(I: Infos[0].get()); |
641 | ASSERT_EQ(1u, GlobalNS1->Children.Functions.size()); |
642 | |
643 | const FunctionInfo &Func1 = GlobalNS1->Children.Functions[0]; |
644 | EXPECT_EQ("GetFoo" , Func1.Name); |
645 | ASSERT_TRUE(Func1.Template); |
646 | EXPECT_FALSE(Func1.Template->Specialization); // Not a specialization. |
647 | |
648 | // Template parameter. |
649 | ASSERT_EQ(1u, Func1.Template->Params.size()); |
650 | EXPECT_EQ("typename T = int" , Func1.Template->Params[0].Contents); |
651 | |
652 | // The second will be another global namespace with the function in it (the |
653 | // global namespace is duplicated because the items haven't been merged at the |
654 | // serialization phase of processing). |
655 | NamespaceInfo *GlobalNS2 = InfoAsNamespace(I: Infos[1].get()); |
656 | ASSERT_EQ(1u, GlobalNS2->Children.Functions.size()); |
657 | |
658 | // This one is a template specialization. |
659 | const FunctionInfo &Func2 = GlobalNS2->Children.Functions[0]; |
660 | EXPECT_EQ("GetFoo" , Func2.Name); |
661 | ASSERT_TRUE(Func2.Template); |
662 | EXPECT_TRUE(Func2.Template->Params.empty()); // No template params. |
663 | ASSERT_TRUE(Func2.Template->Specialization); |
664 | |
665 | // Specialization values. |
666 | ASSERT_EQ(1u, Func2.Template->Specialization->Params.size()); |
667 | EXPECT_EQ("bool" , Func2.Template->Specialization->Params[0].Contents); |
668 | EXPECT_EQ(Func1.USR, Func2.Template->Specialization->SpecializationOf); |
669 | } |
670 | |
671 | TEST(SerializeTests, emitClassTemplate) { |
672 | EmittedInfoList Infos; |
673 | // This will generate 2x the number of infos: each Record will be followed by |
674 | // a copy of the global namespace containing it (this test checks the data |
675 | // pre-merge). |
676 | ExtractInfosFromCode( |
677 | Code: "template<int I> class MyTemplate { int i[I]; };\n" |
678 | "template<> class MyTemplate<0> {};\n" |
679 | "template<typename T, int U = 1> class OtherTemplate {};\n" |
680 | "template<int U> class OtherTemplate<MyTemplate<0>, U> {};" , |
681 | NumExpectedInfos: 8, |
682 | /*Public=*/false, EmittedInfos&: Infos); |
683 | |
684 | // First record. |
685 | const RecordInfo *Rec1 = InfoAsRecord(I: Infos[0].get()); |
686 | EXPECT_EQ("MyTemplate" , Rec1->Name); |
687 | ASSERT_TRUE(Rec1->Template); |
688 | EXPECT_FALSE(Rec1->Template->Specialization); // Not a specialization. |
689 | |
690 | // First record template parameter. |
691 | ASSERT_EQ(1u, Rec1->Template->Params.size()); |
692 | EXPECT_EQ("int I" , Rec1->Template->Params[0].Contents); |
693 | |
694 | // Second record. |
695 | const RecordInfo *Rec2 = InfoAsRecord(I: Infos[2].get()); |
696 | EXPECT_EQ("MyTemplate" , Rec2->Name); |
697 | ASSERT_TRUE(Rec2->Template); |
698 | EXPECT_TRUE(Rec2->Template->Params.empty()); // No template params. |
699 | ASSERT_TRUE(Rec2->Template->Specialization); |
700 | |
701 | // Second record specialization values. |
702 | ASSERT_EQ(1u, Rec2->Template->Specialization->Params.size()); |
703 | EXPECT_EQ("0" , Rec2->Template->Specialization->Params[0].Contents); |
704 | EXPECT_EQ(Rec1->USR, Rec2->Template->Specialization->SpecializationOf); |
705 | |
706 | // Third record. |
707 | const RecordInfo *Rec3 = InfoAsRecord(I: Infos[4].get()); |
708 | EXPECT_EQ("OtherTemplate" , Rec3->Name); |
709 | ASSERT_TRUE(Rec3->Template); |
710 | |
711 | // Third record template parameters. |
712 | ASSERT_EQ(2u, Rec3->Template->Params.size()); |
713 | EXPECT_EQ("typename T" , Rec3->Template->Params[0].Contents); |
714 | EXPECT_EQ("int U = 1" , Rec3->Template->Params[1].Contents); |
715 | |
716 | // Fourth record. |
717 | const RecordInfo *Rec4 = InfoAsRecord(I: Infos[6].get()); |
718 | EXPECT_EQ("OtherTemplate" , Rec3->Name); |
719 | ASSERT_TRUE(Rec4->Template); |
720 | ASSERT_TRUE(Rec4->Template->Specialization); |
721 | |
722 | // Fourth record template + specialization parameters. |
723 | ASSERT_EQ(1u, Rec4->Template->Params.size()); |
724 | EXPECT_EQ("int U" , Rec4->Template->Params[0].Contents); |
725 | ASSERT_EQ(2u, Rec4->Template->Specialization->Params.size()); |
726 | EXPECT_EQ("MyTemplate<0>" , |
727 | Rec4->Template->Specialization->Params[0].Contents); |
728 | EXPECT_EQ("U" , Rec4->Template->Specialization->Params[1].Contents); |
729 | } |
730 | |
731 | } // namespace doc |
732 | } // end namespace clang |
733 | |