1 | #include "lldb/Utility/DiagnosticsRendering.h" |
2 | #include "lldb/Utility/StreamString.h" |
3 | #include "gtest/gtest.h" |
4 | |
5 | using namespace lldb_private; |
6 | using namespace lldb; |
7 | using llvm::StringRef; |
8 | namespace { |
9 | class ErrorDisplayTest : public ::testing::Test {}; |
10 | |
11 | std::string Render(std::vector<DiagnosticDetail> details) { |
12 | StreamString stream; |
13 | RenderDiagnosticDetails(stream, offset_in_command: 0, show_inline: true, details); |
14 | return stream.GetData(); |
15 | } |
16 | } // namespace |
17 | |
18 | TEST_F(ErrorDisplayTest, RenderStatus) { |
19 | using SourceLocation = DiagnosticDetail::SourceLocation; |
20 | { |
21 | SourceLocation inline_loc; |
22 | inline_loc.in_user_input = true; |
23 | std::string result = |
24 | Render(details: {DiagnosticDetail{.source_location: inline_loc, .severity: eSeverityError, .message: "foo" , .rendered: "" }}); |
25 | ASSERT_TRUE(StringRef(result).contains("error:" )); |
26 | ASSERT_TRUE(StringRef(result).contains("foo" )); |
27 | } |
28 | |
29 | { |
30 | // Test that diagnostics on the same column can be handled and all |
31 | // three errors are diagnosed. |
32 | SourceLocation loc1 = {.file: FileSpec{"a.c" }, .line: 13, .column: 5, .length: 0, .hidden: false, .in_user_input: true}; |
33 | SourceLocation loc2 = {.file: FileSpec{"a.c" }, .line: 13, .column: 7, .length: 0, .hidden: false, .in_user_input: true}; |
34 | SourceLocation loc3 = {.file: FileSpec{"a.c" }, .line: 13, .column: 9, .length: 0, .hidden: false, .in_user_input: true}; |
35 | std::string result = |
36 | Render(details: {DiagnosticDetail{.source_location: loc1, .severity: eSeverityError, .message: "1" , .rendered: "1" }, |
37 | DiagnosticDetail{.source_location: loc2, .severity: eSeverityError, .message: "2a" , .rendered: "2a" }, |
38 | DiagnosticDetail{.source_location: loc2, .severity: eSeverityInfo, .message: "2b" , .rendered: "2b" }, |
39 | DiagnosticDetail{.source_location: loc3, .severity: eSeverityError, .message: "3" , .rendered: "3" }}); |
40 | llvm::SmallVector<StringRef> lines; |
41 | StringRef(result).split(A&: lines, Separator: '\n'); |
42 | // 1234567890123 |
43 | ASSERT_EQ(lines[0], " ^ ^ ^" ); |
44 | ASSERT_EQ(lines[1], " | | error: 3" ); |
45 | ASSERT_EQ(lines[2], " | error: 2a" ); |
46 | ASSERT_EQ(lines[3], " | note: 2b" ); |
47 | ASSERT_EQ(lines[4], " error: 1" ); |
48 | } |
49 | { |
50 | // Test that diagnostics in reverse order are emitted correctly. |
51 | SourceLocation loc1 = {.file: FileSpec{"a.c" }, .line: 1, .column: 20, .length: 0, .hidden: false, .in_user_input: true}; |
52 | SourceLocation loc2 = {.file: FileSpec{"a.c" }, .line: 2, .column: 10, .length: 0, .hidden: false, .in_user_input: true}; |
53 | std::string result = |
54 | Render(details: {DiagnosticDetail{.source_location: loc2, .severity: eSeverityError, .message: "X" , .rendered: "X" }, |
55 | DiagnosticDetail{.source_location: loc1, .severity: eSeverityError, .message: "Y" , .rendered: "Y" }}); |
56 | // Unintuitively the later diagnostic appears first in the string: |
57 | // ^ ^ |
58 | // | second |
59 | // first |
60 | ASSERT_GT(StringRef(result).find("Y" ), StringRef(result).find("X" )); |
61 | } |
62 | { |
63 | // Test that diagnostics in reverse order are emitted correctly. |
64 | SourceLocation loc1 = {.file: FileSpec{"a.c" }, .line: 1, .column: 10, .length: 0, .hidden: false, .in_user_input: true}; |
65 | SourceLocation loc2 = {.file: FileSpec{"a.c" }, .line: 1, .column: 20, .length: 0, .hidden: false, .in_user_input: true}; |
66 | std::string result = |
67 | Render(details: {DiagnosticDetail{.source_location: loc2, .severity: eSeverityError, .message: "X" , .rendered: "X" }, |
68 | DiagnosticDetail{.source_location: loc1, .severity: eSeverityError, .message: "Y" , .rendered: "Y" }}); |
69 | ASSERT_GT(StringRef(result).find("Y" ), StringRef(result).find("X" )); |
70 | } |
71 | { |
72 | // Test that range diagnostics are emitted correctly. |
73 | SourceLocation loc1 = {.file: FileSpec{"a.c" }, .line: 1, .column: 1, .length: 3, .hidden: false, .in_user_input: true}; |
74 | SourceLocation loc2 = {.file: FileSpec{"a.c" }, .line: 1, .column: 5, .length: 3, .hidden: false, .in_user_input: true}; |
75 | std::string result = |
76 | Render(details: {DiagnosticDetail{.source_location: loc1, .severity: eSeverityError, .message: "X" , .rendered: "X" }, |
77 | DiagnosticDetail{.source_location: loc2, .severity: eSeverityError, .message: "Y" , .rendered: "Y" }}); |
78 | llvm::SmallVector<StringRef> lines; |
79 | StringRef(result).split(A&: lines, Separator: '\n'); |
80 | // 1234567 |
81 | ASSERT_EQ(lines[0], "^~~ ^~~" ); |
82 | ASSERT_EQ(lines[1], "| error: Y" ); |
83 | ASSERT_EQ(lines[2], "error: X" ); |
84 | } |
85 | { |
86 | // Test diagnostics on the same line are emitted correctly. |
87 | SourceLocation loc1 = {.file: FileSpec{"a.c" }, .line: 1, .column: 2, .length: 0, .hidden: false, .in_user_input: true}; |
88 | SourceLocation loc2 = {.file: FileSpec{"a.c" }, .line: 1, .column: 6, .length: 0, .hidden: false, .in_user_input: true}; |
89 | std::string result = |
90 | Render(details: {DiagnosticDetail{.source_location: loc1, .severity: eSeverityError, .message: "X" , .rendered: "X" }, |
91 | DiagnosticDetail{.source_location: loc2, .severity: eSeverityError, .message: "Y" , .rendered: "Y" }}); |
92 | llvm::SmallVector<StringRef> lines; |
93 | StringRef(result).split(A&: lines, Separator: '\n'); |
94 | // 1234567 |
95 | ASSERT_EQ(lines[0], " ^ ^" ); |
96 | ASSERT_EQ(lines[1], " | error: Y" ); |
97 | ASSERT_EQ(lines[2], " error: X" ); |
98 | } |
99 | } |
100 | |