1 | //===-- clang-doc/BitcodeTest.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 "BitcodeReader.h" |
10 | #include "BitcodeWriter.h" |
11 | #include "ClangDocTest.h" |
12 | #include "Representation.h" |
13 | #include "llvm/Bitstream/BitstreamReader.h" |
14 | #include "llvm/Bitstream/BitstreamWriter.h" |
15 | #include "gtest/gtest.h" |
16 | |
17 | namespace clang { |
18 | namespace doc { |
19 | |
20 | template <typename T> static std::string writeInfo(T &I) { |
21 | SmallString<2048> Buffer; |
22 | llvm::BitstreamWriter Stream(Buffer); |
23 | ClangDocBitcodeWriter Writer(Stream); |
24 | Writer.emitBlock(I); |
25 | return Buffer.str().str(); |
26 | } |
27 | |
28 | std::string writeInfo(Info *I) { |
29 | switch (I->IT) { |
30 | case InfoType::IT_namespace: |
31 | return writeInfo(I&: *static_cast<NamespaceInfo *>(I)); |
32 | case InfoType::IT_record: |
33 | return writeInfo(I&: *static_cast<RecordInfo *>(I)); |
34 | case InfoType::IT_enum: |
35 | return writeInfo(I&: *static_cast<EnumInfo *>(I)); |
36 | case InfoType::IT_function: |
37 | return writeInfo(I&: *static_cast<FunctionInfo *>(I)); |
38 | case InfoType::IT_typedef: |
39 | return writeInfo(I&: *static_cast<TypedefInfo *>(I)); |
40 | default: |
41 | return "" ; |
42 | } |
43 | } |
44 | |
45 | std::vector<std::unique_ptr<Info>> readInfo(StringRef Bitcode, |
46 | size_t NumInfos) { |
47 | llvm::BitstreamCursor Stream(Bitcode); |
48 | doc::ClangDocBitcodeReader Reader(Stream); |
49 | auto Infos = Reader.readBitcode(); |
50 | |
51 | // Check that there was no error in the read. |
52 | assert(Infos); |
53 | EXPECT_EQ(Infos.get().size(), NumInfos); |
54 | return std::move(Infos.get()); |
55 | } |
56 | |
57 | TEST(BitcodeTest, emitNamespaceInfoBitcode) { |
58 | NamespaceInfo I; |
59 | I.Name = "r" ; |
60 | I.Namespace.emplace_back(Args: EmptySID, Args: "A" , Args: InfoType::IT_namespace); |
61 | |
62 | I.Children.Namespaces.emplace_back(args: EmptySID, args: "ChildNamespace" , |
63 | args: InfoType::IT_namespace); |
64 | I.Children.Records.emplace_back(args: EmptySID, args: "ChildStruct" , args: InfoType::IT_record); |
65 | I.Children.Functions.emplace_back(); |
66 | I.Children.Enums.emplace_back(); |
67 | |
68 | std::string WriteResult = writeInfo(I: &I); |
69 | EXPECT_TRUE(WriteResult.size() > 0); |
70 | std::vector<std::unique_ptr<Info>> ReadResults = readInfo(Bitcode: WriteResult, NumInfos: 1); |
71 | |
72 | CheckNamespaceInfo(Expected: &I, Actual: InfoAsNamespace(I: ReadResults[0].get())); |
73 | } |
74 | |
75 | TEST(BitcodeTest, emitRecordInfoBitcode) { |
76 | RecordInfo I; |
77 | I.Name = "r" ; |
78 | I.Namespace.emplace_back(Args: EmptySID, Args: "A" , Args: InfoType::IT_namespace); |
79 | |
80 | I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp" }); |
81 | I.Loc.emplace_back(Args: 12, Args: llvm::SmallString<16>{"test.cpp" }); |
82 | |
83 | I.Members.emplace_back(Args: TypeInfo("int" ), Args: "X" , Args: AccessSpecifier::AS_private); |
84 | I.TagType = TagTypeKind::Class; |
85 | I.IsTypeDef = true; |
86 | I.Bases.emplace_back(args: EmptySID, args: "F" , args: "path/to/F" , args: true, |
87 | args: AccessSpecifier::AS_public, args: true); |
88 | I.Bases.back().Children.Functions.emplace_back(); |
89 | I.Bases.back().Members.emplace_back(Args: TypeInfo("int" ), Args: "X" , |
90 | Args: AccessSpecifier::AS_private); |
91 | I.Parents.emplace_back(Args: EmptySID, Args: "F" , Args: InfoType::IT_record); |
92 | I.VirtualParents.emplace_back(Args: EmptySID, Args: "G" , Args: InfoType::IT_record); |
93 | |
94 | // Documentation for the data member. |
95 | CommentInfo ; |
96 | TopComment.Kind = "FullComment" ; |
97 | TopComment.Children.emplace_back(args: std::make_unique<CommentInfo>()); |
98 | CommentInfo *Brief = TopComment.Children.back().get(); |
99 | Brief->Kind = "ParagraphComment" ; |
100 | Brief->Children.emplace_back(args: std::make_unique<CommentInfo>()); |
101 | Brief->Children.back()->Kind = "TextComment" ; |
102 | Brief->Children.back()->Name = "ParagraphComment" ; |
103 | Brief->Children.back()->Text = "Value of the thing." ; |
104 | I.Bases.back().Members.back().Description.emplace_back(args: std::move(TopComment)); |
105 | |
106 | I.Children.Records.emplace_back(args: EmptySID, args: "ChildStruct" , args: InfoType::IT_record); |
107 | I.Children.Functions.emplace_back(); |
108 | I.Children.Enums.emplace_back(); |
109 | |
110 | std::string WriteResult = writeInfo(I: &I); |
111 | EXPECT_TRUE(WriteResult.size() > 0); |
112 | std::vector<std::unique_ptr<Info>> ReadResults = readInfo(Bitcode: WriteResult, NumInfos: 1); |
113 | |
114 | CheckRecordInfo(Expected: &I, Actual: InfoAsRecord(I: ReadResults[0].get())); |
115 | } |
116 | |
117 | TEST(BitcodeTest, emitFunctionInfoBitcode) { |
118 | FunctionInfo I; |
119 | I.Name = "f" ; |
120 | I.Namespace.emplace_back(Args: EmptySID, Args: "A" , Args: InfoType::IT_namespace); |
121 | |
122 | I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp" }); |
123 | I.Loc.emplace_back(Args: 12, Args: llvm::SmallString<16>{"test.cpp" }); |
124 | |
125 | I.ReturnType = TypeInfo("void" ); |
126 | I.Params.emplace_back(Args: TypeInfo("int" ), Args: "P" ); |
127 | |
128 | I.Access = AccessSpecifier::AS_none; |
129 | |
130 | std::string WriteResult = writeInfo(I: &I); |
131 | EXPECT_TRUE(WriteResult.size() > 0); |
132 | std::vector<std::unique_ptr<Info>> ReadResults = readInfo(Bitcode: WriteResult, NumInfos: 1); |
133 | |
134 | CheckFunctionInfo(Expected: &I, Actual: InfoAsFunction(I: ReadResults[0].get())); |
135 | } |
136 | |
137 | TEST(BitcodeTest, emitMethodInfoBitcode) { |
138 | FunctionInfo I; |
139 | I.Name = "f" ; |
140 | I.Namespace.emplace_back(Args: EmptySID, Args: "A" , Args: InfoType::IT_namespace); |
141 | |
142 | I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp" }); |
143 | I.Loc.emplace_back(Args: 12, Args: llvm::SmallString<16>{"test.cpp" }); |
144 | |
145 | I.ReturnType = TypeInfo("void" ); |
146 | I.Params.emplace_back(Args: TypeInfo("int" ), Args: "P" ); |
147 | I.IsMethod = true; |
148 | I.Parent = Reference(EmptySID, "Parent" , InfoType::IT_record); |
149 | |
150 | I.Access = AccessSpecifier::AS_public; |
151 | |
152 | std::string WriteResult = writeInfo(I: &I); |
153 | EXPECT_TRUE(WriteResult.size() > 0); |
154 | std::vector<std::unique_ptr<Info>> ReadResults = readInfo(Bitcode: WriteResult, NumInfos: 1); |
155 | |
156 | CheckFunctionInfo(Expected: &I, Actual: InfoAsFunction(I: ReadResults[0].get())); |
157 | } |
158 | |
159 | TEST(BitcodeTest, emitEnumInfoBitcode) { |
160 | EnumInfo I; |
161 | I.Name = "e" ; |
162 | I.Namespace.emplace_back(Args: EmptySID, Args: "A" , Args: InfoType::IT_namespace); |
163 | |
164 | I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp" }); |
165 | I.Loc.emplace_back(Args: 12, Args: llvm::SmallString<16>{"test.cpp" }); |
166 | |
167 | I.Members.emplace_back(Args: "X" ); |
168 | I.Scoped = true; |
169 | |
170 | std::string WriteResult = writeInfo(I: &I); |
171 | EXPECT_TRUE(WriteResult.size() > 0); |
172 | std::vector<std::unique_ptr<Info>> ReadResults = readInfo(Bitcode: WriteResult, NumInfos: 1); |
173 | |
174 | CheckEnumInfo(Expected: &I, Actual: InfoAsEnum(I: ReadResults[0].get())); |
175 | } |
176 | |
177 | TEST(BitcodeTest, emitTypedefInfoBitcode) { |
178 | TypedefInfo I; |
179 | I.Name = "MyInt" ; |
180 | I.Namespace.emplace_back(Args: EmptySID, Args: "A" , Args: InfoType::IT_namespace); |
181 | |
182 | I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp" }); |
183 | I.Underlying = TypeInfo("unsigned" ); |
184 | I.IsUsing = true; |
185 | |
186 | CommentInfo Top; |
187 | Top.Kind = "FullComment" ; |
188 | |
189 | Top.Children.emplace_back(args: std::make_unique<CommentInfo>()); |
190 | CommentInfo *BlankLine = Top.Children.back().get(); |
191 | BlankLine->Kind = "ParagraphComment" ; |
192 | BlankLine->Children.emplace_back(args: std::make_unique<CommentInfo>()); |
193 | BlankLine->Children.back()->Kind = "TextComment" ; |
194 | |
195 | I.Description.emplace_back(args: std::move(Top)); |
196 | |
197 | std::string WriteResult = writeInfo(I: &I); |
198 | EXPECT_TRUE(WriteResult.size() > 0); |
199 | std::vector<std::unique_ptr<Info>> ReadResults = readInfo(Bitcode: WriteResult, NumInfos: 1); |
200 | |
201 | CheckTypedefInfo(Expected: &I, Actual: InfoAsTypedef(I: ReadResults[0].get())); |
202 | |
203 | // Check one with no IsUsing set, no description, and no definition location. |
204 | TypedefInfo I2; |
205 | I2.Name = "SomethingElse" ; |
206 | I2.IsUsing = false; |
207 | I2.Underlying = TypeInfo("int" ); |
208 | |
209 | WriteResult = writeInfo(I: &I2); |
210 | EXPECT_TRUE(WriteResult.size() > 0); |
211 | ReadResults = readInfo(Bitcode: WriteResult, NumInfos: 1); |
212 | CheckTypedefInfo(Expected: &I2, Actual: InfoAsTypedef(I: ReadResults[0].get())); |
213 | } |
214 | |
215 | TEST(SerializeTest, emitInfoWithCommentBitcode) { |
216 | FunctionInfo F; |
217 | F.Name = "F" ; |
218 | F.ReturnType = TypeInfo("void" ); |
219 | F.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp" }); |
220 | F.Params.emplace_back(Args: TypeInfo("int" ), Args: "I" ); |
221 | |
222 | CommentInfo Top; |
223 | Top.Kind = "FullComment" ; |
224 | |
225 | Top.Children.emplace_back(args: std::make_unique<CommentInfo>()); |
226 | CommentInfo *BlankLine = Top.Children.back().get(); |
227 | BlankLine->Kind = "ParagraphComment" ; |
228 | BlankLine->Children.emplace_back(args: std::make_unique<CommentInfo>()); |
229 | BlankLine->Children.back()->Kind = "TextComment" ; |
230 | |
231 | Top.Children.emplace_back(args: std::make_unique<CommentInfo>()); |
232 | CommentInfo *Brief = Top.Children.back().get(); |
233 | Brief->Kind = "ParagraphComment" ; |
234 | Brief->Children.emplace_back(args: std::make_unique<CommentInfo>()); |
235 | Brief->Children.back()->Kind = "TextComment" ; |
236 | Brief->Children.back()->Name = "ParagraphComment" ; |
237 | Brief->Children.back()->Text = " Brief description." ; |
238 | |
239 | Top.Children.emplace_back(args: std::make_unique<CommentInfo>()); |
240 | CommentInfo *Extended = Top.Children.back().get(); |
241 | Extended->Kind = "ParagraphComment" ; |
242 | Extended->Children.emplace_back(args: std::make_unique<CommentInfo>()); |
243 | Extended->Children.back()->Kind = "TextComment" ; |
244 | Extended->Children.back()->Text = " Extended description that" ; |
245 | Extended->Children.emplace_back(args: std::make_unique<CommentInfo>()); |
246 | Extended->Children.back()->Kind = "TextComment" ; |
247 | Extended->Children.back()->Text = " continues onto the next line." ; |
248 | |
249 | Top.Children.emplace_back(args: std::make_unique<CommentInfo>()); |
250 | CommentInfo *HTML = Top.Children.back().get(); |
251 | HTML->Kind = "ParagraphComment" ; |
252 | HTML->Children.emplace_back(args: std::make_unique<CommentInfo>()); |
253 | HTML->Children.back()->Kind = "TextComment" ; |
254 | HTML->Children.emplace_back(args: std::make_unique<CommentInfo>()); |
255 | HTML->Children.back()->Kind = "HTMLStartTagComment" ; |
256 | HTML->Children.back()->Name = "ul" ; |
257 | HTML->Children.back()->AttrKeys.emplace_back(Args: "class" ); |
258 | HTML->Children.back()->AttrValues.emplace_back(Args: "test" ); |
259 | HTML->Children.emplace_back(args: std::make_unique<CommentInfo>()); |
260 | HTML->Children.back()->Kind = "HTMLStartTagComment" ; |
261 | HTML->Children.back()->Name = "li" ; |
262 | HTML->Children.emplace_back(args: std::make_unique<CommentInfo>()); |
263 | HTML->Children.back()->Kind = "TextComment" ; |
264 | HTML->Children.back()->Text = " Testing." ; |
265 | HTML->Children.emplace_back(args: std::make_unique<CommentInfo>()); |
266 | HTML->Children.back()->Kind = "HTMLEndTagComment" ; |
267 | HTML->Children.back()->Name = "ul" ; |
268 | HTML->Children.back()->SelfClosing = true; |
269 | |
270 | Top.Children.emplace_back(args: std::make_unique<CommentInfo>()); |
271 | CommentInfo *Verbatim = Top.Children.back().get(); |
272 | Verbatim->Kind = "VerbatimBlockComment" ; |
273 | Verbatim->Name = "verbatim" ; |
274 | Verbatim->CloseName = "endverbatim" ; |
275 | Verbatim->Children.emplace_back(args: std::make_unique<CommentInfo>()); |
276 | Verbatim->Children.back()->Kind = "VerbatimBlockLineComment" ; |
277 | Verbatim->Children.back()->Text = " The description continues." ; |
278 | |
279 | Top.Children.emplace_back(args: std::make_unique<CommentInfo>()); |
280 | CommentInfo *ParamOut = Top.Children.back().get(); |
281 | ParamOut->Kind = "ParamCommandComment" ; |
282 | ParamOut->Direction = "[out]" ; |
283 | ParamOut->ParamName = "I" ; |
284 | ParamOut->Explicit = true; |
285 | ParamOut->Children.emplace_back(args: std::make_unique<CommentInfo>()); |
286 | ParamOut->Children.back()->Kind = "ParagraphComment" ; |
287 | ParamOut->Children.back()->Children.emplace_back( |
288 | args: std::make_unique<CommentInfo>()); |
289 | ParamOut->Children.back()->Children.back()->Kind = "TextComment" ; |
290 | ParamOut->Children.back()->Children.emplace_back( |
291 | args: std::make_unique<CommentInfo>()); |
292 | ParamOut->Children.back()->Children.back()->Kind = "TextComment" ; |
293 | ParamOut->Children.back()->Children.back()->Text = " is a parameter." ; |
294 | |
295 | Top.Children.emplace_back(args: std::make_unique<CommentInfo>()); |
296 | CommentInfo *ParamIn = Top.Children.back().get(); |
297 | ParamIn->Kind = "ParamCommandComment" ; |
298 | ParamIn->Direction = "[in]" ; |
299 | ParamIn->ParamName = "J" ; |
300 | ParamIn->Children.emplace_back(args: std::make_unique<CommentInfo>()); |
301 | ParamIn->Children.back()->Kind = "ParagraphComment" ; |
302 | ParamIn->Children.back()->Children.emplace_back( |
303 | args: std::make_unique<CommentInfo>()); |
304 | ParamIn->Children.back()->Children.back()->Kind = "TextComment" ; |
305 | ParamIn->Children.back()->Children.back()->Text = " is a parameter." ; |
306 | ParamIn->Children.back()->Children.emplace_back( |
307 | args: std::make_unique<CommentInfo>()); |
308 | ParamIn->Children.back()->Children.back()->Kind = "TextComment" ; |
309 | |
310 | Top.Children.emplace_back(args: std::make_unique<CommentInfo>()); |
311 | CommentInfo *Return = Top.Children.back().get(); |
312 | Return->Kind = "BlockCommandComment" ; |
313 | Return->Name = "return" ; |
314 | Return->Explicit = true; |
315 | Return->Children.emplace_back(args: std::make_unique<CommentInfo>()); |
316 | Return->Children.back()->Kind = "ParagraphComment" ; |
317 | Return->Children.back()->Children.emplace_back( |
318 | args: std::make_unique<CommentInfo>()); |
319 | Return->Children.back()->Children.back()->Kind = "TextComment" ; |
320 | Return->Children.back()->Children.back()->Text = "void" ; |
321 | |
322 | F.Description.emplace_back(args: std::move(Top)); |
323 | |
324 | std::string WriteResult = writeInfo(I: &F); |
325 | EXPECT_TRUE(WriteResult.size() > 0); |
326 | std::vector<std::unique_ptr<Info>> ReadResults = readInfo(Bitcode: WriteResult, NumInfos: 1); |
327 | |
328 | CheckFunctionInfo(Expected: &F, Actual: InfoAsFunction(I: ReadResults[0].get())); |
329 | } |
330 | |
331 | } // namespace doc |
332 | } // namespace clang |
333 | |