1 | //===-- FormatTests.cpp - Automatic code formatting 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 "Format.h" |
10 | #include "Annotations.h" |
11 | #include "SourceCode.h" |
12 | #include "clang/Format/Format.h" |
13 | #include "clang/Tooling/Core/Replacement.h" |
14 | #include "llvm/Support/Error.h" |
15 | #include "gmock/gmock.h" |
16 | #include "gtest/gtest.h" |
17 | |
18 | namespace clang { |
19 | namespace clangd { |
20 | namespace { |
21 | |
22 | std::string afterTyped(llvm::StringRef CodeWithCursor, llvm::StringRef Typed, |
23 | clang::format::FormatStyle Style) { |
24 | Annotations Code(CodeWithCursor); |
25 | unsigned Cursor = llvm::cantFail(ValOrErr: positionToOffset(Code: Code.code(), P: Code.point())); |
26 | auto Changes = formatIncremental(Code: Code.code(), Cursor, InsertedText: Typed, Style); |
27 | tooling::Replacements Merged; |
28 | for (const auto& R : Changes) |
29 | if (llvm::Error E = Merged.add(R)) |
30 | ADD_FAILURE() << llvm::toString(E: std::move(E)); |
31 | auto NewCode = tooling::applyAllReplacements(Code: Code.code(), Replaces: Merged); |
32 | EXPECT_TRUE(bool(NewCode)) |
33 | << "Bad replacements: " << llvm::toString(E: NewCode.takeError()); |
34 | NewCode->insert(pos: transformCursorPosition(Offset: Cursor, Replacements: Changes), s: "^" ); |
35 | return *NewCode; |
36 | } |
37 | |
38 | // We can't pass raw strings directly to EXPECT_EQ because of gcc bugs. |
39 | void expectAfterNewline(const char *Before, const char *After, |
40 | format::FormatStyle Style = format::getGoogleStyle( |
41 | Language: format::FormatStyle::LK_Cpp)) { |
42 | EXPECT_EQ(After, afterTyped(Before, "\n" , Style)) << Before; |
43 | } |
44 | void expectAfter(const char *Typed, const char *Before, const char *After, |
45 | format::FormatStyle Style = |
46 | format::getGoogleStyle(Language: format::FormatStyle::LK_Cpp)) { |
47 | EXPECT_EQ(After, afterTyped(Before, Typed, Style)) << Before; |
48 | } |
49 | |
50 | TEST(FormatIncremental, SplitComment) { |
51 | expectAfterNewline(Before: R"cpp( |
52 | // this comment was |
53 | ^split |
54 | )cpp" , |
55 | After: R"cpp( |
56 | // this comment was |
57 | // ^split |
58 | )cpp" ); |
59 | |
60 | expectAfterNewline(Before: R"cpp( |
61 | // trailing whitespace is not a split |
62 | ^ |
63 | )cpp" , |
64 | After: R"cpp( |
65 | // trailing whitespace is not a split |
66 | ^ |
67 | )cpp" ); |
68 | |
69 | expectAfterNewline(Before: R"cpp( |
70 | // splitting a |
71 | ^ |
72 | // multiline comment |
73 | )cpp" , |
74 | After: R"cpp( |
75 | // splitting a |
76 | // ^ |
77 | // multiline comment |
78 | )cpp" ); |
79 | |
80 | expectAfterNewline(Before: R"cpp( |
81 | // extra |
82 | ^ whitespace |
83 | )cpp" , |
84 | After: R"cpp( |
85 | // extra |
86 | // ^whitespace |
87 | )cpp" ); |
88 | |
89 | expectAfterNewline(Before: R"cpp( |
90 | /// triple |
91 | ^slash |
92 | )cpp" , |
93 | After: R"cpp( |
94 | /// triple |
95 | /// ^slash |
96 | )cpp" ); |
97 | |
98 | expectAfterNewline(Before: R"cpp( |
99 | /// editor continuation |
100 | //^ |
101 | )cpp" , |
102 | After: R"cpp( |
103 | /// editor continuation |
104 | /// ^ |
105 | )cpp" ); |
106 | |
107 | expectAfterNewline(Before: R"cpp( |
108 | // break before |
109 | ^ // slashes |
110 | )cpp" , |
111 | After: R"cpp( |
112 | // break before |
113 | ^// slashes |
114 | )cpp" ); |
115 | |
116 | |
117 | expectAfterNewline(Before: R"cpp( |
118 | int x; // aligned |
119 | ^comment |
120 | )cpp" , |
121 | After: R"cpp( |
122 | int x; // aligned |
123 | // ^comment |
124 | )cpp" ); |
125 | |
126 | // Fixed bug: the second line of the aligned comment shouldn't be "attached" |
127 | // to the cursor and outdented. |
128 | expectAfterNewline(Before: R"cpp( |
129 | void foo() { |
130 | if (x) |
131 | return; // All spelled tokens are accounted for. |
132 | // that takes two lines |
133 | ^ |
134 | } |
135 | )cpp" , |
136 | After: R"cpp( |
137 | void foo() { |
138 | if (x) |
139 | return; // All spelled tokens are accounted for. |
140 | // that takes two lines |
141 | ^ |
142 | } |
143 | )cpp" ); |
144 | |
145 | // Handle tab character in leading indentation |
146 | format::FormatStyle TabStyle = |
147 | format::getGoogleStyle(Language: format::FormatStyle::LK_Cpp); |
148 | TabStyle.UseTab = format::FormatStyle::UT_Always; |
149 | TabStyle.TabWidth = 4; |
150 | TabStyle.IndentWidth = 4; |
151 | // Do not use raw strings, otherwise '\t' will be interpreted literally. |
152 | expectAfterNewline(Before: "void foo() {\n\t// this comment was\n^split\n}\n" , |
153 | After: "void foo() {\n\t// this comment was\n\t// ^split\n}\n" , |
154 | Style: TabStyle); |
155 | } |
156 | |
157 | TEST(FormatIncremental, Indentation) { |
158 | expectAfterNewline(Before: R"cpp( |
159 | void foo() { |
160 | if (bar) |
161 | ^ |
162 | )cpp" , |
163 | After: R"cpp( |
164 | void foo() { |
165 | if (bar) |
166 | ^ |
167 | )cpp" ); |
168 | |
169 | expectAfterNewline(Before: R"cpp( |
170 | void foo() { |
171 | bar(baz( |
172 | ^ |
173 | )cpp" , |
174 | After: R"cpp( |
175 | void foo() { |
176 | bar(baz( |
177 | ^ |
178 | )cpp" ); |
179 | |
180 | expectAfterNewline(Before: R"cpp( |
181 | void foo() { |
182 | ^} |
183 | )cpp" , |
184 | After: R"cpp( |
185 | void foo() { |
186 | ^ |
187 | } |
188 | )cpp" ); |
189 | |
190 | expectAfterNewline(Before: R"cpp( |
191 | class X { |
192 | protected: |
193 | ^ |
194 | )cpp" , |
195 | After: R"cpp( |
196 | class X { |
197 | protected: |
198 | ^ |
199 | )cpp" ); |
200 | |
201 | // Mismatched brackets (1) |
202 | expectAfterNewline(Before: R"cpp( |
203 | void foo() { |
204 | foo{bar( |
205 | ^} |
206 | } |
207 | )cpp" , |
208 | After: R"cpp( |
209 | void foo() { |
210 | foo { |
211 | bar( |
212 | ^} |
213 | } |
214 | )cpp" ); |
215 | // Mismatched brackets (2) |
216 | expectAfterNewline(Before: R"cpp( |
217 | void foo() { |
218 | foo{bar( |
219 | ^text} |
220 | } |
221 | )cpp" , |
222 | After: R"cpp( |
223 | void foo() { |
224 | foo { |
225 | bar( |
226 | ^text} |
227 | } |
228 | )cpp" ); |
229 | // Matched brackets |
230 | expectAfterNewline(Before: R"cpp( |
231 | void foo() { |
232 | foo{bar( |
233 | ^) |
234 | } |
235 | )cpp" , |
236 | After: R"cpp( |
237 | void foo() { |
238 | foo { |
239 | bar( |
240 | ^) |
241 | } |
242 | )cpp" ); |
243 | } |
244 | |
245 | TEST(FormatIncremental, FormatPreviousLine) { |
246 | expectAfterNewline(Before: R"cpp( |
247 | void foo() { |
248 | untouched( ); |
249 | int x=2; |
250 | ^ |
251 | )cpp" , |
252 | After: R"cpp( |
253 | void foo() { |
254 | untouched( ); |
255 | int x = 2; |
256 | ^ |
257 | )cpp" ); |
258 | |
259 | expectAfterNewline(Before: R"cpp( |
260 | int x=untouched( ); |
261 | auto L = []{return;return;}; |
262 | ^ |
263 | )cpp" , |
264 | After: R"cpp( |
265 | int x=untouched( ); |
266 | auto L = [] { |
267 | return; |
268 | return; |
269 | }; |
270 | ^ |
271 | )cpp" ); |
272 | } |
273 | |
274 | TEST(FormatIncremental, Annoyances) { |
275 | // Don't remove newlines the user typed! |
276 | expectAfterNewline(Before: R"cpp( |
277 | int x(){ |
278 | |
279 | |
280 | ^ |
281 | } |
282 | )cpp" , |
283 | After: R"cpp( |
284 | int x(){ |
285 | |
286 | |
287 | ^ |
288 | } |
289 | )cpp" ); |
290 | // FIXME: we should not remove newlines here, either. |
291 | expectAfterNewline(Before: R"cpp( |
292 | class x{ |
293 | public: |
294 | |
295 | ^ |
296 | } |
297 | )cpp" , |
298 | After: R"cpp( |
299 | class x{ |
300 | public: |
301 | ^ |
302 | } |
303 | )cpp" ); |
304 | } |
305 | |
306 | TEST(FormatIncremental, FormatBrace) { |
307 | expectAfter(Typed: "}" , Before: R"cpp( |
308 | vector<int> x= { |
309 | 1, |
310 | 2, |
311 | 3}^ |
312 | )cpp" , |
313 | After: R"cpp( |
314 | vector<int> x = {1, 2, 3}^ |
315 | )cpp" ); |
316 | } |
317 | |
318 | } // namespace |
319 | } // namespace clangd |
320 | } // namespace clang |
321 | |