1 | //===- unittests/Tooling/DiagnosticsYamlTest.cpp - Serialization 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 | // Tests for serialization of Diagnostics. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "clang/Tooling/DiagnosticsYaml.h" |
14 | #include "clang/Tooling/Core/Diagnostic.h" |
15 | #include "clang/Tooling/ReplacementsYaml.h" |
16 | #include "llvm/ADT/SmallVector.h" |
17 | #include "gtest/gtest.h" |
18 | |
19 | using namespace llvm; |
20 | using namespace clang::tooling; |
21 | using clang::tooling::Diagnostic; |
22 | |
23 | static DiagnosticMessage |
24 | makeMessage(const std::string &Message, int FileOffset, |
25 | const std::string &FilePath, const StringMap<Replacements> &Fix, |
26 | const SmallVector<FileByteRange, 1> &Ranges) { |
27 | DiagnosticMessage DiagMessage; |
28 | DiagMessage.Message = Message; |
29 | DiagMessage.FileOffset = FileOffset; |
30 | DiagMessage.FilePath = FilePath; |
31 | DiagMessage.Fix = Fix; |
32 | DiagMessage.Ranges = Ranges; |
33 | return DiagMessage; |
34 | } |
35 | |
36 | static FileByteRange makeByteRange(int FileOffset, |
37 | int Length, |
38 | const std::string &FilePath) { |
39 | FileByteRange Range; |
40 | Range.FileOffset = FileOffset; |
41 | Range.Length = Length; |
42 | Range.FilePath = FilePath; |
43 | return Range; |
44 | } |
45 | |
46 | static Diagnostic makeDiagnostic(StringRef DiagnosticName, |
47 | const std::string &Message, int FileOffset, |
48 | const std::string &FilePath, |
49 | const StringMap<Replacements> &Fix, |
50 | const SmallVector<FileByteRange, 1> &Ranges, |
51 | Diagnostic::Level DiagnosticLevel) { |
52 | return Diagnostic(DiagnosticName, |
53 | makeMessage(Message, FileOffset, FilePath, Fix, Ranges), {}, |
54 | DiagnosticLevel, "path/to/build/directory" ); |
55 | } |
56 | |
57 | static const char *YAMLContent = |
58 | "---\n" |
59 | "MainSourceFile: 'path/to/source.cpp'\n" |
60 | "Diagnostics:\n" |
61 | " - DiagnosticName: 'diagnostic#1\'\n" |
62 | " DiagnosticMessage:\n" |
63 | " Message: 'message #1'\n" |
64 | " FilePath: 'path/to/source.cpp'\n" |
65 | " FileOffset: 55\n" |
66 | " Replacements:\n" |
67 | " - FilePath: 'path/to/source.cpp'\n" |
68 | " Offset: 100\n" |
69 | " Length: 12\n" |
70 | " ReplacementText: 'replacement #1'\n" |
71 | " Level: Warning\n" |
72 | " BuildDirectory: 'path/to/build/directory'\n" |
73 | " - DiagnosticName: 'diagnostic#2'\n" |
74 | " DiagnosticMessage:\n" |
75 | " Message: 'message #2'\n" |
76 | " FilePath: 'path/to/header.h'\n" |
77 | " FileOffset: 60\n" |
78 | " Replacements:\n" |
79 | " - FilePath: 'path/to/header.h'\n" |
80 | " Offset: 62\n" |
81 | " Length: 2\n" |
82 | " ReplacementText: 'replacement #2'\n" |
83 | " Ranges:\n" |
84 | " - FilePath: 'path/to/source.cpp'\n" |
85 | " FileOffset: 10\n" |
86 | " Length: 10\n" |
87 | " Level: Warning\n" |
88 | " BuildDirectory: 'path/to/build/directory'\n" |
89 | " - DiagnosticName: 'diagnostic#3'\n" |
90 | " DiagnosticMessage:\n" |
91 | " Message: 'message #3'\n" |
92 | " FilePath: 'path/to/source2.cpp'\n" |
93 | " FileOffset: 72\n" |
94 | " Replacements: []\n" |
95 | " Notes:\n" |
96 | " - Message: Note1\n" |
97 | " FilePath: 'path/to/note1.cpp'\n" |
98 | " FileOffset: 88\n" |
99 | " Replacements: []\n" |
100 | " - Message: Note2\n" |
101 | " FilePath: 'path/to/note2.cpp'\n" |
102 | " FileOffset: 99\n" |
103 | " Replacements: []\n" |
104 | " Level: Warning\n" |
105 | " BuildDirectory: 'path/to/build/directory'\n" |
106 | " - DiagnosticName: 'diagnostic#4'\n" |
107 | " DiagnosticMessage:\n" |
108 | " Message: 'message #4'\n" |
109 | " FilePath: 'path/to/source3.cpp'\n" |
110 | " FileOffset: 72\n" |
111 | " Replacements: []\n" |
112 | " Level: Remark\n" |
113 | " BuildDirectory: 'path/to/build/directory'\n" |
114 | "...\n" ; |
115 | |
116 | TEST(DiagnosticsYamlTest, serializesDiagnostics) { |
117 | TranslationUnitDiagnostics TUD; |
118 | TUD.MainSourceFile = "path/to/source.cpp" ; |
119 | |
120 | StringMap<Replacements> Fix1 = { |
121 | {"path/to/source.cpp" , |
122 | Replacements({"path/to/source.cpp" , 100, 12, "replacement #1" })}}; |
123 | TUD.Diagnostics.push_back(x: makeDiagnostic(DiagnosticName: "diagnostic#1" , Message: "message #1" , FileOffset: 55, |
124 | FilePath: "path/to/source.cpp" , Fix: Fix1, Ranges: {}, |
125 | DiagnosticLevel: Diagnostic::Warning)); |
126 | |
127 | StringMap<Replacements> Fix2 = { |
128 | {"path/to/header.h" , |
129 | Replacements({"path/to/header.h" , 62, 2, "replacement #2" })}}; |
130 | SmallVector<FileByteRange, 1> Ranges2 = |
131 | {makeByteRange(FileOffset: 10, Length: 10, FilePath: "path/to/source.cpp" )}; |
132 | TUD.Diagnostics.push_back(x: makeDiagnostic(DiagnosticName: "diagnostic#2" , Message: "message #2" , FileOffset: 60, |
133 | FilePath: "path/to/header.h" , Fix: Fix2, Ranges: Ranges2, |
134 | DiagnosticLevel: Diagnostic::Warning)); |
135 | |
136 | TUD.Diagnostics.push_back(x: makeDiagnostic(DiagnosticName: "diagnostic#3" , Message: "message #3" , FileOffset: 72, |
137 | FilePath: "path/to/source2.cpp" , Fix: {}, Ranges: {}, |
138 | DiagnosticLevel: Diagnostic::Warning)); |
139 | TUD.Diagnostics.back().Notes.push_back( |
140 | Elt: makeMessage(Message: "Note1" , FileOffset: 88, FilePath: "path/to/note1.cpp" , Fix: {}, Ranges: {})); |
141 | TUD.Diagnostics.back().Notes.push_back( |
142 | Elt: makeMessage(Message: "Note2" , FileOffset: 99, FilePath: "path/to/note2.cpp" , Fix: {}, Ranges: {})); |
143 | |
144 | TUD.Diagnostics.push_back(x: makeDiagnostic(DiagnosticName: "diagnostic#4" , Message: "message #4" , FileOffset: 72, |
145 | FilePath: "path/to/source3.cpp" , Fix: {}, Ranges: {}, |
146 | DiagnosticLevel: Diagnostic::Remark)); |
147 | |
148 | std::string YamlContent; |
149 | raw_string_ostream YamlContentStream(YamlContent); |
150 | |
151 | yaml::Output YAML(YamlContentStream); |
152 | YAML << TUD; |
153 | |
154 | EXPECT_EQ(YAMLContent, YamlContentStream.str()); |
155 | } |
156 | |
157 | TEST(DiagnosticsYamlTest, deserializesDiagnostics) { |
158 | TranslationUnitDiagnostics TUDActual; |
159 | yaml::Input YAML(YAMLContent); |
160 | YAML >> TUDActual; |
161 | |
162 | ASSERT_FALSE(YAML.error()); |
163 | ASSERT_EQ(4u, TUDActual.Diagnostics.size()); |
164 | EXPECT_EQ("path/to/source.cpp" , TUDActual.MainSourceFile); |
165 | |
166 | auto getFixes = [](const StringMap<Replacements> &Fix) { |
167 | std::vector<Replacement> Fixes; |
168 | for (auto &Replacements : Fix) { |
169 | for (auto &Replacement : Replacements.second) { |
170 | Fixes.push_back(x: Replacement); |
171 | } |
172 | } |
173 | return Fixes; |
174 | }; |
175 | |
176 | Diagnostic D1 = TUDActual.Diagnostics[0]; |
177 | EXPECT_EQ("diagnostic#1" , D1.DiagnosticName); |
178 | EXPECT_EQ("message #1" , D1.Message.Message); |
179 | EXPECT_EQ(55u, D1.Message.FileOffset); |
180 | EXPECT_EQ("path/to/source.cpp" , D1.Message.FilePath); |
181 | std::vector<Replacement> Fixes1 = getFixes(D1.Message.Fix); |
182 | ASSERT_EQ(1u, Fixes1.size()); |
183 | EXPECT_EQ("path/to/source.cpp" , Fixes1[0].getFilePath()); |
184 | EXPECT_EQ(100u, Fixes1[0].getOffset()); |
185 | EXPECT_EQ(12u, Fixes1[0].getLength()); |
186 | EXPECT_EQ("replacement #1" , Fixes1[0].getReplacementText()); |
187 | EXPECT_TRUE(D1.Message.Ranges.empty()); |
188 | |
189 | Diagnostic D2 = TUDActual.Diagnostics[1]; |
190 | EXPECT_EQ("diagnostic#2" , D2.DiagnosticName); |
191 | EXPECT_EQ("message #2" , D2.Message.Message); |
192 | EXPECT_EQ(60u, D2.Message.FileOffset); |
193 | EXPECT_EQ("path/to/header.h" , D2.Message.FilePath); |
194 | std::vector<Replacement> Fixes2 = getFixes(D2.Message.Fix); |
195 | ASSERT_EQ(1u, Fixes2.size()); |
196 | EXPECT_EQ("path/to/header.h" , Fixes2[0].getFilePath()); |
197 | EXPECT_EQ(62u, Fixes2[0].getOffset()); |
198 | EXPECT_EQ(2u, Fixes2[0].getLength()); |
199 | EXPECT_EQ("replacement #2" , Fixes2[0].getReplacementText()); |
200 | EXPECT_EQ(1u, D2.Message.Ranges.size()); |
201 | EXPECT_EQ("path/to/source.cpp" , D2.Message.Ranges[0].FilePath); |
202 | EXPECT_EQ(10u, D2.Message.Ranges[0].FileOffset); |
203 | EXPECT_EQ(10u, D2.Message.Ranges[0].Length); |
204 | |
205 | Diagnostic D3 = TUDActual.Diagnostics[2]; |
206 | EXPECT_EQ("diagnostic#3" , D3.DiagnosticName); |
207 | EXPECT_EQ("message #3" , D3.Message.Message); |
208 | EXPECT_EQ(72u, D3.Message.FileOffset); |
209 | EXPECT_EQ("path/to/source2.cpp" , D3.Message.FilePath); |
210 | EXPECT_EQ(2u, D3.Notes.size()); |
211 | EXPECT_EQ("Note1" , D3.Notes[0].Message); |
212 | EXPECT_EQ(88u, D3.Notes[0].FileOffset); |
213 | EXPECT_EQ("path/to/note1.cpp" , D3.Notes[0].FilePath); |
214 | EXPECT_EQ("Note2" , D3.Notes[1].Message); |
215 | EXPECT_EQ(99u, D3.Notes[1].FileOffset); |
216 | EXPECT_EQ("path/to/note2.cpp" , D3.Notes[1].FilePath); |
217 | std::vector<Replacement> Fixes3 = getFixes(D3.Message.Fix); |
218 | EXPECT_TRUE(Fixes3.empty()); |
219 | EXPECT_TRUE(D3.Message.Ranges.empty()); |
220 | } |
221 | |