1 | //===-- DiagnosticManagerTest.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 "lldb/Expression/DiagnosticManager.h" |
10 | #include "gtest/gtest.h" |
11 | |
12 | using namespace lldb_private; |
13 | |
14 | static const uint32_t custom_diag_id = 42; |
15 | |
16 | namespace { |
17 | class FixItDiag : public Diagnostic { |
18 | bool m_has_fixits; |
19 | |
20 | public: |
21 | FixItDiag(llvm::StringRef msg, bool has_fixits) |
22 | : Diagnostic(DiagnosticOrigin::eDiagnosticOriginLLDB, custom_diag_id, |
23 | DiagnosticDetail{.source_location: {}, .severity: lldb::eSeverityError, .message: msg.str(), .rendered: {}}), |
24 | m_has_fixits(has_fixits) {} |
25 | bool HasFixIts() const override { return m_has_fixits; } |
26 | }; |
27 | } // namespace |
28 | |
29 | namespace { |
30 | class TextDiag : public Diagnostic { |
31 | public: |
32 | TextDiag(llvm::StringRef msg, lldb::Severity severity) |
33 | : Diagnostic(DiagnosticOrigin::eDiagnosticOriginLLDB, custom_diag_id, |
34 | DiagnosticDetail{.source_location: {}, .severity: severity, .message: msg.str(), .rendered: msg.str()}) {} |
35 | }; |
36 | } // namespace |
37 | |
38 | TEST(DiagnosticManagerTest, AddDiagnostic) { |
39 | DiagnosticManager mgr; |
40 | EXPECT_EQ(0U, mgr.Diagnostics().size()); |
41 | |
42 | std::string msg = "foo bar has happened" ; |
43 | lldb::Severity severity = lldb::eSeverityError; |
44 | DiagnosticOrigin origin = DiagnosticOrigin::eDiagnosticOriginLLDB; |
45 | auto diag = std::make_unique<Diagnostic>( |
46 | args&: origin, args: custom_diag_id, args: DiagnosticDetail{.source_location: {}, .severity: severity, .message: msg, .rendered: {}}); |
47 | mgr.AddDiagnostic(diagnostic: std::move(diag)); |
48 | EXPECT_EQ(1U, mgr.Diagnostics().size()); |
49 | const Diagnostic *got = mgr.Diagnostics().front().get(); |
50 | EXPECT_EQ(DiagnosticOrigin::eDiagnosticOriginLLDB, got->getKind()); |
51 | EXPECT_EQ(msg, got->GetMessage()); |
52 | EXPECT_EQ(severity, got->GetSeverity()); |
53 | EXPECT_EQ(custom_diag_id, got->GetCompilerID()); |
54 | EXPECT_EQ(false, got->HasFixIts()); |
55 | } |
56 | |
57 | TEST(DiagnosticManagerTest, HasFixits) { |
58 | DiagnosticManager mgr; |
59 | // By default we shouldn't have any fixits. |
60 | EXPECT_FALSE(mgr.HasFixIts()); |
61 | // Adding a diag without fixits shouldn't make HasFixIts return true. |
62 | mgr.AddDiagnostic(diagnostic: std::make_unique<FixItDiag>(args: "no fixit" , args: false)); |
63 | EXPECT_FALSE(mgr.HasFixIts()); |
64 | // Adding a diag with fixits will mark the manager as containing fixits. |
65 | mgr.AddDiagnostic(diagnostic: std::make_unique<FixItDiag>(args: "fixit" , args: true)); |
66 | EXPECT_TRUE(mgr.HasFixIts()); |
67 | // Adding another diag without fixit shouldn't make it return false. |
68 | mgr.AddDiagnostic(diagnostic: std::make_unique<FixItDiag>(args: "no fixit" , args: false)); |
69 | EXPECT_TRUE(mgr.HasFixIts()); |
70 | // Adding a diag with fixits. The manager should still return true. |
71 | mgr.AddDiagnostic(diagnostic: std::make_unique<FixItDiag>(args: "fixit" , args: true)); |
72 | EXPECT_TRUE(mgr.HasFixIts()); |
73 | } |
74 | |
75 | static std::string toString(DiagnosticManager &mgr) { |
76 | // The error code doesn't really matter since we just convert the |
77 | // diagnostics to a string. |
78 | auto result = lldb::eExpressionCompleted; |
79 | return llvm::toString(E: mgr.GetAsError(result)); |
80 | } |
81 | |
82 | TEST(DiagnosticManagerTest, GetStringNoDiags) { |
83 | DiagnosticManager mgr; |
84 | EXPECT_EQ("" , toString(mgr)); |
85 | std::unique_ptr<Diagnostic> empty; |
86 | mgr.AddDiagnostic(diagnostic: std::move(empty)); |
87 | EXPECT_EQ("" , toString(mgr)); |
88 | } |
89 | |
90 | TEST(DiagnosticManagerTest, GetStringBasic) { |
91 | DiagnosticManager mgr; |
92 | mgr.AddDiagnostic(diagnostic: std::make_unique<TextDiag>(args: "abc" , args: lldb::eSeverityError)); |
93 | EXPECT_EQ("error: abc\n" , toString(mgr)); |
94 | } |
95 | |
96 | TEST(DiagnosticManagerTest, GetStringMultiline) { |
97 | DiagnosticManager mgr; |
98 | |
99 | // Multiline diagnostics should only get one severity label. |
100 | mgr.AddDiagnostic(diagnostic: std::make_unique<TextDiag>(args: "b\nc" , args: lldb::eSeverityError)); |
101 | EXPECT_EQ("error: b\nc\n" , toString(mgr)); |
102 | } |
103 | |
104 | TEST(DiagnosticManagerTest, GetStringMultipleDiags) { |
105 | DiagnosticManager mgr; |
106 | mgr.AddDiagnostic(diagnostic: std::make_unique<TextDiag>(args: "abc" , args: lldb::eSeverityError)); |
107 | EXPECT_EQ("error: abc\n" , toString(mgr)); |
108 | mgr.AddDiagnostic(diagnostic: std::make_unique<TextDiag>(args: "def" , args: lldb::eSeverityError)); |
109 | EXPECT_EQ("error: abc\nerror: def\n" , toString(mgr)); |
110 | } |
111 | |
112 | TEST(DiagnosticManagerTest, GetStringSeverityLabels) { |
113 | DiagnosticManager mgr; |
114 | |
115 | // Different severities should cause different labels. |
116 | mgr.AddDiagnostic(diagnostic: std::make_unique<TextDiag>(args: "foo" , args: lldb::eSeverityError)); |
117 | mgr.AddDiagnostic(diagnostic: std::make_unique<TextDiag>(args: "bar" , args: lldb::eSeverityWarning)); |
118 | // Remarks have no labels. |
119 | mgr.AddDiagnostic(diagnostic: std::make_unique<TextDiag>(args: "baz" , args: lldb::eSeverityInfo)); |
120 | EXPECT_EQ("error: foo\nwarning: bar\nbaz\n" , toString(mgr)); |
121 | } |
122 | |
123 | TEST(DiagnosticManagerTest, GetStringPreserveOrder) { |
124 | DiagnosticManager mgr; |
125 | |
126 | // Make sure we preserve the diagnostic order and do not sort them in any way. |
127 | mgr.AddDiagnostic(diagnostic: std::make_unique<TextDiag>(args: "baz" , args: lldb::eSeverityInfo)); |
128 | mgr.AddDiagnostic(diagnostic: std::make_unique<TextDiag>(args: "bar" , args: lldb::eSeverityWarning)); |
129 | mgr.AddDiagnostic(diagnostic: std::make_unique<TextDiag>(args: "foo" , args: lldb::eSeverityError)); |
130 | EXPECT_EQ("baz\nwarning: bar\nerror: foo\n" , toString(mgr)); |
131 | } |
132 | |
133 | TEST(DiagnosticManagerTest, AppendMessageNoDiag) { |
134 | DiagnosticManager mgr; |
135 | |
136 | // FIXME: This *really* should not just fail silently. |
137 | mgr.AppendMessageToDiagnostic(str: "no diag has been pushed yet" ); |
138 | EXPECT_EQ(0U, mgr.Diagnostics().size()); |
139 | } |
140 | |
141 | TEST(DiagnosticManagerTest, AppendMessageAttachToLastDiag) { |
142 | DiagnosticManager mgr; |
143 | |
144 | mgr.AddDiagnostic(diagnostic: std::make_unique<TextDiag>(args: "foo" , args: lldb::eSeverityError)); |
145 | mgr.AddDiagnostic(diagnostic: std::make_unique<TextDiag>(args: "bar" , args: lldb::eSeverityError)); |
146 | // This should append to 'bar' and not to 'foo'. |
147 | mgr.AppendMessageToDiagnostic(str: "message text" ); |
148 | |
149 | EXPECT_EQ("error: foo\nerror: bar\nmessage text\n" , toString(mgr)); |
150 | } |
151 | |
152 | TEST(DiagnosticManagerTest, AppendMessageSubsequentDiags) { |
153 | DiagnosticManager mgr; |
154 | |
155 | mgr.AddDiagnostic(diagnostic: std::make_unique<TextDiag>(args: "bar" , args: lldb::eSeverityError)); |
156 | mgr.AppendMessageToDiagnostic(str: "message text" ); |
157 | // Pushing another diag after the message should work fine. |
158 | mgr.AddDiagnostic(diagnostic: std::make_unique<TextDiag>(args: "foo" , args: lldb::eSeverityError)); |
159 | |
160 | EXPECT_EQ("error: bar\nmessage text\nerror: foo\n" , toString(mgr)); |
161 | } |
162 | |
163 | TEST(DiagnosticManagerTest, PutString) { |
164 | DiagnosticManager mgr; |
165 | |
166 | mgr.PutString(severity: lldb::eSeverityError, str: "foo" ); |
167 | EXPECT_EQ(1U, mgr.Diagnostics().size()); |
168 | EXPECT_EQ(eDiagnosticOriginLLDB, mgr.Diagnostics().front()->getKind()); |
169 | EXPECT_EQ("error: foo\n" , toString(mgr)); |
170 | } |
171 | |
172 | TEST(DiagnosticManagerTest, PutStringMultiple) { |
173 | DiagnosticManager mgr; |
174 | |
175 | // Multiple PutString should behave like multiple diagnostics. |
176 | mgr.PutString(severity: lldb::eSeverityError, str: "foo" ); |
177 | mgr.PutString(severity: lldb::eSeverityError, str: "bar" ); |
178 | EXPECT_EQ(2U, mgr.Diagnostics().size()); |
179 | EXPECT_EQ("error: foo\nerror: bar\n" , toString(mgr)); |
180 | } |
181 | |
182 | TEST(DiagnosticManagerTest, PutStringSeverities) { |
183 | DiagnosticManager mgr; |
184 | |
185 | // Multiple PutString with different severities should behave like we |
186 | // created multiple diagnostics. |
187 | mgr.PutString(severity: lldb::eSeverityError, str: "foo" ); |
188 | mgr.PutString(severity: lldb::eSeverityWarning, str: "bar" ); |
189 | EXPECT_EQ(2U, mgr.Diagnostics().size()); |
190 | EXPECT_EQ("error: foo\nwarning: bar\n" , toString(mgr)); |
191 | } |
192 | |
193 | TEST(DiagnosticManagerTest, FixedExpression) { |
194 | DiagnosticManager mgr; |
195 | |
196 | // By default there should be no fixed expression. |
197 | EXPECT_EQ("" , mgr.GetFixedExpression()); |
198 | |
199 | // Setting the fixed expression should change it. |
200 | mgr.SetFixedExpression("foo" ); |
201 | EXPECT_EQ("foo" , mgr.GetFixedExpression()); |
202 | |
203 | // Setting the fixed expression again should also change it. |
204 | mgr.SetFixedExpression("bar" ); |
205 | EXPECT_EQ("bar" , mgr.GetFixedExpression()); |
206 | } |
207 | |
208 | TEST(DiagnosticManagerTest, StatusConversion) { |
209 | DiagnosticManager mgr; |
210 | mgr.AddDiagnostic(diagnostic: std::make_unique<TextDiag>(args: "abc" , args: lldb::eSeverityError)); |
211 | mgr.AddDiagnostic(diagnostic: std::make_unique<TextDiag>(args: "def" , args: lldb::eSeverityWarning)); |
212 | Status status = |
213 | Status::FromError(error: mgr.GetAsError(result: lldb::eExpressionParseError)); |
214 | EXPECT_EQ(std::string("error: abc\nwarning: def\n" ), |
215 | std::string(status.AsCString())); |
216 | } |
217 | |