1//===- unittest/Support/YAMLRemarksParsingTest.cpp - OptTable tests -------===//
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 "llvm-c/Remarks.h"
10#include "llvm/Remarks/Remark.h"
11#include "llvm/Remarks/RemarkParser.h"
12#include "gtest/gtest.h"
13#include <optional>
14
15using namespace llvm;
16
17template <size_t N> void parseGood(const char (&Buf)[N]) {
18 Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =
19 remarks::createRemarkParser(remarks::Format::YAML, {Buf, N - 1});
20 EXPECT_FALSE(errorToBool(MaybeParser.takeError()));
21 EXPECT_TRUE(*MaybeParser != nullptr);
22
23 remarks::RemarkParser &Parser = **MaybeParser;
24 Expected<std::unique_ptr<remarks::Remark>> Remark = Parser.next();
25 EXPECT_FALSE(errorToBool(Remark.takeError())); // Check for parsing errors.
26 EXPECT_TRUE(*Remark != nullptr); // At least one remark.
27 Remark = Parser.next();
28 Error E = Remark.takeError();
29 EXPECT_TRUE(E.isA<remarks::EndOfFileError>());
30 EXPECT_TRUE(errorToBool(std::move(E))); // Check for parsing errors.
31}
32
33void parseGoodMeta(StringRef Buf) {
34 Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =
35 remarks::createRemarkParserFromMeta(ParserFormat: remarks::Format::YAML, Buf);
36 EXPECT_FALSE(errorToBool(MaybeParser.takeError()));
37 EXPECT_TRUE(*MaybeParser != nullptr);
38
39 remarks::RemarkParser &Parser = **MaybeParser;
40 Expected<std::unique_ptr<remarks::Remark>> Remark = Parser.next();
41 EXPECT_FALSE(errorToBool(Remark.takeError())); // Check for parsing errors.
42 EXPECT_TRUE(*Remark != nullptr); // At least one remark.
43 Remark = Parser.next();
44 Error E = Remark.takeError();
45 EXPECT_TRUE(E.isA<remarks::EndOfFileError>());
46 EXPECT_TRUE(errorToBool(std::move(E))); // Check for parsing errors.
47}
48
49template <size_t N>
50bool parseExpectError(const char (&Buf)[N], const char *Error) {
51 Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =
52 remarks::createRemarkParser(remarks::Format::YAML, {Buf, N - 1});
53 EXPECT_FALSE(errorToBool(MaybeParser.takeError()));
54 EXPECT_TRUE(*MaybeParser != nullptr);
55
56 remarks::RemarkParser &Parser = **MaybeParser;
57 Expected<std::unique_ptr<remarks::Remark>> Remark = Parser.next();
58 EXPECT_FALSE(Remark); // Check for parsing errors.
59
60 std::string ErrorStr;
61 raw_string_ostream Stream(ErrorStr);
62 handleAllErrors(Remark.takeError(),
63 [&](const ErrorInfoBase &EIB) { EIB.log(OS&: Stream); });
64 return StringRef(Stream.str()).contains(Other: Error);
65}
66
67enum class CmpType {
68 Equal,
69 Contains
70};
71
72void parseExpectErrorMeta(
73 StringRef Buf, const char *Error, CmpType Cmp,
74 std::optional<StringRef> ExternalFilePrependPath = std::nullopt) {
75 std::string ErrorStr;
76 raw_string_ostream Stream(ErrorStr);
77
78 Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =
79 remarks::createRemarkParserFromMeta(ParserFormat: remarks::Format::YAML, Buf,
80 /*StrTab=*/std::nullopt,
81 ExternalFilePrependPath: std::move(ExternalFilePrependPath));
82 handleAllErrors(E: MaybeParser.takeError(),
83 Handlers: [&](const ErrorInfoBase &EIB) { EIB.log(OS&: Stream); });
84
85 // Use a case insensitive comparision due to case differences in error strings
86 // for different OSs.
87 if (Cmp == CmpType::Equal) {
88 EXPECT_EQ(StringRef(Stream.str()).lower(), StringRef(Error).lower());
89 }
90
91 if (Cmp == CmpType::Contains) {
92 EXPECT_TRUE(StringRef(Stream.str()).contains(StringRef(Error)));
93 }
94}
95
96TEST(YAMLRemarks, ParsingEmpty) {
97 EXPECT_TRUE(parseExpectError("\n\n", "document root is not of mapping type."));
98}
99
100TEST(YAMLRemarks, ParsingNotYAML) {
101 EXPECT_TRUE(
102 parseExpectError("\x01\x02\x03\x04\x05\x06", "Got empty plain scalar"));
103}
104
105TEST(YAMLRemarks, ParsingGood) {
106 parseGood(Buf: "\n"
107 "--- !Missed\n"
108 "Pass: inline\n"
109 "Name: NoDefinition\n"
110 "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n"
111 "Function: foo\n"
112 "Args:\n"
113 " - Callee: bar\n"
114 " - String: ' will not be inlined into '\n"
115 " - Caller: foo\n"
116 " DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"
117 " - String: ' because its definition is unavailable'\n"
118 "");
119
120 // No debug loc should also pass.
121 parseGood(Buf: "\n"
122 "--- !Missed\n"
123 "Pass: inline\n"
124 "Name: NoDefinition\n"
125 "Function: foo\n"
126 "Args:\n"
127 " - Callee: bar\n"
128 " - String: ' will not be inlined into '\n"
129 " - Caller: foo\n"
130 " DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"
131 " - String: ' because its definition is unavailable'\n"
132 "");
133
134 // No args is also ok.
135 parseGood(Buf: "\n"
136 "--- !Missed\n"
137 "Pass: inline\n"
138 "Name: NoDefinition\n"
139 "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n"
140 "Function: foo\n"
141 "");
142
143 // Different order.
144 parseGood(Buf: "\n"
145 "--- !Missed\n"
146 "DebugLoc: { Line: 3, Column: 12, File: file.c }\n"
147 "Function: foo\n"
148 "Name: NoDefinition\n"
149 "Args:\n"
150 " - Callee: bar\n"
151 " - String: ' will not be inlined into '\n"
152 " - Caller: foo\n"
153 " DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"
154 " - String: ' because its definition is unavailable'\n"
155 "Pass: inline\n"
156 "");
157
158 // Block Remark.
159 parseGood(Buf: "\n"
160 "--- !Missed\n"
161 "Function: foo\n"
162 "Name: NoDefinition\n"
163 "Args:\n"
164 " - String: |\n \n \n blocks\n"
165 "Pass: inline\n"
166 "");
167}
168
169// Mandatory common part of a remark.
170#define COMMON_REMARK "\nPass: inline\nName: NoDefinition\nFunction: foo\n\n"
171// Test all the types.
172TEST(YAMLRemarks, ParsingTypes) {
173 // Type: Passed
174 parseGood(Buf: "--- !Passed" COMMON_REMARK);
175 // Type: Missed
176 parseGood(Buf: "--- !Missed" COMMON_REMARK);
177 // Type: Analysis
178 parseGood(Buf: "--- !Analysis" COMMON_REMARK);
179 // Type: AnalysisFPCommute
180 parseGood(Buf: "--- !AnalysisFPCommute" COMMON_REMARK);
181 // Type: AnalysisAliasing
182 parseGood(Buf: "--- !AnalysisAliasing" COMMON_REMARK);
183 // Type: Failure
184 parseGood(Buf: "--- !Failure" COMMON_REMARK);
185}
186#undef COMMON_REMARK
187
188TEST(YAMLRemarks, ParsingMissingFields) {
189 // No type.
190 EXPECT_TRUE(parseExpectError("\n"
191 "---\n"
192 "Pass: inline\n"
193 "Name: NoDefinition\n"
194 "Function: foo\n"
195 "",
196 "expected a remark tag."));
197 // No pass.
198 EXPECT_TRUE(parseExpectError("\n"
199 "--- !Missed\n"
200 "Name: NoDefinition\n"
201 "Function: foo\n"
202 "",
203 "Type, Pass, Name or Function missing."));
204 // No name.
205 EXPECT_TRUE(parseExpectError("\n"
206 "--- !Missed\n"
207 "Pass: inline\n"
208 "Function: foo\n"
209 "",
210 "Type, Pass, Name or Function missing."));
211 // No function.
212 EXPECT_TRUE(parseExpectError("\n"
213 "--- !Missed\n"
214 "Pass: inline\n"
215 "Name: NoDefinition\n"
216 "",
217 "Type, Pass, Name or Function missing."));
218 // Debug loc but no file.
219 EXPECT_TRUE(parseExpectError("\n"
220 "--- !Missed\n"
221 "Pass: inline\n"
222 "Name: NoDefinition\n"
223 "Function: foo\n"
224 "DebugLoc: { Line: 3, Column: 12 }\n"
225 "",
226 "DebugLoc node incomplete."));
227 // Debug loc but no line.
228 EXPECT_TRUE(parseExpectError("\n"
229 "--- !Missed\n"
230 "Pass: inline\n"
231 "Name: NoDefinition\n"
232 "Function: foo\n"
233 "DebugLoc: { File: file.c, Column: 12 }\n"
234 "",
235 "DebugLoc node incomplete."));
236 // Debug loc but no column.
237 EXPECT_TRUE(parseExpectError("\n"
238 "--- !Missed\n"
239 "Pass: inline\n"
240 "Name: NoDefinition\n"
241 "Function: foo\n"
242 "DebugLoc: { File: file.c, Line: 3 }\n"
243 "",
244 "DebugLoc node incomplete."));
245}
246
247TEST(YAMLRemarks, ParsingWrongTypes) {
248 // Wrong debug loc type.
249 EXPECT_TRUE(parseExpectError("\n"
250 "--- !Missed\n"
251 "Pass: inline\n"
252 "Name: NoDefinition\n"
253 "Function: foo\n"
254 "DebugLoc: foo\n"
255 "",
256 "expected a value of mapping type."));
257 // Wrong line type.
258 EXPECT_TRUE(parseExpectError("\n"
259 "--- !Missed\n"
260 "Pass: inline\n"
261 "Name: NoDefinition\n"
262 "Function: foo\n"
263 "DebugLoc: { File: file.c, Line: b, Column: 12 }\n"
264 "",
265 "expected a value of integer type."));
266 // Wrong column type.
267 EXPECT_TRUE(parseExpectError("\n"
268 "--- !Missed\n"
269 "Pass: inline\n"
270 "Name: NoDefinition\n"
271 "Function: foo\n"
272 "DebugLoc: { File: file.c, Line: 3, Column: c }\n"
273 "",
274 "expected a value of integer type."));
275 // Wrong args type.
276 EXPECT_TRUE(parseExpectError("\n"
277 "--- !Missed\n"
278 "Pass: inline\n"
279 "Name: NoDefinition\n"
280 "Function: foo\n"
281 "Args: foo\n"
282 "",
283 "wrong value type for key."));
284 // Wrong key type.
285 EXPECT_TRUE(parseExpectError("\n"
286 "--- !Missed\n"
287 "{ A: a }: inline\n"
288 "Name: NoDefinition\n"
289 "Function: foo\n"
290 "",
291 "key is not a string."));
292 // Debug loc with unknown entry.
293 EXPECT_TRUE(parseExpectError("\n"
294 "--- !Missed\n"
295 "Pass: inline\n"
296 "Name: NoDefinition\n"
297 "Function: foo\n"
298 "DebugLoc: { File: file.c, Column: 12, Unknown: 12 }\n"
299 "",
300 "unknown entry in DebugLoc map."));
301 // Unknown entry.
302 EXPECT_TRUE(parseExpectError("\n"
303 "--- !Missed\n"
304 "Unknown: inline\n"
305 "",
306 "unknown key."));
307 // Not a scalar.
308 EXPECT_TRUE(parseExpectError("\n"
309 "--- !Missed\n"
310 "Pass: { File: a, Line: 1, Column: 2 }\n"
311 "Name: NoDefinition\n"
312 "Function: foo\n"
313 "",
314 "expected a value of scalar type."));
315 // Not a string file in debug loc.
316 EXPECT_TRUE(parseExpectError("\n"
317 "--- !Missed\n"
318 "Pass: inline\n"
319 "Name: NoDefinition\n"
320 "Function: foo\n"
321 "DebugLoc: { File: { a: b }, Column: 12, Line: 12 }\n"
322 "",
323 "expected a value of scalar type."));
324 // Not a integer column in debug loc.
325 EXPECT_TRUE(parseExpectError("\n"
326 "--- !Missed\n"
327 "Pass: inline\n"
328 "Name: NoDefinition\n"
329 "Function: foo\n"
330 "DebugLoc: { File: file.c, Column: { a: b }, Line: 12 }\n"
331 "",
332 "expected a value of scalar type."));
333 // Not a integer line in debug loc.
334 EXPECT_TRUE(parseExpectError("\n"
335 "--- !Missed\n"
336 "Pass: inline\n"
337 "Name: NoDefinition\n"
338 "Function: foo\n"
339 "DebugLoc: { File: file.c, Column: 12, Line: { a: b } }\n"
340 "",
341 "expected a value of scalar type."));
342 // Not a mapping type value for args.
343 EXPECT_TRUE(parseExpectError("\n"
344 "--- !Missed\n"
345 "Pass: inline\n"
346 "Name: NoDefinition\n"
347 "Function: foo\n"
348 "DebugLoc: { File: file.c, Column: 12, Line: { a: b } }\n"
349 "",
350 "expected a value of scalar type."));
351}
352
353TEST(YAMLRemarks, ParsingWrongArgs) {
354 // Multiple debug locs per arg.
355 EXPECT_TRUE(parseExpectError("\n"
356 "--- !Missed\n"
357 "Pass: inline\n"
358 "Name: NoDefinition\n"
359 "Function: foo\n"
360 "Args:\n"
361 " - Str: string\n"
362 " DebugLoc: { File: a, Line: 1, Column: 2 }\n"
363 " DebugLoc: { File: a, Line: 1, Column: 2 }\n"
364 "",
365 "only one DebugLoc entry is allowed per argument."));
366 // Multiple strings per arg.
367 EXPECT_TRUE(parseExpectError("\n"
368 "--- !Missed\n"
369 "Pass: inline\n"
370 "Name: NoDefinition\n"
371 "Function: foo\n"
372 "Args:\n"
373 " - Str: string\n"
374 " Str2: string\n"
375 " DebugLoc: { File: a, Line: 1, Column: 2 }\n"
376 "",
377 "only one string entry is allowed per argument."));
378 // No arg value.
379 EXPECT_TRUE(parseExpectError("\n"
380 "--- !Missed\n"
381 "Pass: inline\n"
382 "Name: NoDefinition\n"
383 "Function: foo\n"
384 "Args:\n"
385 " - DebugLoc: { File: a, Line: 1, Column: 2 }\n"
386 "",
387 "argument key is missing."));
388}
389
390static inline StringRef checkStr(StringRef Str, unsigned ExpectedLen) {
391 const char *StrData = Str.data();
392 unsigned StrLen = Str.size();
393 EXPECT_EQ(StrLen, ExpectedLen);
394 return StringRef(StrData, StrLen);
395}
396
397TEST(YAMLRemarks, Contents) {
398 StringRef Buf = "\n"
399 "--- !Missed\n"
400 "Pass: inline\n"
401 "Name: NoDefinition\n"
402 "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n"
403 "Function: foo\n"
404 "Hotness: 4\n"
405 "Args:\n"
406 " - Callee: bar\n"
407 " - String: ' will not be inlined into '\n"
408 " - Caller: foo\n"
409 " DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"
410 " - String: ' because its definition is unavailable'\n"
411 "\n";
412
413 Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =
414 remarks::createRemarkParser(ParserFormat: remarks::Format::YAML, Buf);
415 EXPECT_FALSE(errorToBool(MaybeParser.takeError()));
416 EXPECT_TRUE(*MaybeParser != nullptr);
417
418 remarks::RemarkParser &Parser = **MaybeParser;
419 Expected<std::unique_ptr<remarks::Remark>> MaybeRemark = Parser.next();
420 EXPECT_FALSE(
421 errorToBool(MaybeRemark.takeError())); // Check for parsing errors.
422 EXPECT_TRUE(*MaybeRemark != nullptr); // At least one remark.
423
424 const remarks::Remark &Remark = **MaybeRemark;
425 EXPECT_EQ(Remark.RemarkType, remarks::Type::Missed);
426 EXPECT_EQ(checkStr(Remark.PassName, 6), "inline");
427 EXPECT_EQ(checkStr(Remark.RemarkName, 12), "NoDefinition");
428 EXPECT_EQ(checkStr(Remark.FunctionName, 3), "foo");
429 EXPECT_TRUE(Remark.Loc);
430 const remarks::RemarkLocation &RL = *Remark.Loc;
431 EXPECT_EQ(checkStr(RL.SourceFilePath, 6), "file.c");
432 EXPECT_EQ(RL.SourceLine, 3U);
433 EXPECT_EQ(RL.SourceColumn, 12U);
434 EXPECT_TRUE(Remark.Hotness);
435 EXPECT_EQ(*Remark.Hotness, 4U);
436 EXPECT_EQ(Remark.Args.size(), 4U);
437
438 unsigned ArgID = 0;
439 for (const remarks::Argument &Arg : Remark.Args) {
440 switch (ArgID) {
441 case 0:
442 EXPECT_EQ(checkStr(Arg.Key, 6), "Callee");
443 EXPECT_EQ(checkStr(Arg.Val, 3), "bar");
444 EXPECT_FALSE(Arg.Loc);
445 break;
446 case 1:
447 EXPECT_EQ(checkStr(Arg.Key, 6), "String");
448 EXPECT_EQ(checkStr(Arg.Val, 26), " will not be inlined into ");
449 EXPECT_FALSE(Arg.Loc);
450 break;
451 case 2: {
452 EXPECT_EQ(checkStr(Arg.Key, 6), "Caller");
453 EXPECT_EQ(checkStr(Arg.Val, 3), "foo");
454 EXPECT_TRUE(Arg.Loc);
455 const remarks::RemarkLocation &RL = *Arg.Loc;
456 EXPECT_EQ(checkStr(RL.SourceFilePath, 6), "file.c");
457 EXPECT_EQ(RL.SourceLine, 2U);
458 EXPECT_EQ(RL.SourceColumn, 0U);
459 break;
460 }
461 case 3:
462 EXPECT_EQ(checkStr(Arg.Key, 6), "String");
463 EXPECT_EQ(checkStr(Arg.Val, 38),
464 " because its definition is unavailable");
465 EXPECT_FALSE(Arg.Loc);
466 break;
467 default:
468 break;
469 }
470 ++ArgID;
471 }
472
473 MaybeRemark = Parser.next();
474 Error E = MaybeRemark.takeError();
475 EXPECT_TRUE(E.isA<remarks::EndOfFileError>());
476 EXPECT_TRUE(errorToBool(std::move(E))); // Check for parsing errors.
477}
478
479static inline StringRef checkStr(LLVMRemarkStringRef Str,
480 unsigned ExpectedLen) {
481 const char *StrData = LLVMRemarkStringGetData(String: Str);
482 unsigned StrLen = LLVMRemarkStringGetLen(String: Str);
483 EXPECT_EQ(StrLen, ExpectedLen);
484 return StringRef(StrData, StrLen);
485}
486
487TEST(YAMLRemarks, ContentsCAPI) {
488 StringRef Buf = "\n"
489 "--- !Missed\n"
490 "Pass: inline\n"
491 "Name: NoDefinition\n"
492 "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n"
493 "Function: foo\n"
494 "Args:\n"
495 " - Callee: bar\n"
496 " - String: ' will not be inlined into '\n"
497 " - Caller: foo\n"
498 " DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"
499 " - String: ' because its definition is unavailable'\n"
500 "\n";
501
502 LLVMRemarkParserRef Parser =
503 LLVMRemarkParserCreateYAML(Buf: Buf.data(), Size: Buf.size());
504 LLVMRemarkEntryRef Remark = LLVMRemarkParserGetNext(Parser);
505 EXPECT_FALSE(Remark == nullptr);
506 EXPECT_EQ(LLVMRemarkEntryGetType(Remark), LLVMRemarkTypeMissed);
507 EXPECT_EQ(checkStr(LLVMRemarkEntryGetPassName(Remark), 6), "inline");
508 EXPECT_EQ(checkStr(LLVMRemarkEntryGetRemarkName(Remark), 12), "NoDefinition");
509 EXPECT_EQ(checkStr(LLVMRemarkEntryGetFunctionName(Remark), 3), "foo");
510 LLVMRemarkDebugLocRef DL = LLVMRemarkEntryGetDebugLoc(Remark);
511 EXPECT_EQ(checkStr(LLVMRemarkDebugLocGetSourceFilePath(DL), 6), "file.c");
512 EXPECT_EQ(LLVMRemarkDebugLocGetSourceLine(DL), 3U);
513 EXPECT_EQ(LLVMRemarkDebugLocGetSourceColumn(DL), 12U);
514 EXPECT_EQ(LLVMRemarkEntryGetHotness(Remark), 0U);
515 EXPECT_EQ(LLVMRemarkEntryGetNumArgs(Remark), 4U);
516
517 unsigned ArgID = 0;
518 LLVMRemarkArgRef Arg = LLVMRemarkEntryGetFirstArg(Remark);
519 do {
520 switch (ArgID) {
521 case 0:
522 EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "Callee");
523 EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 3), "bar");
524 EXPECT_EQ(LLVMRemarkArgGetDebugLoc(Arg), nullptr);
525 break;
526 case 1:
527 EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "String");
528 EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 26),
529 " will not be inlined into ");
530 EXPECT_EQ(LLVMRemarkArgGetDebugLoc(Arg), nullptr);
531 break;
532 case 2: {
533 EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "Caller");
534 EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 3), "foo");
535 LLVMRemarkDebugLocRef DL = LLVMRemarkArgGetDebugLoc(Arg);
536 EXPECT_EQ(checkStr(LLVMRemarkDebugLocGetSourceFilePath(DL), 6), "file.c");
537 EXPECT_EQ(LLVMRemarkDebugLocGetSourceLine(DL), 2U);
538 EXPECT_EQ(LLVMRemarkDebugLocGetSourceColumn(DL), 0U);
539 break;
540 }
541 case 3:
542 EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "String");
543 EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 38),
544 " because its definition is unavailable");
545 EXPECT_EQ(LLVMRemarkArgGetDebugLoc(Arg), nullptr);
546 break;
547 default:
548 break;
549 }
550 ++ArgID;
551 } while ((Arg = LLVMRemarkEntryGetNextArg(It: Arg, Remark)));
552
553 LLVMRemarkEntryDispose(Remark);
554
555 EXPECT_EQ(LLVMRemarkParserGetNext(Parser), nullptr);
556
557 EXPECT_FALSE(LLVMRemarkParserHasError(Parser));
558 LLVMRemarkParserDispose(Parser);
559}
560
561TEST(YAMLRemarks, ContentsStrTab) {
562 StringRef Buf = "\n"
563 "--- !Missed\n"
564 "Pass: 0\n"
565 "Name: 1\n"
566 "DebugLoc: { File: 2, Line: 3, Column: 12 }\n"
567 "Function: 3\n"
568 "Hotness: 4\n"
569 "Args:\n"
570 " - Callee: 5\n"
571 " - String: 7\n"
572 " - Caller: 3\n"
573 " DebugLoc: { File: 2, Line: 2, Column: 0 }\n"
574 " - String: 8\n"
575 "\n";
576
577 StringRef StrTabBuf =
578 StringRef("inline\0NoDefinition\0file.c\0foo\0Callee\0bar\0String\0 "
579 "will not be inlined into \0 because its definition is "
580 "unavailable",
581 115);
582
583 remarks::ParsedStringTable StrTab(StrTabBuf);
584 Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =
585 remarks::createRemarkParser(ParserFormat: remarks::Format::YAMLStrTab, Buf,
586 StrTab: std::move(StrTab));
587 EXPECT_FALSE(errorToBool(MaybeParser.takeError()));
588 EXPECT_TRUE(*MaybeParser != nullptr);
589
590 remarks::RemarkParser &Parser = **MaybeParser;
591 Expected<std::unique_ptr<remarks::Remark>> MaybeRemark = Parser.next();
592 EXPECT_FALSE(
593 errorToBool(MaybeRemark.takeError())); // Check for parsing errors.
594 EXPECT_TRUE(*MaybeRemark != nullptr); // At least one remark.
595
596 const remarks::Remark &Remark = **MaybeRemark;
597 EXPECT_EQ(Remark.RemarkType, remarks::Type::Missed);
598 EXPECT_EQ(checkStr(Remark.PassName, 6), "inline");
599 EXPECT_EQ(checkStr(Remark.RemarkName, 12), "NoDefinition");
600 EXPECT_EQ(checkStr(Remark.FunctionName, 3), "foo");
601 EXPECT_TRUE(Remark.Loc);
602 const remarks::RemarkLocation &RL = *Remark.Loc;
603 EXPECT_EQ(checkStr(RL.SourceFilePath, 6), "file.c");
604 EXPECT_EQ(RL.SourceLine, 3U);
605 EXPECT_EQ(RL.SourceColumn, 12U);
606 EXPECT_TRUE(Remark.Hotness);
607 EXPECT_EQ(*Remark.Hotness, 4U);
608 EXPECT_EQ(Remark.Args.size(), 4U);
609
610 unsigned ArgID = 0;
611 for (const remarks::Argument &Arg : Remark.Args) {
612 switch (ArgID) {
613 case 0:
614 EXPECT_EQ(checkStr(Arg.Key, 6), "Callee");
615 EXPECT_EQ(checkStr(Arg.Val, 3), "bar");
616 EXPECT_FALSE(Arg.Loc);
617 break;
618 case 1:
619 EXPECT_EQ(checkStr(Arg.Key, 6), "String");
620 EXPECT_EQ(checkStr(Arg.Val, 26), " will not be inlined into ");
621 EXPECT_FALSE(Arg.Loc);
622 break;
623 case 2: {
624 EXPECT_EQ(checkStr(Arg.Key, 6), "Caller");
625 EXPECT_EQ(checkStr(Arg.Val, 3), "foo");
626 EXPECT_TRUE(Arg.Loc);
627 const remarks::RemarkLocation &RL = *Arg.Loc;
628 EXPECT_EQ(checkStr(RL.SourceFilePath, 6), "file.c");
629 EXPECT_EQ(RL.SourceLine, 2U);
630 EXPECT_EQ(RL.SourceColumn, 0U);
631 break;
632 }
633 case 3:
634 EXPECT_EQ(checkStr(Arg.Key, 6), "String");
635 EXPECT_EQ(checkStr(Arg.Val, 38),
636 " because its definition is unavailable");
637 EXPECT_FALSE(Arg.Loc);
638 break;
639 default:
640 break;
641 }
642 ++ArgID;
643 }
644
645 MaybeRemark = Parser.next();
646 Error E = MaybeRemark.takeError();
647 EXPECT_TRUE(E.isA<remarks::EndOfFileError>());
648 EXPECT_TRUE(errorToBool(std::move(E))); // Check for parsing errors.
649}
650
651TEST(YAMLRemarks, ParsingBadStringTableIndex) {
652 StringRef Buf = "\n"
653 "--- !Missed\n"
654 "Pass: 50\n"
655 "\n";
656
657 StringRef StrTabBuf = StringRef("inline");
658
659 remarks::ParsedStringTable StrTab(StrTabBuf);
660 Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =
661 remarks::createRemarkParser(ParserFormat: remarks::Format::YAMLStrTab, Buf,
662 StrTab: std::move(StrTab));
663 EXPECT_FALSE(errorToBool(MaybeParser.takeError()));
664 EXPECT_TRUE(*MaybeParser != nullptr);
665
666 remarks::RemarkParser &Parser = **MaybeParser;
667 Expected<std::unique_ptr<remarks::Remark>> MaybeRemark = Parser.next();
668 EXPECT_FALSE(MaybeRemark); // Expect an error here.
669
670 std::string ErrorStr;
671 raw_string_ostream Stream(ErrorStr);
672 handleAllErrors(E: MaybeRemark.takeError(),
673 Handlers: [&](const ErrorInfoBase &EIB) { EIB.log(OS&: Stream); });
674 EXPECT_TRUE(
675 StringRef(Stream.str())
676 .contains("String with index 50 is out of bounds (size = 1)."));
677}
678
679TEST(YAMLRemarks, ParsingGoodMeta) {
680 // No metadata should also work.
681 parseGoodMeta(Buf: "--- !Missed\n"
682 "Pass: inline\n"
683 "Name: NoDefinition\n"
684 "Function: foo\n");
685
686 // No string table.
687 parseGoodMeta(Buf: StringRef("REMARKS\0"
688 "\0\0\0\0\0\0\0\0"
689 "\0\0\0\0\0\0\0\0"
690 "--- !Missed\n"
691 "Pass: inline\n"
692 "Name: NoDefinition\n"
693 "Function: foo\n",
694 82));
695
696 // Use the string table from the metadata.
697 parseGoodMeta(Buf: StringRef("REMARKS\0"
698 "\0\0\0\0\0\0\0\0"
699 "\x02\0\0\0\0\0\0\0"
700 "a\0"
701 "--- !Missed\n"
702 "Pass: 0\n"
703 "Name: 0\n"
704 "Function: 0\n",
705 66));
706}
707
708TEST(YAMLRemarks, ParsingBadMeta) {
709 parseExpectErrorMeta(Buf: StringRef("REMARKSS", 9),
710 Error: "Expecting \\0 after magic number.", Cmp: CmpType::Equal);
711
712 parseExpectErrorMeta(Buf: StringRef("REMARKS\0", 8), Error: "Expecting version number.",
713 Cmp: CmpType::Equal);
714
715 parseExpectErrorMeta(Buf: StringRef("REMARKS\0"
716 "\x09\0\0\0\0\0\0\0",
717 16),
718 Error: "Mismatching remark version. Got 9, expected 0.",
719 Cmp: CmpType::Equal);
720
721 parseExpectErrorMeta(Buf: StringRef("REMARKS\0"
722 "\0\0\0\0\0\0\0\0",
723 16),
724 Error: "Expecting string table size.", Cmp: CmpType::Equal);
725
726 parseExpectErrorMeta(Buf: StringRef("REMARKS\0"
727 "\0\0\0\0\0\0\0\0"
728 "\x01\0\0\0\0\0\0\0",
729 24),
730 Error: "Expecting string table.", Cmp: CmpType::Equal);
731
732 parseExpectErrorMeta(Buf: StringRef("REMARKS\0"
733 "\0\0\0\0\0\0\0\0"
734 "\0\0\0\0\0\0\0\0"
735 "/path/",
736 30),
737 Error: "'/path/'", Cmp: CmpType::Contains);
738
739 parseExpectErrorMeta(Buf: StringRef("REMARKS\0"
740 "\0\0\0\0\0\0\0\0"
741 "\0\0\0\0\0\0\0\0"
742 "/path/",
743 30),
744 Error: "'/baddir/path/'", Cmp: CmpType::Contains,
745 ExternalFilePrependPath: StringRef("/baddir/"));
746}
747

source code of llvm/unittests/Remarks/YAMLRemarksParsingTest.cpp