1 | //===- unittest/Format/FormatTestTableGen.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 "FormatTestUtils.h" |
10 | #include "clang/Format/Format.h" |
11 | #include "llvm/Support/Debug.h" |
12 | #include "gtest/gtest.h" |
13 | |
14 | #define DEBUG_TYPE "format-test" |
15 | |
16 | namespace clang { |
17 | namespace format { |
18 | |
19 | class FormatTestTableGen : public testing::Test { |
20 | protected: |
21 | static std::string format(StringRef Code, unsigned Offset, unsigned Length, |
22 | const FormatStyle &Style) { |
23 | LLVM_DEBUG(llvm::errs() << "---\n" ); |
24 | LLVM_DEBUG(llvm::errs() << Code << "\n\n" ); |
25 | std::vector<tooling::Range> Ranges(1, tooling::Range(Offset, Length)); |
26 | tooling::Replacements Replaces = reformat(Style, Code, Ranges); |
27 | auto Result = applyAllReplacements(Code, Replaces); |
28 | EXPECT_TRUE(static_cast<bool>(Result)); |
29 | LLVM_DEBUG(llvm::errs() << "\n" << *Result << "\n\n" ); |
30 | return *Result; |
31 | } |
32 | |
33 | static std::string format(StringRef Code) { |
34 | FormatStyle Style = getGoogleStyle(Language: FormatStyle::LK_TableGen); |
35 | Style.ColumnLimit = 60; // To make writing tests easier. |
36 | return format(Code, Offset: 0, Length: Code.size(), Style); |
37 | } |
38 | |
39 | static void verifyFormat(StringRef Code) { |
40 | EXPECT_EQ(Code.str(), format(Code)) << "Expected code is not stable" ; |
41 | EXPECT_EQ(Code.str(), format(test::messUp(Code))); |
42 | } |
43 | |
44 | static void verifyFormat(StringRef Result, StringRef MessedUp) { |
45 | EXPECT_EQ(Result, format(MessedUp)); |
46 | } |
47 | |
48 | static void verifyFormat(StringRef Code, const FormatStyle &Style) { |
49 | EXPECT_EQ(Code.str(), format(Code, 0, Code.size(), Style)) |
50 | << "Expected code is not stable" ; |
51 | auto MessUp = test::messUp(Code); |
52 | EXPECT_EQ(Code.str(), format(MessUp, 0, MessUp.size(), Style)); |
53 | } |
54 | }; |
55 | |
56 | TEST_F(FormatTestTableGen, FormatStringBreak) { |
57 | verifyFormat(Code: "include \"OptParser.td\"\n" |
58 | "def flag : Flag<\"--foo\">,\n" |
59 | " HelpText<\n" |
60 | " \"This is a very, very, very, very, \"\n" |
61 | " \"very, very, very, very, very, very, \"\n" |
62 | " \"very long help string\">;" ); |
63 | } |
64 | |
65 | TEST_F(FormatTestTableGen, NoSpacesInSquareBracketLists) { |
66 | verifyFormat(Code: "def flag : Flag<[\"-\", \"--\"], \"foo\">;" ); |
67 | } |
68 | |
69 | TEST_F(FormatTestTableGen, LiteralsAndIdentifiers) { |
70 | verifyFormat(Code: "def LiteralAndIdentifiers {\n" |
71 | " let someInteger = -42;\n" |
72 | " let 0startID = $TokVarName;\n" |
73 | " let 0xstartInteger = 0x42;\n" |
74 | " let someIdentifier = $TokVarName;\n" |
75 | "}" ); |
76 | } |
77 | |
78 | TEST_F(FormatTestTableGen, BangOperators) { |
79 | verifyFormat(Code: "def BangOperators {\n" |
80 | " let IfOpe = !if(\n" |
81 | " !not(!and(!gt(!add(1, 2), !sub(3, 4)), !isa<Ty>($x))),\n" |
82 | " !foldl(0, !listconcat(!range(5, 6), !range(7, 8)),\n" |
83 | " total, rec, !add(total, rec.Number)),\n" |
84 | " !tail(!range(9, 10)));\n" |
85 | " let ForeachOpe = !foreach(\n" |
86 | " arg, arglist,\n" |
87 | " !if(!isa<SomeType>(arg.Type),\n" |
88 | " !add(!cast<SomeOtherType>(arg).Number, x), arg));\n" |
89 | " let CondOpe1 = !cond(!eq(size, 1): 1,\n" |
90 | " !eq(size, 2): 1,\n" |
91 | " !eq(size, 4): 1,\n" |
92 | " !eq(size, 8): 1,\n" |
93 | " !eq(size, 16): 1,\n" |
94 | " true: 0);\n" |
95 | " let CondOpe2 = !cond(!lt(x, 0): \"negativenegative\",\n" |
96 | " !eq(x, 0): \"zerozero\",\n" |
97 | " true: \"positivepositive\");\n" |
98 | " let CondOpe2WithComment = !cond(!lt(x, 0): // negative\n" |
99 | " \"negativenegative\",\n" |
100 | " !eq(x, 0): // zero\n" |
101 | " \"zerozero\",\n" |
102 | " true: // default\n" |
103 | " \"positivepositive\");\n" |
104 | " let CondOpe3WithCommentAfterLParen = !cond(\n" |
105 | " // comment\n" |
106 | " !eq(/* comment */ x, 0): \"zero\");\n" |
107 | "}" ); |
108 | } |
109 | |
110 | TEST_F(FormatTestTableGen, Include) { |
111 | verifyFormat(Code: "include \"test/IncludeFile.h\"" ); |
112 | } |
113 | |
114 | TEST_F(FormatTestTableGen, Types) { |
115 | verifyFormat(Code: "def Types : list<int>, bits<3>, list<list<string>> {}" ); |
116 | } |
117 | |
118 | TEST_F(FormatTestTableGen, SimpleValue1_SingleLiterals) { |
119 | verifyFormat(Code: "def SimpleValue {\n" |
120 | " let Integer = 42;\n" |
121 | " let String = \"some string\";\n" |
122 | "}" ); |
123 | } |
124 | |
125 | TEST_F(FormatTestTableGen, SimpleValue1_MultilineString) { |
126 | // test::messUp does not understand multiline TableGen code-literals. |
127 | // We have to give the result and the strings to format manually. |
128 | StringRef DefWithCode = |
129 | "def SimpleValueCode {\n" |
130 | " let Code =\n" |
131 | " [{ A TokCode is nothing more than a multi-line string literal " |
132 | "delimited by \\[{ and }\\]. It can break across lines and the line " |
133 | "breaks are retained in the string. \n" |
134 | "(https://llvm.org/docs/TableGen/ProgRef.html#grammar-token-TokCode)}];\n" |
135 | "}" ; |
136 | StringRef DefWithCodeMessedUp = |
137 | "def SimpleValueCode { let \n" |
138 | "Code= \n" |
139 | " [{ A TokCode is nothing more than a multi-line string " |
140 | "literal " |
141 | "delimited by \\[{ and }\\]. It can break across lines and the line " |
142 | "breaks are retained in the string. \n" |
143 | "(https://llvm.org/docs/TableGen/ProgRef.html#grammar-token-TokCode)}] \n" |
144 | " ; \n" |
145 | " } " ; |
146 | verifyFormat(Result: DefWithCode, MessedUp: DefWithCodeMessedUp); |
147 | } |
148 | |
149 | TEST_F(FormatTestTableGen, SimpleValue2) { |
150 | verifyFormat(Code: "def SimpleValue2 {\n" |
151 | " let True = true;\n" |
152 | " let False = false;\n" |
153 | "}" ); |
154 | } |
155 | |
156 | TEST_F(FormatTestTableGen, SimpleValue3) { |
157 | verifyFormat(Code: "class SimpleValue3<int x> { int Question = ?; }" ); |
158 | } |
159 | |
160 | TEST_F(FormatTestTableGen, SimpleValue4) { |
161 | verifyFormat(Code: "def SimpleValue4 { let ValueList = {1, 2, 3}; }" ); |
162 | } |
163 | |
164 | TEST_F(FormatTestTableGen, SimpleValue5) { |
165 | verifyFormat(Code: "def SimpleValue5 {\n" |
166 | " let SquareList = [1, 4, 9];\n" |
167 | " let SquareListWithType = [\"a\", \"b\", \"c\"]<string>;\n" |
168 | " let SquareListListWithType = [[1, 2], [3, 4, 5], [7]]<\n" |
169 | " list<int>>;\n" |
170 | " let SquareBitsListWithType = [ {1, 2},\n" |
171 | " {3, 4} ]<list<bits<8>>>;\n" |
172 | "}" ); |
173 | } |
174 | |
175 | TEST_F(FormatTestTableGen, SimpleValue6) { |
176 | verifyFormat(Code: "def SimpleValue6 {\n" |
177 | " let DAGArgIns = (ins i32:$src1, i32:$src2);\n" |
178 | " let DAGArgOuts = (outs i32:$dst1, i32:$dst2, i32:$dst3,\n" |
179 | " i32:$dst4, i32:$dst5, i32:$dst6, i32:$dst7);\n" |
180 | " let DAGArgOutsWithComment = (outs i32:$dst1, // dst1\n" |
181 | " i32:$dst2, // dst2\n" |
182 | " i32:$dst3, // dst3\n" |
183 | " i32:$dst4, // dst4\n" |
184 | " i32:$dst5, // dst5\n" |
185 | " i32:$dst6, // dst6\n" |
186 | " i32:$dst7 // dst7\n" |
187 | " );\n" |
188 | " let DAGArgBang = (!cast<SomeType>(\"Some\") i32:$src1,\n" |
189 | " i32:$src2);\n" |
190 | "}" ); |
191 | } |
192 | |
193 | TEST_F(FormatTestTableGen, SimpleValue7) { |
194 | verifyFormat(Code: "def SimpleValue7 { let Identifier = SimpleValue; }" ); |
195 | } |
196 | |
197 | TEST_F(FormatTestTableGen, SimpleValue8) { |
198 | verifyFormat(Code: "def SimpleValue8 { let Class = SimpleValue3<3>; }" ); |
199 | } |
200 | |
201 | TEST_F(FormatTestTableGen, ValueSuffix) { |
202 | verifyFormat(Code: "def SuffixedValues {\n" |
203 | " let Bit = value{17};\n" |
204 | " let Bits = value{8...15};\n" |
205 | " let List = value[1];\n" |
206 | " let Slice1 = value[1, ];\n" |
207 | " let Slice2 = value[4...7, 17, 2...3, 4];\n" |
208 | " let Field = value.field;\n" |
209 | "}" ); |
210 | } |
211 | |
212 | TEST_F(FormatTestTableGen, PasteOperator) { |
213 | verifyFormat(Code: "def Paste#\"Operator\" { string Paste = \"Paste\"#operator; }" ); |
214 | |
215 | verifyFormat(Code: "def [\"Traring\", \"Paste\"]# {\n" |
216 | " string X = Traring#;\n" |
217 | " string Y = List<\"Operator\">#;\n" |
218 | " string Z = [\"Traring\", \"Paste\", \"Traring\", \"Paste\",\n" |
219 | " \"Traring\", \"Paste\"]#;\n" |
220 | "}" ); |
221 | |
222 | verifyFormat(Result: "def x#x {}" , MessedUp: "def x\n" |
223 | "#x {}" ); |
224 | verifyFormat(Result: "def x#x {}" , MessedUp: "def x\n" |
225 | "#\n" |
226 | "x {}" ); |
227 | verifyFormat(Code: "def x#x" ); |
228 | } |
229 | |
230 | TEST_F(FormatTestTableGen, ClassDefinition) { |
231 | verifyFormat(Code: "class Class<int x, int y = 1, string z = \"z\", int w = -1>\n" |
232 | " : Parent1, Parent2<x, y> {\n" |
233 | " int Item1 = 1;\n" |
234 | " int Item2;\n" |
235 | " code Item3 = [{ Item3 }];\n" |
236 | " let Item4 = 4;\n" |
237 | " let Item5{1, 2} = 5;\n" |
238 | " defvar Item6 = 6;\n" |
239 | " let Item7 = ?;\n" |
240 | " assert !ge(x, 0), \"Assert7\";\n" |
241 | "}" ); |
242 | |
243 | verifyFormat(Code: "class FPFormat<bits<3> val> { bits<3> Value = val; }" ); |
244 | } |
245 | |
246 | TEST_F(FormatTestTableGen, Def) { |
247 | verifyFormat(Code: "def Def : Parent1<Def>, Parent2(defs Def) {\n" |
248 | " code Item1 = [{ Item1 }];\n" |
249 | " let Item2{1, 3...4} = {1, 2};\n" |
250 | " defvar Item3 = (ops nodty:$node1, nodty:$node2);\n" |
251 | " assert !le(Item2, 0), \"Assert4\";\n" |
252 | "}" ); |
253 | |
254 | verifyFormat(Code: "class FPFormat<bits<3> val> { bits<3> Value = val; }" ); |
255 | |
256 | verifyFormat(Code: "def NotFP : FPFormat<0>;" ); |
257 | } |
258 | |
259 | TEST_F(FormatTestTableGen, Let) { |
260 | verifyFormat(Code: "let x = 1, y = value<type>,\n" |
261 | " z = !and(!gt(!add(1, 2), !sub(3, 4)), !isa<Ty>($x)) in {\n" |
262 | " class Class1 : Parent<x, y> { let Item1 = z; }\n" |
263 | "}" ); |
264 | } |
265 | |
266 | TEST_F(FormatTestTableGen, MultiClass) { |
267 | verifyFormat(Code: "multiclass Multiclass<int x> {\n" |
268 | " def : Def1<(item type:$src1),\n" |
269 | " (!if(!ge(x, 0), !mul(!add(x, 1), !sub(x, 2)),\n" |
270 | " !sub(x, 2)))>;\n" |
271 | " def Def2 : value<type>;\n" |
272 | " def Def3 : type { let value = 1; }\n" |
273 | " defm : SomeMultiClass<Def1, Def2>;\n" |
274 | " defvar DefVar = 6;\n" |
275 | " foreach i = [1, 2, 3] in {\n" |
276 | " def : Foreach#i<(item type:$src1),\n" |
277 | " (!if(!gt(x, i),\n" |
278 | " !mul(!add(x, i), !sub(x, i)),\n" |
279 | " !sub(x, !add(i, 1))))>;\n" |
280 | " }\n" |
281 | " if !gt(x, 0) then {\n" |
282 | " def : IfThen<x>;\n" |
283 | " } else {\n" |
284 | " def : IfElse<x>;\n" |
285 | " }\n" |
286 | " if (dagid x, 0) then {\n" |
287 | " def : If2<1>;\n" |
288 | " }\n" |
289 | " let y = 1, z = 2 in {\n" |
290 | " multiclass Multiclass2<int x> {\n" |
291 | " foreach i = [1, 2, 3] in {\n" |
292 | " def : Foreach#i<(item type:$src1),\n" |
293 | " (!if(!gt(z, i),\n" |
294 | " !mul(!add(y, i), !sub(x, i)),\n" |
295 | " !sub(z, !add(i, 1))))>;\n" |
296 | " }\n" |
297 | " }\n" |
298 | " }\n" |
299 | "}" ); |
300 | } |
301 | |
302 | TEST_F(FormatTestTableGen, MultiClassesWithPasteOperator) { |
303 | // This is a sensitive example for the handling of the paste operators in |
304 | // brace type calculation. |
305 | verifyFormat(Code: "multiclass MultiClass1<int i> {\n" |
306 | " def : Def#x<i>;\n" |
307 | " def : Def#y<i>;\n" |
308 | "}\n" |
309 | "multiclass MultiClass2<int i> { def : Def#x<i>; }" ); |
310 | } |
311 | |
312 | TEST_F(FormatTestTableGen, Defm) { |
313 | verifyFormat(Code: "defm : Multiclass<0>;" ); |
314 | |
315 | verifyFormat(Code: "defm Defm1 : Multiclass<1>;" ); |
316 | } |
317 | |
318 | TEST_F(FormatTestTableGen, Defset) { |
319 | verifyFormat(Code: "defset list<Class> DefSet1 = {\n" |
320 | " def Def1 : Class<1>;\n" |
321 | " def Def2 : Class<2>;\n" |
322 | "}" ); |
323 | } |
324 | |
325 | TEST_F(FormatTestTableGen, Defvar) { |
326 | verifyFormat(Code: "defvar DefVar1 = !cond(!ge(!size(PaseOperator.Paste), 1): 1,\n" |
327 | " true: 0);" ); |
328 | } |
329 | |
330 | TEST_F(FormatTestTableGen, ForEach) { |
331 | verifyFormat( |
332 | Code: "foreach i = [1, 2, 3] in {\n" |
333 | " def : Foreach#i<(item type:$src1),\n" |
334 | " (!if(!lt(x, i),\n" |
335 | " !shl(!mul(x, i), !size(\"string\")),\n" |
336 | " !size(!strconcat(\"a\", \"b\", \"c\"))))>;\n" |
337 | "}" ); |
338 | } |
339 | |
340 | TEST_F(FormatTestTableGen, Dump) { verifyFormat(Code: "dump \"Dump\";" ); } |
341 | |
342 | TEST_F(FormatTestTableGen, If) { |
343 | verifyFormat(Code: "if !gt(x, 0) then {\n" |
344 | " def : IfThen<x>;\n" |
345 | "} else {\n" |
346 | " def : IfElse<x>;\n" |
347 | "}" ); |
348 | } |
349 | |
350 | TEST_F(FormatTestTableGen, Assert) { |
351 | verifyFormat(Code: "assert !le(DefVar1, 0), \"Assert1\";" ); |
352 | } |
353 | |
354 | TEST_F(FormatTestTableGen, DAGArgBreakElements) { |
355 | FormatStyle Style = getGoogleStyle(Language: FormatStyle::LK_TableGen); |
356 | Style.ColumnLimit = 60; |
357 | // By default, the DAGArg does not have a break inside. |
358 | ASSERT_EQ(Style.TableGenBreakInsideDAGArg, FormatStyle::DAS_DontBreak); |
359 | verifyFormat(Code: "def Def : Parent {\n" |
360 | " let dagarg = (ins a:$src1, aa:$src2, aaa:$src3)\n" |
361 | "}" , |
362 | Style); |
363 | // This option forces to break inside the DAGArg. |
364 | Style.TableGenBreakInsideDAGArg = FormatStyle::DAS_BreakElements; |
365 | verifyFormat(Code: "def Def : Parent {\n" |
366 | " let dagarg = (ins a:$src1,\n" |
367 | " aa:$src2,\n" |
368 | " aaa:$src3);\n" |
369 | "}" , |
370 | Style); |
371 | verifyFormat(Code: "def Def : Parent {\n" |
372 | " let dagarg = (other a:$src1,\n" |
373 | " aa:$src2,\n" |
374 | " aaa:$src3);\n" |
375 | "}" , |
376 | Style); |
377 | // Then, limit the DAGArg operator only to "ins". |
378 | Style.TableGenBreakingDAGArgOperators = {"ins" }; |
379 | verifyFormat(Code: "def Def : Parent {\n" |
380 | " let dagarg = (ins a:$src1,\n" |
381 | " aa:$src2,\n" |
382 | " aaa:$src3);\n" |
383 | "}" , |
384 | Style); |
385 | verifyFormat(Code: "def Def : Parent {\n" |
386 | " let dagarg = (other a:$src1, aa:$src2, aaa:$src3)\n" |
387 | "}" , |
388 | Style); |
389 | } |
390 | |
391 | TEST_F(FormatTestTableGen, DAGArgBreakAll) { |
392 | FormatStyle Style = getGoogleStyle(Language: FormatStyle::LK_TableGen); |
393 | Style.ColumnLimit = 60; |
394 | // By default, the DAGArg does not have a break inside. |
395 | verifyFormat(Code: "def Def : Parent {\n" |
396 | " let dagarg = (ins a:$src1, aa:$src2, aaa:$src3)\n" |
397 | "}" , |
398 | Style); |
399 | // This option forces to break inside the DAGArg. |
400 | Style.TableGenBreakInsideDAGArg = FormatStyle::DAS_BreakAll; |
401 | verifyFormat(Code: "def Def : Parent {\n" |
402 | " let dagarg = (ins\n" |
403 | " a:$src1,\n" |
404 | " aa:$src2,\n" |
405 | " aaa:$src3\n" |
406 | " );\n" |
407 | "}" , |
408 | Style); |
409 | verifyFormat(Code: "def Def : Parent {\n" |
410 | " let dagarg = (other\n" |
411 | " a:$src1,\n" |
412 | " aa:$src2,\n" |
413 | " aaa:$src3\n" |
414 | " );\n" |
415 | "}" , |
416 | Style); |
417 | // Then, limit the DAGArg operator only to "ins". |
418 | Style.TableGenBreakingDAGArgOperators = {"ins" }; |
419 | verifyFormat(Code: "def Def : Parent {\n" |
420 | " let dagarg = (ins\n" |
421 | " a:$src1,\n" |
422 | " aa:$src2,\n" |
423 | " aaa:$src3\n" |
424 | " );\n" |
425 | "}" , |
426 | Style); |
427 | verifyFormat(Code: "def Def : Parent {\n" |
428 | " let dagarg = (other a:$src1, aa:$src2, aaa:$src3);\n" |
429 | "}" , |
430 | Style); |
431 | } |
432 | |
433 | TEST_F(FormatTestTableGen, DAGArgAlignment) { |
434 | FormatStyle Style = getGoogleStyle(Language: FormatStyle::LK_TableGen); |
435 | Style.ColumnLimit = 60; |
436 | Style.TableGenBreakInsideDAGArg = FormatStyle::DAS_BreakAll; |
437 | Style.TableGenBreakingDAGArgOperators = {"ins" , "outs" }; |
438 | verifyFormat(Code: "def Def : Parent {\n" |
439 | " let dagarg = (ins\n" |
440 | " a:$src1,\n" |
441 | " aa:$src2,\n" |
442 | " aaa:$src3\n" |
443 | " )\n" |
444 | "}" , |
445 | Style); |
446 | verifyFormat(Code: "def Def : Parent {\n" |
447 | " let dagarg = (not a:$src1, aa:$src2, aaa:$src2)\n" |
448 | "}" , |
449 | Style); |
450 | Style.AlignConsecutiveTableGenBreakingDAGArgColons.Enabled = true; |
451 | verifyFormat(Code: "def Def : Parent {\n" |
452 | " let dagarg = (ins\n" |
453 | " a :$src1,\n" |
454 | " aa :$src2,\n" |
455 | " aaa:$src3\n" |
456 | " )\n" |
457 | "}" , |
458 | Style); |
459 | verifyFormat(Code: "def Def : Parent {\n" |
460 | " let dagarg = (not a:$src1, aa:$src2, aaa:$src2)\n" |
461 | "}" , |
462 | Style); |
463 | } |
464 | |
465 | TEST_F(FormatTestTableGen, CondOperatorAlignment) { |
466 | FormatStyle Style = getGoogleStyle(Language: FormatStyle::LK_TableGen); |
467 | Style.ColumnLimit = 60; |
468 | verifyFormat(Code: "let CondOpe1 = !cond(!eq(size, 1): 1,\n" |
469 | " !eq(size, 16): 1,\n" |
470 | " true: 0);" , |
471 | Style); |
472 | Style.AlignConsecutiveTableGenCondOperatorColons.Enabled = true; |
473 | verifyFormat(Code: "let CondOpe1 = !cond(!eq(size, 1) : 1,\n" |
474 | " !eq(size, 16): 1,\n" |
475 | " true : 0);" , |
476 | Style); |
477 | } |
478 | |
479 | TEST_F(FormatTestTableGen, DefAlignment) { |
480 | FormatStyle Style = getGoogleStyle(Language: FormatStyle::LK_TableGen); |
481 | Style.ColumnLimit = 60; |
482 | verifyFormat(Code: "def Def : Parent {}\n" |
483 | "def DefDef : Parent {}\n" |
484 | "def DefDefDef : Parent {}" , |
485 | Style); |
486 | Style.AlignConsecutiveTableGenDefinitionColons.Enabled = true; |
487 | verifyFormat(Code: "def Def : Parent {}\n" |
488 | "def DefDef : Parent {}\n" |
489 | "def DefDefDef : Parent {}" , |
490 | Style); |
491 | } |
492 | |
493 | } // namespace format |
494 | } // end namespace clang |
495 | |