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 | Location Loc(0, 0, "test.cpp" ); |
40 | auto [Child, Parent] = serialize::emitInfo(D, getComment(D), Loc, Public); |
41 | if (Child) |
42 | EmittedInfos.emplace_back(std::move(Child)); |
43 | if (Parent) |
44 | EmittedInfos.emplace_back(std::move(Parent)); |
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, 0, "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, 0, "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, 0, "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: 0, Args: "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, 0, "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: 0, Args: "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, Args: 0, Args: "test.cpp" ); |
241 | SpecializedTemplateMethod.Namespace.emplace_back(Args: EmptySID, Args: "F" , |
242 | Args: InfoType::IT_record); |
243 | SpecializedTemplateMethod.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
244 | Args: InfoType::IT_namespace); |
245 | SpecializedTemplateMethod.Access = AccessSpecifier::AS_public; |
246 | SpecializedTemplateMethod.IsMethod = true; |
247 | ExpectedTemplatedRecord.Children.Functions.emplace_back( |
248 | args: std::move(SpecializedTemplateMethod)); |
249 | CheckRecordInfo(Expected: &ExpectedTemplatedRecord, Actual: TemplatedRecord); |
250 | |
251 | RecordInfo *G = InfoAsRecord(I: Infos[8].get()); |
252 | RecordInfo ExpectedG(EmptySID, /*Name=*/"G" , /*Path=*/"GlobalNamespace" ); |
253 | ExpectedG.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
254 | Args: InfoType::IT_namespace); |
255 | ExpectedG.TagType = TagTypeKind::Struct; |
256 | ExpectedG.DefLoc = Location(0, 0, "test.cpp" ); |
257 | ExpectedG.IsTypeDef = true; |
258 | CheckRecordInfo(Expected: &ExpectedG, Actual: G); |
259 | } |
260 | |
261 | // Test serialization of enum declarations. |
262 | TEST(SerializeTest, emitEnumInfo) { |
263 | EmittedInfoList Infos; |
264 | ExtractInfosFromCode(Code: "enum E { X, Y }; enum class G { A, B };" , NumExpectedInfos: 2, |
265 | /*Public=*/false, EmittedInfos&: Infos); |
266 | |
267 | NamespaceInfo *NamespaceWithEnum = InfoAsNamespace(I: Infos[0].get()); |
268 | NamespaceInfo ExpectedNamespaceWithEnum(EmptySID); |
269 | EnumInfo E; |
270 | E.Name = "E" ; |
271 | E.DefLoc = Location(0, 0, "test.cpp" ); |
272 | E.Members.emplace_back(Args: "X" , Args: "0" ); |
273 | E.Members.emplace_back(Args: "Y" , Args: "1" ); |
274 | ExpectedNamespaceWithEnum.Children.Enums.emplace_back(args: std::move(E)); |
275 | CheckNamespaceInfo(Expected: &ExpectedNamespaceWithEnum, Actual: NamespaceWithEnum); |
276 | |
277 | NamespaceInfo *NamespaceWithScopedEnum = InfoAsNamespace(I: Infos[1].get()); |
278 | NamespaceInfo ExpectedNamespaceWithScopedEnum(EmptySID); |
279 | EnumInfo G; |
280 | G.Name = "G" ; |
281 | G.Scoped = true; |
282 | G.DefLoc = Location(0, 0, "test.cpp" ); |
283 | G.Members.emplace_back(Args: "A" , Args: "0" ); |
284 | G.Members.emplace_back(Args: "B" , Args: "1" ); |
285 | ExpectedNamespaceWithScopedEnum.Children.Enums.emplace_back(args: std::move(G)); |
286 | CheckNamespaceInfo(Expected: &ExpectedNamespaceWithScopedEnum, Actual: NamespaceWithScopedEnum); |
287 | } |
288 | |
289 | TEST(SerializeTest, emitUndefinedRecordInfo) { |
290 | EmittedInfoList Infos; |
291 | ExtractInfosFromCode(Code: "class E;" , NumExpectedInfos: 2, /*Public=*/false, EmittedInfos&: Infos); |
292 | |
293 | RecordInfo *E = InfoAsRecord(I: Infos[0].get()); |
294 | RecordInfo ExpectedE(EmptySID, /*Name=*/"E" , /*Path=*/"GlobalNamespace" ); |
295 | ExpectedE.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
296 | Args: InfoType::IT_namespace); |
297 | ExpectedE.TagType = TagTypeKind::Class; |
298 | ExpectedE.Loc.emplace_back(Args: 0, Args: 0, Args: "test.cpp" ); |
299 | CheckRecordInfo(Expected: &ExpectedE, Actual: E); |
300 | } |
301 | |
302 | TEST(SerializeTest, emitRecordMemberInfo) { |
303 | EmittedInfoList Infos; |
304 | ExtractInfosFromCode(Code: "struct E { int I; };" , NumExpectedInfos: 2, /*Public=*/false, EmittedInfos&: Infos); |
305 | |
306 | RecordInfo *E = InfoAsRecord(I: Infos[0].get()); |
307 | RecordInfo ExpectedE(EmptySID, /*Name=*/"E" , /*Path=*/"GlobalNamespace" ); |
308 | ExpectedE.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
309 | Args: InfoType::IT_namespace); |
310 | ExpectedE.TagType = TagTypeKind::Struct; |
311 | ExpectedE.DefLoc = Location(0, 0, "test.cpp" ); |
312 | ExpectedE.Members.emplace_back(Args: TypeInfo("int" ), Args: "I" , |
313 | Args: AccessSpecifier::AS_public); |
314 | CheckRecordInfo(Expected: &ExpectedE, Actual: E); |
315 | } |
316 | |
317 | TEST(SerializeTest, emitInternalRecordInfo) { |
318 | EmittedInfoList Infos; |
319 | ExtractInfosFromCode(Code: "class E { class G {}; };" , NumExpectedInfos: 4, /*Public=*/false, EmittedInfos&: Infos); |
320 | |
321 | RecordInfo *E = InfoAsRecord(I: Infos[0].get()); |
322 | RecordInfo ExpectedE(EmptySID, /*Name=*/"E" , /*Path=*/"GlobalNamespace" ); |
323 | ExpectedE.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
324 | Args: InfoType::IT_namespace); |
325 | ExpectedE.DefLoc = Location(0, 0, "test.cpp" ); |
326 | ExpectedE.TagType = TagTypeKind::Class; |
327 | CheckRecordInfo(Expected: &ExpectedE, Actual: E); |
328 | |
329 | RecordInfo *G = InfoAsRecord(I: Infos[2].get()); |
330 | llvm::SmallString<128> ExpectedGPath("GlobalNamespace/E" ); |
331 | llvm::sys::path::native(path&: ExpectedGPath); |
332 | RecordInfo ExpectedG(EmptySID, /*Name=*/"G" , /*Path=*/ExpectedGPath); |
333 | ExpectedG.DefLoc = Location(0, 0, "test.cpp" ); |
334 | ExpectedG.TagType = TagTypeKind::Class; |
335 | ExpectedG.Namespace.emplace_back(Args: EmptySID, Args: "E" , Args: InfoType::IT_record); |
336 | ExpectedG.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
337 | Args: InfoType::IT_namespace); |
338 | CheckRecordInfo(Expected: &ExpectedG, Actual: G); |
339 | } |
340 | |
341 | TEST(SerializeTest, emitPublicAnonymousNamespaceInfo) { |
342 | EmittedInfoList Infos; |
343 | ExtractInfosFromCode(Code: "namespace { class A; }" , NumExpectedInfos: 0, /*Public=*/true, EmittedInfos&: Infos); |
344 | } |
345 | |
346 | TEST(SerializeTest, emitPublicFunctionInternalInfo) { |
347 | EmittedInfoList Infos; |
348 | ExtractInfosFromCode(Code: "int F() { class G {}; return 0; };" , NumExpectedInfos: 1, /*Public=*/true, |
349 | EmittedInfos&: Infos); |
350 | |
351 | NamespaceInfo *BWithFunction = InfoAsNamespace(I: Infos[0].get()); |
352 | NamespaceInfo ExpectedBWithFunction(EmptySID); |
353 | FunctionInfo F; |
354 | F.Name = "F" ; |
355 | F.ReturnType = TypeInfo("int" ); |
356 | F.DefLoc = Location(0, 0, "test.cpp" ); |
357 | F.Access = AccessSpecifier::AS_none; |
358 | ExpectedBWithFunction.Children.Functions.emplace_back(args: std::move(F)); |
359 | CheckNamespaceInfo(Expected: &ExpectedBWithFunction, Actual: BWithFunction); |
360 | } |
361 | |
362 | TEST(SerializeTest, emitInlinedFunctionInfo) { |
363 | EmittedInfoList Infos; |
364 | ExtractInfosFromCode(Code: "inline void F(int I) { };" , NumExpectedInfos: 1, /*Public=*/true, EmittedInfos&: Infos); |
365 | |
366 | NamespaceInfo *BWithFunction = InfoAsNamespace(I: Infos[0].get()); |
367 | NamespaceInfo ExpectedBWithFunction(EmptySID); |
368 | FunctionInfo F; |
369 | F.Name = "F" ; |
370 | F.ReturnType = TypeInfo("void" ); |
371 | F.DefLoc = Location(0, 0, "test.cpp" ); |
372 | F.Params.emplace_back(Args: TypeInfo("int" ), Args: "I" ); |
373 | F.Access = AccessSpecifier::AS_none; |
374 | ExpectedBWithFunction.Children.Functions.emplace_back(args: std::move(F)); |
375 | CheckNamespaceInfo(Expected: &ExpectedBWithFunction, Actual: BWithFunction); |
376 | } |
377 | |
378 | TEST(SerializeTest, emitInheritedRecordInfo) { |
379 | EmittedInfoList Infos; |
380 | ExtractInfosFromCode(Code: R"raw(class F { protected: void set(int N); }; |
381 | class G { public: int get() { return 1; } protected: int I; }; |
382 | class E : public F, virtual private G {}; |
383 | class H : private E {}; |
384 | template <typename T> |
385 | class I {} ; |
386 | class J : public I<int> {} ;)raw" , |
387 | NumExpectedInfos: 14, /*Public=*/false, EmittedInfos&: Infos); |
388 | |
389 | RecordInfo *F = InfoAsRecord(I: Infos[0].get()); |
390 | RecordInfo ExpectedF(EmptySID, /*Name=*/"F" , /*Path=*/"GlobalNamespace" ); |
391 | ExpectedF.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
392 | Args: InfoType::IT_namespace, Args: "" ); |
393 | ExpectedF.TagType = TagTypeKind::Class; |
394 | ExpectedF.DefLoc = Location(0, 0, "test.cpp" ); |
395 | CheckRecordInfo(Expected: &ExpectedF, Actual: F); |
396 | |
397 | RecordInfo *G = InfoAsRecord(I: Infos[3].get()); |
398 | RecordInfo ExpectedG(EmptySID, /*Name=*/"G" , /*Path=*/"GlobalNamespace" ); |
399 | ExpectedG.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
400 | Args: InfoType::IT_namespace); |
401 | ExpectedG.TagType = TagTypeKind::Class; |
402 | ExpectedG.DefLoc = Location(0, 0, "test.cpp" ); |
403 | ExpectedG.Members.emplace_back(Args: TypeInfo("int" ), Args: "I" , |
404 | Args: AccessSpecifier::AS_protected); |
405 | CheckRecordInfo(Expected: &ExpectedG, Actual: G); |
406 | |
407 | RecordInfo *E = InfoAsRecord(I: Infos[6].get()); |
408 | RecordInfo ExpectedE(EmptySID, /*Name=*/"E" , /*Path=*/"GlobalNamespace" ); |
409 | ExpectedE.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
410 | Args: InfoType::IT_namespace); |
411 | ExpectedE.Parents.emplace_back(Args: EmptySID, /*Name=*/Args: "F" , Args: InfoType::IT_record, |
412 | /*QualName=*/Args: "" , /*Path*=*/Args: "GlobalNamespace" ); |
413 | ExpectedE.VirtualParents.emplace_back(Args: EmptySID, /*Name=*/Args: "G" , |
414 | Args: InfoType::IT_record, /*QualName=*/Args: "G" , |
415 | /*Path*=*/Args: "GlobalNamespace" ); |
416 | ExpectedE.Bases.emplace_back(args: EmptySID, /*Name=*/args: "F" , |
417 | /*Path=*/args: "GlobalNamespace" , args: false, |
418 | args: AccessSpecifier::AS_public, args: true); |
419 | FunctionInfo FunctionSet; |
420 | FunctionSet.Name = "set" ; |
421 | FunctionSet.ReturnType = TypeInfo("void" ); |
422 | FunctionSet.Loc.emplace_back(); |
423 | FunctionSet.Params.emplace_back(Args: TypeInfo("int" ), Args: "N" ); |
424 | FunctionSet.Namespace.emplace_back(Args: EmptySID, Args: "F" , Args: InfoType::IT_record); |
425 | FunctionSet.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
426 | Args: InfoType::IT_namespace); |
427 | FunctionSet.Access = AccessSpecifier::AS_protected; |
428 | FunctionSet.IsMethod = true; |
429 | ExpectedE.Bases.back().Children.Functions.emplace_back( |
430 | args: std::move(FunctionSet)); |
431 | ExpectedE.Bases.emplace_back(args: EmptySID, /*Name=*/args: "G" , |
432 | /*Path=*/args: "GlobalNamespace" , args: true, |
433 | args: AccessSpecifier::AS_private, args: true); |
434 | FunctionInfo FunctionGet; |
435 | FunctionGet.Name = "get" ; |
436 | FunctionGet.ReturnType = TypeInfo("int" ); |
437 | FunctionGet.DefLoc = Location(); |
438 | FunctionGet.Namespace.emplace_back(Args: EmptySID, Args: "G" , Args: InfoType::IT_record); |
439 | FunctionGet.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
440 | Args: InfoType::IT_namespace); |
441 | FunctionGet.Access = AccessSpecifier::AS_private; |
442 | FunctionGet.IsMethod = true; |
443 | ExpectedE.Bases.back().Children.Functions.emplace_back( |
444 | args: std::move(FunctionGet)); |
445 | ExpectedE.Bases.back().Members.emplace_back(Args: TypeInfo("int" ), Args: "I" , |
446 | Args: AccessSpecifier::AS_private); |
447 | ExpectedE.DefLoc = Location(0, 0, "test.cpp" ); |
448 | ExpectedE.TagType = TagTypeKind::Class; |
449 | CheckRecordInfo(Expected: &ExpectedE, Actual: E); |
450 | |
451 | RecordInfo *H = InfoAsRecord(I: Infos[8].get()); |
452 | RecordInfo ExpectedH(EmptySID, /*Name=*/"H" , /*Path=*/"GlobalNamespace" ); |
453 | ExpectedH.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
454 | Args: InfoType::IT_namespace); |
455 | ExpectedH.TagType = TagTypeKind::Class; |
456 | ExpectedH.DefLoc = Location(0, 0, "test.cpp" ); |
457 | ExpectedH.Parents.emplace_back(Args: EmptySID, /*Name=*/Args: "E" , Args: InfoType::IT_record, |
458 | /*QualName=*/Args: "E" , /*Path=*/Args: "GlobalNamespace" ); |
459 | ExpectedH.VirtualParents.emplace_back(Args: EmptySID, /*Name=*/Args: "G" , |
460 | Args: InfoType::IT_record, /*QualName=*/Args: "G" , |
461 | /*Path=*/Args: "GlobalNamespace" ); |
462 | ExpectedH.Bases.emplace_back(args: EmptySID, /*Name=*/args: "E" , |
463 | /*Path=*/args: "GlobalNamespace" , args: false, |
464 | args: AccessSpecifier::AS_private, args: true); |
465 | ExpectedH.Bases.emplace_back(args: EmptySID, /*Name=*/args: "F" , |
466 | /*Path=*/args: "GlobalNamespace" , args: false, |
467 | args: AccessSpecifier::AS_private, args: false); |
468 | FunctionInfo FunctionSetNew; |
469 | FunctionSetNew.Name = "set" ; |
470 | FunctionSetNew.ReturnType = TypeInfo("void" ); |
471 | FunctionSetNew.Loc.emplace_back(); |
472 | FunctionSetNew.Params.emplace_back(Args: TypeInfo("int" ), Args: "N" ); |
473 | FunctionSetNew.Namespace.emplace_back(Args: EmptySID, Args: "F" , Args: InfoType::IT_record); |
474 | FunctionSetNew.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
475 | Args: InfoType::IT_namespace); |
476 | FunctionSetNew.Access = AccessSpecifier::AS_private; |
477 | FunctionSetNew.IsMethod = true; |
478 | ExpectedH.Bases.back().Children.Functions.emplace_back( |
479 | args: std::move(FunctionSetNew)); |
480 | ExpectedH.Bases.emplace_back(args: EmptySID, /*Name=*/args: "G" , |
481 | /*Path=*/args: "GlobalNamespace" , args: true, |
482 | args: AccessSpecifier::AS_private, args: false); |
483 | FunctionInfo FunctionGetNew; |
484 | FunctionGetNew.Name = "get" ; |
485 | FunctionGetNew.ReturnType = TypeInfo("int" ); |
486 | FunctionGetNew.DefLoc = Location(); |
487 | FunctionGetNew.Namespace.emplace_back(Args: EmptySID, Args: "G" , Args: InfoType::IT_record); |
488 | FunctionGetNew.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
489 | Args: InfoType::IT_namespace); |
490 | FunctionGetNew.Access = AccessSpecifier::AS_private; |
491 | FunctionGetNew.IsMethod = true; |
492 | ExpectedH.Bases.back().Children.Functions.emplace_back( |
493 | args: std::move(FunctionGetNew)); |
494 | ExpectedH.Bases.back().Members.emplace_back(Args: TypeInfo("int" ), Args: "I" , |
495 | Args: AccessSpecifier::AS_private); |
496 | CheckRecordInfo(Expected: &ExpectedH, Actual: H); |
497 | |
498 | RecordInfo *I = InfoAsRecord(I: Infos[10].get()); |
499 | RecordInfo ExpectedI(EmptySID, /*Name=*/"I" , /*Path=*/"GlobalNamespace" ); |
500 | ExpectedI.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
501 | Args: InfoType::IT_namespace); |
502 | ExpectedI.TagType = TagTypeKind::Class; |
503 | ExpectedI.DefLoc = Location(0, 0, "test.cpp" ); |
504 | CheckRecordInfo(Expected: &ExpectedI, Actual: I); |
505 | |
506 | RecordInfo *J = InfoAsRecord(I: Infos[12].get()); |
507 | RecordInfo ExpectedJ(EmptySID, /*Name=*/"J" , /*Path=*/"GlobalNamespace" ); |
508 | ExpectedJ.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
509 | Args: InfoType::IT_namespace); |
510 | ExpectedJ.Parents.emplace_back(Args: EmptySID, /*Name=*/Args: "I<int>" , |
511 | Args: InfoType::IT_record); |
512 | ExpectedJ.Bases.emplace_back(args: EmptySID, /*Name=*/args: "I<int>" , |
513 | /*Path=*/args: "GlobalNamespace" , args: false, |
514 | args: AccessSpecifier::AS_public, args: true); |
515 | ExpectedJ.DefLoc = Location(0, 0, "test.cpp" ); |
516 | ExpectedJ.TagType = TagTypeKind::Class; |
517 | CheckRecordInfo(Expected: &ExpectedJ, Actual: J); |
518 | } |
519 | |
520 | TEST(SerializeTest, emitModulePublicLFunctions) { |
521 | EmittedInfoList Infos; |
522 | std::vector<std::string> Args; |
523 | Args.push_back(x: "-fmodules-ts" ); |
524 | ExtractInfosFromCodeWithArgs(Code: R"raw(export module M; |
525 | int moduleFunction(int x, double d = 3.2 - 1.0); |
526 | static int staticModuleFunction(int x); |
527 | export double exportedModuleFunction(double y);)raw" , |
528 | NumExpectedInfos: 2, /*Public=*/true, EmittedInfos&: Infos, Args); |
529 | |
530 | NamespaceInfo *BWithFunction = InfoAsNamespace(I: Infos[0].get()); |
531 | NamespaceInfo ExpectedBWithFunction(EmptySID); |
532 | FunctionInfo F; |
533 | F.Name = "moduleFunction" ; |
534 | F.ReturnType = TypeInfo("int" ); |
535 | F.Loc.emplace_back(Args: 0, Args: 0, Args: "test.cpp" ); |
536 | F.Params.emplace_back(Args: TypeInfo("int" ), Args: "x" ); |
537 | F.Params.emplace_back(Args: TypeInfo("double" ), Args: "d" ); |
538 | F.Params.back().DefaultValue = "3.2 - 1.0" ; |
539 | F.Access = AccessSpecifier::AS_none; |
540 | ExpectedBWithFunction.Children.Functions.emplace_back(args: std::move(F)); |
541 | CheckNamespaceInfo(Expected: &ExpectedBWithFunction, Actual: BWithFunction); |
542 | |
543 | NamespaceInfo *BWithExportedFunction = InfoAsNamespace(I: Infos[1].get()); |
544 | NamespaceInfo ExpectedBWithExportedFunction(EmptySID); |
545 | FunctionInfo ExportedF; |
546 | ExportedF.Name = "exportedModuleFunction" ; |
547 | ExportedF.ReturnType = |
548 | TypeInfo(Reference(EmptySID, "double" , InfoType::IT_default)); |
549 | ExportedF.Loc.emplace_back(Args: 0, Args: 0, Args: "test.cpp" ); |
550 | ExportedF.Params.emplace_back(Args: TypeInfo("double" ), Args: "y" ); |
551 | ExportedF.Access = AccessSpecifier::AS_none; |
552 | ExpectedBWithExportedFunction.Children.Functions.emplace_back( |
553 | args: std::move(ExportedF)); |
554 | CheckNamespaceInfo(Expected: &ExpectedBWithExportedFunction, Actual: BWithExportedFunction); |
555 | } |
556 | |
557 | // Test serialization of child records in namespaces and other records |
558 | TEST(SerializeTest, emitChildRecords) { |
559 | EmittedInfoList Infos; |
560 | ExtractInfosFromCode(Code: "class A { class B {}; }; namespace { class C {}; } " , NumExpectedInfos: 8, |
561 | /*Public=*/false, EmittedInfos&: Infos); |
562 | |
563 | NamespaceInfo *ParentA = InfoAsNamespace(I: Infos[1].get()); |
564 | NamespaceInfo ExpectedParentA(EmptySID); |
565 | ExpectedParentA.Children.Records.emplace_back( |
566 | args: EmptySID, args: "A" , args: InfoType::IT_record, args: "A" , args: "GlobalNamespace" ); |
567 | CheckNamespaceInfo(Expected: &ExpectedParentA, Actual: ParentA); |
568 | |
569 | RecordInfo *ParentB = InfoAsRecord(I: Infos[3].get()); |
570 | RecordInfo ExpectedParentB(EmptySID); |
571 | llvm::SmallString<128> ExpectedParentBPath("GlobalNamespace/A" ); |
572 | llvm::sys::path::native(path&: ExpectedParentBPath); |
573 | ExpectedParentB.Children.Records.emplace_back( |
574 | args: EmptySID, args: "B" , args: InfoType::IT_record, args: "A::B" , args&: ExpectedParentBPath); |
575 | CheckRecordInfo(Expected: &ExpectedParentB, Actual: ParentB); |
576 | |
577 | NamespaceInfo *ParentC = InfoAsNamespace(I: Infos[7].get()); |
578 | NamespaceInfo ExpectedParentC(EmptySID); |
579 | ExpectedParentC.Children.Records.emplace_back( |
580 | args: EmptySID, args: "C" , args: InfoType::IT_record, args: "C" , args: "@nonymous_namespace" ); |
581 | CheckNamespaceInfo(Expected: &ExpectedParentC, Actual: ParentC); |
582 | } |
583 | |
584 | // Test serialization of child namespaces |
585 | TEST(SerializeTest, emitChildNamespaces) { |
586 | EmittedInfoList Infos; |
587 | ExtractInfosFromCode(Code: "namespace A { namespace B { } }" , NumExpectedInfos: 4, /*Public=*/false, |
588 | EmittedInfos&: Infos); |
589 | |
590 | NamespaceInfo *ParentA = InfoAsNamespace(I: Infos[1].get()); |
591 | NamespaceInfo ExpectedParentA(EmptySID); |
592 | ExpectedParentA.Children.Namespaces.emplace_back(args: EmptySID, args: "A" , |
593 | args: InfoType::IT_namespace); |
594 | CheckNamespaceInfo(Expected: &ExpectedParentA, Actual: ParentA); |
595 | |
596 | NamespaceInfo *ParentB = InfoAsNamespace(I: Infos[3].get()); |
597 | NamespaceInfo ExpectedParentB(EmptySID); |
598 | ExpectedParentB.Children.Namespaces.emplace_back( |
599 | args: EmptySID, args: "B" , args: InfoType::IT_namespace, args: "A::B" , args: "A" ); |
600 | CheckNamespaceInfo(Expected: &ExpectedParentB, Actual: ParentB); |
601 | } |
602 | |
603 | TEST(SerializeTests, emitTypedefs) { |
604 | EmittedInfoList Infos; |
605 | ExtractInfosFromCode(Code: "typedef int MyInt; using MyDouble = double;" , NumExpectedInfos: 2, |
606 | /*Public=*/false, EmittedInfos&: Infos); |
607 | |
608 | // First info will be the global namespace with the typedef in it. |
609 | NamespaceInfo *GlobalNS1 = InfoAsNamespace(I: Infos[0].get()); |
610 | ASSERT_EQ(1u, GlobalNS1->Children.Typedefs.size()); |
611 | |
612 | const TypedefInfo &FirstTD = GlobalNS1->Children.Typedefs[0]; |
613 | EXPECT_EQ("MyInt" , FirstTD.Name); |
614 | EXPECT_FALSE(FirstTD.IsUsing); |
615 | EXPECT_EQ("int" , FirstTD.Underlying.Type.Name); |
616 | |
617 | // The second will be another global namespace with the using in it (the |
618 | // global namespace is duplicated because the items haven't been merged at the |
619 | // serialization phase of processing). |
620 | NamespaceInfo *GlobalNS2 = InfoAsNamespace(I: Infos[1].get()); |
621 | ASSERT_EQ(1u, GlobalNS2->Children.Typedefs.size()); |
622 | |
623 | // Second is the "using" typedef. |
624 | const TypedefInfo &SecondTD = GlobalNS2->Children.Typedefs[0]; |
625 | EXPECT_EQ("MyDouble" , SecondTD.Name); |
626 | EXPECT_TRUE(SecondTD.IsUsing); |
627 | EXPECT_EQ("double" , SecondTD.Underlying.Type.Name); |
628 | } |
629 | |
630 | TEST(SerializeTests, emitFunctionTemplate) { |
631 | EmittedInfoList Infos; |
632 | // A template and a specialization. |
633 | ExtractInfosFromCode(Code: "template<typename T = int> bool GetFoo(T);\n" |
634 | "template<> bool GetFoo<bool>(bool);" , |
635 | NumExpectedInfos: 2, |
636 | /*Public=*/false, EmittedInfos&: Infos); |
637 | |
638 | // First info will be the global namespace. |
639 | NamespaceInfo *GlobalNS1 = InfoAsNamespace(I: Infos[0].get()); |
640 | ASSERT_EQ(1u, GlobalNS1->Children.Functions.size()); |
641 | |
642 | const FunctionInfo &Func1 = GlobalNS1->Children.Functions[0]; |
643 | EXPECT_EQ("GetFoo" , Func1.Name); |
644 | ASSERT_TRUE(Func1.Template); |
645 | EXPECT_FALSE(Func1.Template->Specialization); // Not a specialization. |
646 | |
647 | // Template parameter. |
648 | ASSERT_EQ(1u, Func1.Template->Params.size()); |
649 | EXPECT_EQ("typename T = int" , Func1.Template->Params[0].Contents); |
650 | |
651 | // The second will be another global namespace with the function in it (the |
652 | // global namespace is duplicated because the items haven't been merged at the |
653 | // serialization phase of processing). |
654 | NamespaceInfo *GlobalNS2 = InfoAsNamespace(I: Infos[1].get()); |
655 | ASSERT_EQ(1u, GlobalNS2->Children.Functions.size()); |
656 | |
657 | // This one is a template specialization. |
658 | const FunctionInfo &Func2 = GlobalNS2->Children.Functions[0]; |
659 | EXPECT_EQ("GetFoo" , Func2.Name); |
660 | ASSERT_TRUE(Func2.Template); |
661 | EXPECT_TRUE(Func2.Template->Params.empty()); // No template params. |
662 | ASSERT_TRUE(Func2.Template->Specialization); |
663 | |
664 | // Specialization values. |
665 | ASSERT_EQ(1u, Func2.Template->Specialization->Params.size()); |
666 | EXPECT_EQ("bool" , Func2.Template->Specialization->Params[0].Contents); |
667 | EXPECT_EQ(Func1.USR, Func2.Template->Specialization->SpecializationOf); |
668 | |
669 | EXPECT_EQ("bool" , Func2.ReturnType.Type.Name); |
670 | } |
671 | |
672 | TEST(SerializeTests, emitClassTemplate) { |
673 | EmittedInfoList Infos; |
674 | // This will generate 2x the number of infos: each Record will be followed by |
675 | // a copy of the global namespace containing it (this test checks the data |
676 | // pre-merge). |
677 | ExtractInfosFromCode( |
678 | Code: "template<int I> class MyTemplate { int i[I]; };\n" |
679 | "template<> class MyTemplate<0> {};\n" |
680 | "template<typename T, int U = 1> class OtherTemplate {};\n" |
681 | "template<int U> class OtherTemplate<MyTemplate<0>, U> {};" , |
682 | NumExpectedInfos: 8, |
683 | /*Public=*/false, EmittedInfos&: Infos); |
684 | |
685 | // First record. |
686 | const RecordInfo *Rec1 = InfoAsRecord(I: Infos[0].get()); |
687 | EXPECT_EQ("MyTemplate" , Rec1->Name); |
688 | ASSERT_TRUE(Rec1->Template); |
689 | EXPECT_FALSE(Rec1->Template->Specialization); // Not a specialization. |
690 | |
691 | // First record template parameter. |
692 | ASSERT_EQ(1u, Rec1->Template->Params.size()); |
693 | EXPECT_EQ("int I" , Rec1->Template->Params[0].Contents); |
694 | |
695 | // Second record. |
696 | const RecordInfo *Rec2 = InfoAsRecord(I: Infos[2].get()); |
697 | EXPECT_EQ("MyTemplate" , Rec2->Name); |
698 | ASSERT_TRUE(Rec2->Template); |
699 | EXPECT_TRUE(Rec2->Template->Params.empty()); // No template params. |
700 | ASSERT_TRUE(Rec2->Template->Specialization); |
701 | |
702 | // Second record specialization values. |
703 | ASSERT_EQ(1u, Rec2->Template->Specialization->Params.size()); |
704 | EXPECT_EQ("0" , Rec2->Template->Specialization->Params[0].Contents); |
705 | EXPECT_EQ(Rec1->USR, Rec2->Template->Specialization->SpecializationOf); |
706 | |
707 | // Third record. |
708 | const RecordInfo *Rec3 = InfoAsRecord(I: Infos[4].get()); |
709 | EXPECT_EQ("OtherTemplate" , Rec3->Name); |
710 | ASSERT_TRUE(Rec3->Template); |
711 | |
712 | // Third record template parameters. |
713 | ASSERT_EQ(2u, Rec3->Template->Params.size()); |
714 | EXPECT_EQ("typename T" , Rec3->Template->Params[0].Contents); |
715 | EXPECT_EQ("int U = 1" , Rec3->Template->Params[1].Contents); |
716 | |
717 | // Fourth record. |
718 | const RecordInfo *Rec4 = InfoAsRecord(I: Infos[6].get()); |
719 | EXPECT_EQ("OtherTemplate" , Rec3->Name); |
720 | ASSERT_TRUE(Rec4->Template); |
721 | ASSERT_TRUE(Rec4->Template->Specialization); |
722 | |
723 | // Fourth record template + specialization parameters. |
724 | ASSERT_EQ(1u, Rec4->Template->Params.size()); |
725 | EXPECT_EQ("int U" , Rec4->Template->Params[0].Contents); |
726 | ASSERT_EQ(2u, Rec4->Template->Specialization->Params.size()); |
727 | EXPECT_EQ("MyTemplate<0>" , |
728 | Rec4->Template->Specialization->Params[0].Contents); |
729 | EXPECT_EQ("U" , Rec4->Template->Specialization->Params[1].Contents); |
730 | } |
731 | |
732 | } // namespace doc |
733 | } // end namespace clang |
734 | |