| 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 | |