1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <sstream>
6
7 #include "gn/input_file.h"
8 #include "gn/parser.h"
9 #include "gn/tokenizer.h"
10 #include "util/test/test.h"
11
12 namespace {
13
GetTokens(const InputFile * input,std::vector<Token> * result)14 bool GetTokens(const InputFile* input, std::vector<Token>* result) {
15 result->clear();
16 Err err;
17 *result = Tokenizer::Tokenize(input, &err);
18 return !err.has_error();
19 }
20
DoParserPrintTest(const char * input,const char * expected)21 void DoParserPrintTest(const char* input, const char* expected) {
22 std::vector<Token> tokens;
23 InputFile input_file(SourceFile("/test"));
24 input_file.SetContents(input);
25 ASSERT_TRUE(GetTokens(&input_file, &tokens));
26
27 Err err;
28 std::unique_ptr<ParseNode> result = Parser::Parse(tokens, &err);
29 if (!result)
30 err.PrintToStdout();
31 ASSERT_TRUE(result);
32
33 std::ostringstream collector;
34 RenderToText(result->GetJSONNode(), 0, collector);
35
36 EXPECT_EQ(expected, collector.str());
37 }
38
DoExpressionPrintTest(const char * input,const char * expected)39 void DoExpressionPrintTest(const char* input, const char* expected) {
40 std::vector<Token> tokens;
41 InputFile input_file(SourceFile("/test"));
42 input_file.SetContents(input);
43 ASSERT_TRUE(GetTokens(&input_file, &tokens));
44
45 Err err;
46 std::unique_ptr<ParseNode> result = Parser::ParseExpression(tokens, &err);
47 ASSERT_TRUE(result);
48
49 std::ostringstream collector;
50 RenderToText(result->GetJSONNode(), 0, collector);
51
52 EXPECT_EQ(expected, collector.str());
53 }
54
55 // Expects the tokenizer or parser to identify an error at the given line and
56 // character.
DoParserErrorTest(const char * input,int err_line,int err_char)57 void DoParserErrorTest(const char* input, int err_line, int err_char) {
58 InputFile input_file(SourceFile("/test"));
59 input_file.SetContents(input);
60
61 Err err;
62 std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, &err);
63 if (!err.has_error()) {
64 std::unique_ptr<ParseNode> result = Parser::Parse(tokens, &err);
65 ASSERT_FALSE(result);
66 ASSERT_TRUE(err.has_error());
67 }
68
69 EXPECT_EQ(err_line, err.location().line_number());
70 EXPECT_EQ(err_char, err.location().column_number());
71 }
72
73 // Expects the tokenizer or parser to identify an error at the given line and
74 // character.
DoExpressionErrorTest(const char * input,int err_line,int err_char)75 void DoExpressionErrorTest(const char* input, int err_line, int err_char) {
76 InputFile input_file(SourceFile("/test"));
77 input_file.SetContents(input);
78
79 Err err;
80 std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, &err);
81 if (!err.has_error()) {
82 std::unique_ptr<ParseNode> result = Parser::ParseExpression(tokens, &err);
83 ASSERT_FALSE(result);
84 ASSERT_TRUE(err.has_error());
85 }
86
87 EXPECT_EQ(err_line, err.location().line_number());
88 EXPECT_EQ(err_char, err.location().column_number());
89 }
90
91 } // namespace
92
TEST(Parser,Literal)93 TEST(Parser, Literal) {
94 DoExpressionPrintTest("5", "LITERAL(5)\n");
95 DoExpressionPrintTest("\"stuff\"", "LITERAL(\"stuff\")\n");
96 }
97
TEST(Parser,BinaryOp)98 TEST(Parser, BinaryOp) {
99 // TODO(scottmg): The tokenizer is dumb, and treats "5-1" as two integers,
100 // not a binary operator between two positive integers.
101 DoExpressionPrintTest("5 - 1",
102 "BINARY(-)\n"
103 " LITERAL(5)\n"
104 " LITERAL(1)\n");
105 DoExpressionPrintTest("5+1",
106 "BINARY(+)\n"
107 " LITERAL(5)\n"
108 " LITERAL(1)\n");
109 DoExpressionPrintTest("5 - 1 - 2",
110 "BINARY(-)\n"
111 " BINARY(-)\n"
112 " LITERAL(5)\n"
113 " LITERAL(1)\n"
114 " LITERAL(2)\n");
115 }
116
TEST(Parser,FunctionCall)117 TEST(Parser, FunctionCall) {
118 DoExpressionPrintTest("foo()",
119 "FUNCTION(foo)\n"
120 " LIST\n");
121 DoExpressionPrintTest("blah(1, 2)",
122 "FUNCTION(blah)\n"
123 " LIST\n"
124 " LITERAL(1)\n"
125 " LITERAL(2)\n");
126 DoExpressionErrorTest("foo(1, 2,)", 1, 10);
127 DoExpressionErrorTest("foo(1 2)", 1, 7);
128 }
129
TEST(Parser,ParenExpression)130 TEST(Parser, ParenExpression) {
131 const char* input = "(foo(1)) + (a + (b - c) + d)";
132 const char* expected =
133 "BINARY(+)\n"
134 " FUNCTION(foo)\n"
135 " LIST\n"
136 " LITERAL(1)\n"
137 " BINARY(+)\n"
138 " BINARY(+)\n"
139 " IDENTIFIER(a)\n"
140 " BINARY(-)\n"
141 " IDENTIFIER(b)\n"
142 " IDENTIFIER(c)\n"
143 " IDENTIFIER(d)\n";
144 DoExpressionPrintTest(input, expected);
145 DoExpressionErrorTest("(a +", 1, 4);
146 }
147
TEST(Parser,OrderOfOperationsLeftAssociative)148 TEST(Parser, OrderOfOperationsLeftAssociative) {
149 const char* input = "5 - 1 - 2\n";
150 const char* expected =
151 "BINARY(-)\n"
152 " BINARY(-)\n"
153 " LITERAL(5)\n"
154 " LITERAL(1)\n"
155 " LITERAL(2)\n";
156 DoExpressionPrintTest(input, expected);
157 }
158
TEST(Parser,OrderOfOperationsEqualityBoolean)159 TEST(Parser, OrderOfOperationsEqualityBoolean) {
160 const char* input =
161 "if (a == \"b\" && is_stuff) {\n"
162 " print(\"hai\")\n"
163 "}\n";
164 const char* expected =
165 "BLOCK\n"
166 " CONDITION\n"
167 " BINARY(&&)\n"
168 " BINARY(==)\n"
169 " IDENTIFIER(a)\n"
170 " LITERAL(\"b\")\n"
171 " IDENTIFIER(is_stuff)\n"
172 " BLOCK\n"
173 " FUNCTION(print)\n"
174 " LIST\n"
175 " LITERAL(\"hai\")\n";
176 DoParserPrintTest(input, expected);
177 }
178
TEST(Parser,UnaryOp)179 TEST(Parser, UnaryOp) {
180 DoExpressionPrintTest("!foo",
181 "UNARY(!)\n"
182 " IDENTIFIER(foo)\n");
183
184 // No contents for binary operator.
185 DoExpressionErrorTest("a = !", 1, 5);
186 }
187
TEST(Parser,List)188 TEST(Parser, List) {
189 DoExpressionPrintTest("[]", "LIST\n");
190 DoExpressionPrintTest("[1,asd,]",
191 "LIST\n"
192 " LITERAL(1)\n"
193 " IDENTIFIER(asd)\n");
194 DoExpressionPrintTest("[1, 2+3 - foo]",
195 "LIST\n"
196 " LITERAL(1)\n"
197 " BINARY(-)\n"
198 " BINARY(+)\n"
199 " LITERAL(2)\n"
200 " LITERAL(3)\n"
201 " IDENTIFIER(foo)\n");
202 DoExpressionPrintTest("[1,\n2,\n 3,\n 4]",
203 "LIST\n"
204 " LITERAL(1)\n"
205 " LITERAL(2)\n"
206 " LITERAL(3)\n"
207 " LITERAL(4)\n");
208
209 DoExpressionErrorTest("[a, 2+,]", 1, 7);
210 DoExpressionErrorTest("[,]", 1, 2);
211 DoExpressionErrorTest("[a,,]", 1, 4);
212 }
213
TEST(Parser,Assignment)214 TEST(Parser, Assignment) {
215 DoParserPrintTest("a=2",
216 "BLOCK\n"
217 " BINARY(=)\n"
218 " IDENTIFIER(a)\n"
219 " LITERAL(2)\n");
220
221 DoExpressionErrorTest("a = ", 1, 3);
222 }
223
TEST(Parser,Accessor)224 TEST(Parser, Accessor) {
225 // Accessor indexing.
226 DoParserPrintTest("a=b[c+2]",
227 "BLOCK\n"
228 " BINARY(=)\n"
229 " IDENTIFIER(a)\n"
230 " ACCESSOR\n"
231 " b\n" // AccessorNode is a bit weird in that it holds
232 // a Token, not a ParseNode for the base.
233 " BINARY(+)\n"
234 " IDENTIFIER(c)\n"
235 " LITERAL(2)\n");
236 DoParserErrorTest("a = b[1][0]", 1, 5);
237
238 // Member accessors.
239 DoParserPrintTest("a=b.c+2",
240 "BLOCK\n"
241 " BINARY(=)\n"
242 " IDENTIFIER(a)\n"
243 " BINARY(+)\n"
244 " ACCESSOR\n"
245 " b\n"
246 " IDENTIFIER(c)\n"
247 " LITERAL(2)\n");
248 DoParserPrintTest("a.b = 5",
249 "BLOCK\n"
250 " BINARY(=)\n"
251 " ACCESSOR\n"
252 " a\n"
253 " IDENTIFIER(b)\n"
254 " LITERAL(5)\n");
255 DoParserErrorTest("a = b.c.d", 1, 6); // Can't nest accessors (currently).
256
257 // Error at the bad dot in the RHS, not the + operator (crbug.com/472038).
258 DoParserErrorTest("foo(a + b.c.d)", 1, 10);
259 }
260
TEST(Parser,Condition)261 TEST(Parser, Condition) {
262 DoParserPrintTest("if(1) { a = 2 }",
263 "BLOCK\n"
264 " CONDITION\n"
265 " LITERAL(1)\n"
266 " BLOCK\n"
267 " BINARY(=)\n"
268 " IDENTIFIER(a)\n"
269 " LITERAL(2)\n");
270
271 DoParserPrintTest("if(1) { a = 2 } else if (0) { a = 3 } else { a = 4 }",
272 "BLOCK\n"
273 " CONDITION\n"
274 " LITERAL(1)\n"
275 " BLOCK\n"
276 " BINARY(=)\n"
277 " IDENTIFIER(a)\n"
278 " LITERAL(2)\n"
279 " CONDITION\n"
280 " LITERAL(0)\n"
281 " BLOCK\n"
282 " BINARY(=)\n"
283 " IDENTIFIER(a)\n"
284 " LITERAL(3)\n"
285 " BLOCK\n"
286 " BINARY(=)\n"
287 " IDENTIFIER(a)\n"
288 " LITERAL(4)\n");
289 }
290
TEST(Parser,OnlyCallAndAssignInBody)291 TEST(Parser, OnlyCallAndAssignInBody) {
292 DoParserErrorTest("[]", 1, 2);
293 DoParserErrorTest("3 + 4", 1, 5);
294 DoParserErrorTest("6 - 7", 1, 5);
295 DoParserErrorTest("if (1) { 5 } else { print(4) }", 1, 12);
296 }
297
TEST(Parser,NoAssignmentInCondition)298 TEST(Parser, NoAssignmentInCondition) {
299 DoParserErrorTest("if (a=2) {}", 1, 5);
300 }
301
TEST(Parser,CompleteFunction)302 TEST(Parser, CompleteFunction) {
303 const char* input =
304 "cc_test(\"foo\") {\n"
305 " sources = [\n"
306 " \"foo.cc\",\n"
307 " \"foo.h\"\n"
308 " ]\n"
309 " dependencies = [\n"
310 " \"base\"\n"
311 " ]\n"
312 "}\n";
313 const char* expected =
314 "BLOCK\n"
315 " FUNCTION(cc_test)\n"
316 " LIST\n"
317 " LITERAL(\"foo\")\n"
318 " BLOCK\n"
319 " BINARY(=)\n"
320 " IDENTIFIER(sources)\n"
321 " LIST\n"
322 " LITERAL(\"foo.cc\")\n"
323 " LITERAL(\"foo.h\")\n"
324 " BINARY(=)\n"
325 " IDENTIFIER(dependencies)\n"
326 " LIST\n"
327 " LITERAL(\"base\")\n";
328 DoParserPrintTest(input, expected);
329 }
330
TEST(Parser,FunctionWithConditional)331 TEST(Parser, FunctionWithConditional) {
332 const char* input =
333 "cc_test(\"foo\") {\n"
334 " sources = [\"foo.cc\"]\n"
335 " if (OS == \"mac\") {\n"
336 " sources += \"bar.cc\"\n"
337 " } else if (OS == \"win\") {\n"
338 " sources -= [\"asd.cc\", \"foo.cc\"]\n"
339 " } else {\n"
340 " dependencies += [\"bar.cc\"]\n"
341 " }\n"
342 "}\n";
343 const char* expected =
344 "BLOCK\n"
345 " FUNCTION(cc_test)\n"
346 " LIST\n"
347 " LITERAL(\"foo\")\n"
348 " BLOCK\n"
349 " BINARY(=)\n"
350 " IDENTIFIER(sources)\n"
351 " LIST\n"
352 " LITERAL(\"foo.cc\")\n"
353 " CONDITION\n"
354 " BINARY(==)\n"
355 " IDENTIFIER(OS)\n"
356 " LITERAL(\"mac\")\n"
357 " BLOCK\n"
358 " BINARY(+=)\n"
359 " IDENTIFIER(sources)\n"
360 " LITERAL(\"bar.cc\")\n"
361 " CONDITION\n"
362 " BINARY(==)\n"
363 " IDENTIFIER(OS)\n"
364 " LITERAL(\"win\")\n"
365 " BLOCK\n"
366 " BINARY(-=)\n"
367 " IDENTIFIER(sources)\n"
368 " LIST\n"
369 " LITERAL(\"asd.cc\")\n"
370 " LITERAL(\"foo.cc\")\n"
371 " BLOCK\n"
372 " BINARY(+=)\n"
373 " IDENTIFIER(dependencies)\n"
374 " LIST\n"
375 " LITERAL(\"bar.cc\")\n";
376 DoParserPrintTest(input, expected);
377 }
378
TEST(Parser,UnterminatedBlock)379 TEST(Parser, UnterminatedBlock) {
380 DoParserErrorTest("stuff() {", 1, 9);
381 }
382
TEST(Parser,BadlyTerminatedNumber)383 TEST(Parser, BadlyTerminatedNumber) {
384 DoParserErrorTest("1234z", 1, 5);
385 }
386
TEST(Parser,NewlinesInUnusualPlaces)387 TEST(Parser, NewlinesInUnusualPlaces) {
388 DoParserPrintTest(
389 "if\n"
390 "(\n"
391 "a\n"
392 ")\n"
393 "{\n"
394 "}\n",
395 "BLOCK\n"
396 " CONDITION\n"
397 " IDENTIFIER(a)\n"
398 " BLOCK\n");
399 }
400
TEST(Parser,NewlinesInUnusualPlaces2)401 TEST(Parser, NewlinesInUnusualPlaces2) {
402 DoParserPrintTest("a\n=\n2\n",
403 "BLOCK\n"
404 " BINARY(=)\n"
405 " IDENTIFIER(a)\n"
406 " LITERAL(2)\n");
407 DoParserPrintTest("x =\ny if\n(1\n) {}",
408 "BLOCK\n"
409 " BINARY(=)\n"
410 " IDENTIFIER(x)\n"
411 " IDENTIFIER(y)\n"
412 " CONDITION\n"
413 " LITERAL(1)\n"
414 " BLOCK\n");
415 DoParserPrintTest("x = 3\n+2",
416 "BLOCK\n"
417 " BINARY(=)\n"
418 " IDENTIFIER(x)\n"
419 " BINARY(+)\n"
420 " LITERAL(3)\n"
421 " LITERAL(2)\n");
422 }
423
TEST(Parser,NewlineBeforeSubscript)424 TEST(Parser, NewlineBeforeSubscript) {
425 const char* input = "a = b[1]";
426 const char* input_with_newline = "a = b\n[1]";
427 const char* expected =
428 "BLOCK\n"
429 " BINARY(=)\n"
430 " IDENTIFIER(a)\n"
431 " ACCESSOR\n"
432 " b\n"
433 " LITERAL(1)\n";
434 DoParserPrintTest(input, expected);
435 DoParserPrintTest(input_with_newline, expected);
436 }
437
TEST(Parser,SequenceOfExpressions)438 TEST(Parser, SequenceOfExpressions) {
439 DoParserPrintTest("a = 1 b = 2",
440 "BLOCK\n"
441 " BINARY(=)\n"
442 " IDENTIFIER(a)\n"
443 " LITERAL(1)\n"
444 " BINARY(=)\n"
445 " IDENTIFIER(b)\n"
446 " LITERAL(2)\n");
447 }
448
TEST(Parser,BlockAfterFunction)449 TEST(Parser, BlockAfterFunction) {
450 const char* input = "func(\"stuff\") {\n}";
451 // TODO(scottmg): Do we really want these to mean different things?
452 const char* input_with_newline = "func(\"stuff\")\n{\n}";
453 const char* expected =
454 "BLOCK\n"
455 " FUNCTION(func)\n"
456 " LIST\n"
457 " LITERAL(\"stuff\")\n"
458 " BLOCK\n";
459 DoParserPrintTest(input, expected);
460 DoParserPrintTest(input_with_newline, expected);
461 }
462
TEST(Parser,LongExpression)463 TEST(Parser, LongExpression) {
464 const char* input = "a = b + c && d || e";
465 const char* expected =
466 "BLOCK\n"
467 " BINARY(=)\n"
468 " IDENTIFIER(a)\n"
469 " BINARY(||)\n"
470 " BINARY(&&)\n"
471 " BINARY(+)\n"
472 " IDENTIFIER(b)\n"
473 " IDENTIFIER(c)\n"
474 " IDENTIFIER(d)\n"
475 " IDENTIFIER(e)\n";
476 DoParserPrintTest(input, expected);
477 }
478
TEST(Parser,CommentsStandalone)479 TEST(Parser, CommentsStandalone) {
480 const char* input =
481 "# Toplevel comment.\n"
482 "\n"
483 "executable(\"wee\") {}\n";
484 const char* expected =
485 "BLOCK\n"
486 " BLOCK_COMMENT(# Toplevel comment.)\n"
487 " FUNCTION(executable)\n"
488 " LIST\n"
489 " LITERAL(\"wee\")\n"
490 " BLOCK\n";
491 DoParserPrintTest(input, expected);
492 }
493
TEST(Parser,CommentsStandaloneEof)494 TEST(Parser, CommentsStandaloneEof) {
495 const char* input =
496 "executable(\"wee\") {}\n"
497 "# EOF comment.\n";
498 const char* expected =
499 "BLOCK\n"
500 " +AFTER_COMMENT(\"# EOF comment.\")\n"
501 " FUNCTION(executable)\n"
502 " LIST\n"
503 " LITERAL(\"wee\")\n"
504 " BLOCK\n";
505 DoParserPrintTest(input, expected);
506 }
507
TEST(Parser,CommentsLineAttached)508 TEST(Parser, CommentsLineAttached) {
509 const char* input =
510 "executable(\"wee\") {\n"
511 " # Some sources.\n"
512 " sources = [\n"
513 " \"stuff.cc\",\n"
514 " \"things.cc\",\n"
515 " # This file is special or something.\n"
516 " \"another.cc\",\n"
517 " ]\n"
518 "}\n";
519 const char* expected =
520 "BLOCK\n"
521 " FUNCTION(executable)\n"
522 " LIST\n"
523 " LITERAL(\"wee\")\n"
524 " BLOCK\n"
525 " BINARY(=)\n"
526 " +BEFORE_COMMENT(\"# Some sources.\")\n"
527 " IDENTIFIER(sources)\n"
528 " LIST\n"
529 " LITERAL(\"stuff.cc\")\n"
530 " LITERAL(\"things.cc\")\n"
531 " LITERAL(\"another.cc\")\n"
532 " +BEFORE_COMMENT(\"# This file is special or something.\")\n";
533 DoParserPrintTest(input, expected);
534 }
535
TEST(Parser,CommentsSuffix)536 TEST(Parser, CommentsSuffix) {
537 const char* input =
538 "executable(\"wee\") { # This is some stuff.\n"
539 "sources = [ \"a.cc\" # And another comment here.\n"
540 "] }";
541 const char* expected =
542 "BLOCK\n"
543 " FUNCTION(executable)\n"
544 " LIST\n"
545 " LITERAL(\"wee\")\n"
546 " END())\n"
547 " +SUFFIX_COMMENT(\"# This is some stuff.\")\n"
548 " BLOCK\n"
549 " BINARY(=)\n"
550 " IDENTIFIER(sources)\n"
551 " LIST\n"
552 " LITERAL(\"a.cc\")\n"
553 " +SUFFIX_COMMENT(\"# And another comment here.\")\n";
554 DoParserPrintTest(input, expected);
555 }
556
TEST(Parser,CommentsSuffixDifferentLine)557 TEST(Parser, CommentsSuffixDifferentLine) {
558 const char* input =
559 "executable(\"wee\") {\n"
560 " sources = [ \"a\",\n"
561 " \"b\" ] # Comment\n"
562 "}\n";
563 const char* expected =
564 "BLOCK\n"
565 " FUNCTION(executable)\n"
566 " LIST\n"
567 " LITERAL(\"wee\")\n"
568 " BLOCK\n"
569 " BINARY(=)\n"
570 " IDENTIFIER(sources)\n"
571 " LIST\n"
572 " LITERAL(\"a\")\n"
573 " LITERAL(\"b\")\n"
574 " END(])\n"
575 " +SUFFIX_COMMENT(\"# Comment\")\n";
576 DoParserPrintTest(input, expected);
577 }
578
TEST(Parser,CommentsSuffixMultiple)579 TEST(Parser, CommentsSuffixMultiple) {
580 const char* input =
581 "executable(\"wee\") {\n"
582 " sources = [\n"
583 " \"a\", # This is a comment,\n"
584 " # and some more,\n" // Note that this is aligned with above.
585 " # then the end.\n"
586 " ]\n"
587 "}\n";
588 const char* expected =
589 "BLOCK\n"
590 " FUNCTION(executable)\n"
591 " LIST\n"
592 " LITERAL(\"wee\")\n"
593 " BLOCK\n"
594 " BINARY(=)\n"
595 " IDENTIFIER(sources)\n"
596 " LIST\n"
597 " LITERAL(\"a\")\n"
598 " +SUFFIX_COMMENT(\"# This is a comment,\")\n"
599 " +SUFFIX_COMMENT(\"# and some more,\")\n"
600 " +SUFFIX_COMMENT(\"# then the end.\")\n";
601 DoParserPrintTest(input, expected);
602 }
603
TEST(Parser,CommentsConnectedInList)604 TEST(Parser, CommentsConnectedInList) {
605 const char* input =
606 "defines = [\n"
607 "\n"
608 " # Connected comment.\n"
609 " \"WEE\",\n"
610 " \"BLORPY\",\n"
611 "]\n";
612 const char* expected =
613 "BLOCK\n"
614 " BINARY(=)\n"
615 " IDENTIFIER(defines)\n"
616 " LIST\n"
617 " LITERAL(\"WEE\")\n"
618 " +BEFORE_COMMENT(\"# Connected comment.\")\n"
619 " LITERAL(\"BLORPY\")\n";
620 DoParserPrintTest(input, expected);
621 }
622
TEST(Parser,CommentsAtEndOfBlock)623 TEST(Parser, CommentsAtEndOfBlock) {
624 const char* input =
625 "if (is_win) {\n"
626 " sources = [\"a.cc\"]\n"
627 " # Some comment at end.\n"
628 "}\n";
629 const char* expected =
630 "BLOCK\n"
631 " CONDITION\n"
632 " IDENTIFIER(is_win)\n"
633 " BLOCK\n"
634 " BINARY(=)\n"
635 " IDENTIFIER(sources)\n"
636 " LIST\n"
637 " LITERAL(\"a.cc\")\n"
638 " END(})\n"
639 " +BEFORE_COMMENT(\"# Some comment at end.\")\n";
640 DoParserPrintTest(input, expected);
641 }
642
643 // TODO(scottmg): I could be convinced this is incorrect. It's not clear to me
644 // which thing this comment is intended to be attached to.
TEST(Parser,CommentsEndOfBlockSingleLine)645 TEST(Parser, CommentsEndOfBlockSingleLine) {
646 const char* input =
647 "defines = [ # EOL defines.\n"
648 "]\n";
649 const char* expected =
650 "BLOCK\n"
651 " BINARY(=)\n"
652 " IDENTIFIER(defines)\n"
653 " +SUFFIX_COMMENT(\"# EOL defines.\")\n"
654 " LIST\n";
655 DoParserPrintTest(input, expected);
656 }
657
TEST(Parser,HangingIf)658 TEST(Parser, HangingIf) {
659 DoParserErrorTest("if", 1, 1);
660 }
661
TEST(Parser,NegatingList)662 TEST(Parser, NegatingList) {
663 DoParserErrorTest("executable(\"wee\") { sources =- [ \"foo.cc\" ] }", 1, 30);
664 }
665
TEST(Parser,ConditionNoBracesIf)666 TEST(Parser, ConditionNoBracesIf) {
667 DoParserErrorTest(
668 "if (true)\n"
669 " foreach(foo, []) {}\n"
670 "else {\n"
671 " foreach(bar, []) {}\n"
672 "}\n",
673 2, 3);
674 }
675
TEST(Parser,ConditionNoBracesElse)676 TEST(Parser, ConditionNoBracesElse) {
677 DoParserErrorTest(
678 "if (true) {\n"
679 " foreach(foo, []) {}\n"
680 "} else\n"
681 " foreach(bar, []) {}\n",
682 4, 3);
683 }
684
TEST(Parser,ConditionNoBracesElseIf)685 TEST(Parser, ConditionNoBracesElseIf) {
686 DoParserErrorTest(
687 "if (true) {\n"
688 " foreach(foo, []) {}\n"
689 "} else if (true)\n"
690 " foreach(bar, []) {}\n",
691 4, 3);
692 }
693
694 // Disallow standalone {} for introducing new scopes. These are ambiguous with
695 // target declarations (e.g. is:
696 // foo("bar") {}
697 // a function with an associated block, or a standalone function with a
698 // freestanding block.
TEST(Parser,StandaloneBlock)699 TEST(Parser, StandaloneBlock) {
700 // The error is reported at the end of the block when nothing is done
701 // with it. If we had said "a = { ..." then it would have been OK.
702 DoParserErrorTest(
703 "if (true) {\n"
704 "}\n"
705 "{\n"
706 " assert(false)\n"
707 "}\n",
708 5, 1);
709 }
710
TEST(Parser,BlockValues)711 TEST(Parser, BlockValues) {
712 const char* input =
713 "print({a = 1 b = 2}, 3)\n"
714 "a = { b = \"asd\" }";
715 const char* expected =
716 "BLOCK\n"
717 " FUNCTION(print)\n"
718 " LIST\n"
719 " BLOCK\n"
720 " BINARY(=)\n"
721 " IDENTIFIER(a)\n"
722 " LITERAL(1)\n"
723 " BINARY(=)\n"
724 " IDENTIFIER(b)\n"
725 " LITERAL(2)\n"
726 " LITERAL(3)\n"
727 " BINARY(=)\n"
728 " IDENTIFIER(a)\n"
729 " BLOCK\n"
730 " BINARY(=)\n"
731 " IDENTIFIER(b)\n"
732 " LITERAL(\"asd\")\n";
733 DoParserPrintTest(input, expected);
734 }
735