1 /*
2 This file is part of solidity.
3
4 solidity is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 solidity is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with solidity. If not, see <http://www.gnu.org/licenses/>.
16 */
17 /**
18 * @author Christian <c@ethdev.com>
19 * @date 2014
20 * Unit tests for the solidity parser.
21 */
22
23 #include <string>
24 #include <memory>
25 #include <liblangutil/Scanner.h>
26 #include <libsolidity/parsing/Parser.h>
27 #include <liblangutil/ErrorReporter.h>
28 #include <test/Common.h>
29 #include <test/libsolidity/ErrorCheck.h>
30 #include <libsolidity/ast/ASTVisitor.h>
31
32 #include <boost/test/unit_test.hpp>
33
34 using namespace std;
35 using namespace solidity::langutil;
36
37 namespace solidity::frontend::test
38 {
39
40 namespace
41 {
parseText(std::string const & _source,ErrorList & _errors,bool errorRecovery=false)42 ASTPointer<ContractDefinition> parseText(std::string const& _source, ErrorList& _errors, bool errorRecovery = false)
43 {
44 ErrorReporter errorReporter(_errors);
45 auto charStream = CharStream(_source, "");
46 ASTPointer<SourceUnit> sourceUnit = Parser(
47 errorReporter,
48 solidity::test::CommonOptions::get().evmVersion(),
49 errorRecovery
50 ).parse(charStream);
51 if (!sourceUnit)
52 return ASTPointer<ContractDefinition>();
53 for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
54 if (ASTPointer<ContractDefinition> contract = dynamic_pointer_cast<ContractDefinition>(node))
55 return contract;
56 BOOST_FAIL("No contract found in source.");
57 return ASTPointer<ContractDefinition>();
58 }
59
successParse(std::string const & _source)60 bool successParse(std::string const& _source)
61 {
62 ErrorList errors;
63 try
64 {
65 auto sourceUnit = parseText(_source, errors);
66 if (!sourceUnit)
67 return false;
68 }
69 catch (FatalError const& /*_exception*/)
70 {
71 if (Error::containsErrorOfType(errors, Error::Type::ParserError))
72 return false;
73 }
74 if (Error::containsErrorOfType(errors, Error::Type::ParserError))
75 return false;
76
77 BOOST_CHECK(!Error::containsErrors(errors));
78 return true;
79 }
80
getError(std::string const & _source,bool errorRecovery=false)81 Error getError(std::string const& _source, bool errorRecovery = false)
82 {
83 ErrorList errors;
84 try
85 {
86 parseText(_source, errors, errorRecovery);
87 }
88 catch (FatalError const& /*_exception*/)
89 {
90 // no-op
91 }
92 Error const* error = Error::containsErrorOfType(errors, Error::Type::ParserError);
93 BOOST_REQUIRE(error);
94 return *error;
95 }
96
checkFunctionNatspec(FunctionDefinition const * _function,std::string const & _expectedDoc)97 void checkFunctionNatspec(
98 FunctionDefinition const* _function,
99 std::string const& _expectedDoc
100 )
101 {
102 auto doc = _function->documentation()->text();
103 BOOST_CHECK_MESSAGE(doc != nullptr, "Function does not have Natspec Doc as expected");
104 BOOST_CHECK_EQUAL(*doc, _expectedDoc);
105 }
106
107 }
108
109 #define CHECK_PARSE_ERROR(source, substring) \
110 do \
111 {\
112 Error err = getError((source)); \
113 BOOST_CHECK(searchErrorMessage(err, (substring))); \
114 }\
115 while(0)
116
117
118 BOOST_AUTO_TEST_SUITE(SolidityParser)
119
BOOST_AUTO_TEST_CASE(reserved_keywords)120 BOOST_AUTO_TEST_CASE(reserved_keywords)
121 {
122 BOOST_CHECK(!TokenTraits::isReservedKeyword(Token::Identifier));
123 BOOST_CHECK(TokenTraits::isReservedKeyword(Token::After));
124 BOOST_CHECK(!TokenTraits::isReservedKeyword(Token::Unchecked));
125 BOOST_CHECK(TokenTraits::isReservedKeyword(Token::Var));
126 BOOST_CHECK(TokenTraits::isReservedKeyword(Token::Reference));
127 BOOST_CHECK(!TokenTraits::isReservedKeyword(Token::Illegal));
128 }
129
BOOST_AUTO_TEST_CASE(function_natspec_documentation)130 BOOST_AUTO_TEST_CASE(function_natspec_documentation)
131 {
132 char const* text = R"(
133 contract test {
134 uint256 stateVar;
135 /// This is a test function
136 function functionName(bytes32 input) public returns (bytes32 out) {}
137 }
138 )";
139 BOOST_CHECK(successParse(text));
140 ErrorList errors;
141 ASTPointer<ContractDefinition> contract = parseText(text, errors);
142 FunctionDefinition const* function = nullptr;
143 auto functions = contract->definedFunctions();
144
145 BOOST_REQUIRE_MESSAGE(function = functions.at(0), "Failed to retrieve function");
146 checkFunctionNatspec(function, "This is a test function");
147 }
148
BOOST_AUTO_TEST_CASE(function_normal_comments)149 BOOST_AUTO_TEST_CASE(function_normal_comments)
150 {
151 FunctionDefinition const* function = nullptr;
152 char const* text = R"(
153 contract test {
154 uint256 stateVar;
155 // We won't see this comment
156 function functionName(bytes32 input) public returns (bytes32 out) {}
157 }
158 )";
159 BOOST_CHECK(successParse(text));
160 ErrorList errors;
161 ASTPointer<ContractDefinition> contract = parseText(text, errors);
162 auto functions = contract->definedFunctions();
163 BOOST_REQUIRE_MESSAGE(function = functions.at(0), "Failed to retrieve function");
164 BOOST_CHECK_MESSAGE(function->documentation() == nullptr,
165 "Should not have gotten a Natspecc comment for this function");
166 }
167
BOOST_AUTO_TEST_CASE(multiple_functions_natspec_documentation)168 BOOST_AUTO_TEST_CASE(multiple_functions_natspec_documentation)
169 {
170 FunctionDefinition const* function = nullptr;
171 char const* text = R"(
172 contract test {
173 uint256 stateVar;
174 /// This is test function 1
175 function functionName1(bytes32 input) public returns (bytes32 out) {}
176 /// This is test function 2
177 function functionName2(bytes32 input) public returns (bytes32 out) {}
178 // nothing to see here
179 function functionName3(bytes32 input) public returns (bytes32 out) {}
180 /// This is test function 4
181 function functionName4(bytes32 input) public returns (bytes32 out) {}
182 }
183 )";
184 BOOST_CHECK(successParse(text));
185 ErrorList errors;
186 ASTPointer<ContractDefinition> contract = parseText(text, errors);
187 auto functions = contract->definedFunctions();
188
189 BOOST_REQUIRE_MESSAGE(function = functions.at(0), "Failed to retrieve function");
190 checkFunctionNatspec(function, "This is test function 1");
191
192 BOOST_REQUIRE_MESSAGE(function = functions.at(1), "Failed to retrieve function");
193 checkFunctionNatspec(function, "This is test function 2");
194
195 BOOST_REQUIRE_MESSAGE(function = functions.at(2), "Failed to retrieve function");
196 BOOST_CHECK_MESSAGE(function->documentation() == nullptr,
197 "Should not have gotten natspec comment for functionName3()");
198
199 BOOST_REQUIRE_MESSAGE(function = functions.at(3), "Failed to retrieve function");
200 checkFunctionNatspec(function, "This is test function 4");
201 }
202
BOOST_AUTO_TEST_CASE(multiline_function_documentation)203 BOOST_AUTO_TEST_CASE(multiline_function_documentation)
204 {
205 FunctionDefinition const* function = nullptr;
206 char const* text = R"(
207 contract test {
208 uint256 stateVar;
209 /// This is a test function
210 /// and it has 2 lines
211 function functionName1(bytes32 input) public returns (bytes32 out) {}
212 }
213 )";
214 BOOST_CHECK(successParse(text));
215 ErrorList errors;
216 ASTPointer<ContractDefinition> contract = parseText(text, errors);
217 auto functions = contract->definedFunctions();
218 BOOST_REQUIRE_MESSAGE(function = functions.at(0), "Failed to retrieve function");
219 checkFunctionNatspec(function, "This is a test function\n"
220 " and it has 2 lines");
221 }
222
BOOST_AUTO_TEST_CASE(natspec_comment_in_function_body)223 BOOST_AUTO_TEST_CASE(natspec_comment_in_function_body)
224 {
225 FunctionDefinition const* function = nullptr;
226 char const* text = R"(
227 contract test {
228 /// fun1 description
229 function fun1(uint256 a) {
230 uint b;
231 // I should not interfere with actual natspec comments (natspec comments on local variables not allowed anymore)
232 uint256 c;
233 mapping(address=>bytes32) d;
234 bytes7 name = "Solidity";
235 }
236 /// This is a test function
237 /// and it has 2 lines
238 function fun(bytes32 input) public returns (bytes32 out) {}
239 }
240 )";
241 BOOST_CHECK(successParse(text));
242 ErrorList errors;
243 ASTPointer<ContractDefinition> contract = parseText(text, errors);
244 auto functions = contract->definedFunctions();
245
246 BOOST_REQUIRE_MESSAGE(function = functions.at(0), "Failed to retrieve function");
247 checkFunctionNatspec(function, "fun1 description");
248
249 BOOST_REQUIRE_MESSAGE(function = functions.at(1), "Failed to retrieve function");
250 checkFunctionNatspec(function, "This is a test function\n"
251 " and it has 2 lines");
252 }
253
BOOST_AUTO_TEST_CASE(natspec_docstring_between_keyword_and_signature)254 BOOST_AUTO_TEST_CASE(natspec_docstring_between_keyword_and_signature)
255 {
256 FunctionDefinition const* function = nullptr;
257 char const* text = R"(
258 contract test {
259 uint256 stateVar;
260 function ///I am in the wrong place
261 fun1(uint256 a) {
262 uint b;
263 // I should not interfere with actual natspec comments (natspec comments on local variables not allowed anymore)
264 uint256 c;
265 mapping(address=>bytes32) d;
266 bytes7 name = "Solidity";
267 }
268 }
269 )";
270 BOOST_CHECK(successParse(text));
271 ErrorList errors;
272 ASTPointer<ContractDefinition> contract = parseText(text, errors);
273 auto functions = contract->definedFunctions();
274
275 BOOST_REQUIRE_MESSAGE(function = functions.at(0), "Failed to retrieve function");
276 BOOST_CHECK_MESSAGE(!function->documentation(),
277 "Shouldn't get natspec docstring for this function");
278 }
279
BOOST_AUTO_TEST_CASE(natspec_docstring_after_signature)280 BOOST_AUTO_TEST_CASE(natspec_docstring_after_signature)
281 {
282 FunctionDefinition const* function = nullptr;
283 char const* text = R"(
284 contract test {
285 uint256 stateVar;
286 function fun1(uint256 a) {
287 // I should have been above the function signature (natspec comments on local variables not allowed anymore)
288 uint b;
289 // I should not interfere with actual natspec comments (natspec comments on local variables not allowed anymore)
290 uint256 c;
291 mapping(address=>bytes32) d;
292 bytes7 name = "Solidity";
293 }
294 }
295 )";
296 BOOST_CHECK(successParse(text));
297 ErrorList errors;
298 ASTPointer<ContractDefinition> contract = parseText(text, errors);
299 auto functions = contract->definedFunctions();
300
301 BOOST_REQUIRE_MESSAGE(function = functions.at(0), "Failed to retrieve function");
302 BOOST_CHECK_MESSAGE(!function->documentation(),
303 "Shouldn't get natspec docstring for this function");
304 }
305
BOOST_AUTO_TEST_CASE(variable_definition)306 BOOST_AUTO_TEST_CASE(variable_definition)
307 {
308 char const* text = R"(
309 contract test {
310 function fun(uint256 a) {
311 uint b;
312 uint256 c;
313 mapping(address=>bytes32) d;
314 customtype varname;
315 }
316 }
317 )";
318 BOOST_CHECK(successParse(text));
319 }
320
BOOST_AUTO_TEST_CASE(variable_definition_with_initialization)321 BOOST_AUTO_TEST_CASE(variable_definition_with_initialization)
322 {
323 char const* text = R"(
324 contract test {
325 function fun(uint256 a) {
326 uint b = 2;
327 uint256 c = 0x87;
328 mapping(address=>bytes32) d;
329 bytes7 name = "Solidity";
330 customtype varname;
331 }
332 }
333 )";
334 BOOST_CHECK(successParse(text));
335 }
336
BOOST_AUTO_TEST_CASE(operator_expression)337 BOOST_AUTO_TEST_CASE(operator_expression)
338 {
339 char const* text = R"(
340 contract test {
341 function fun(uint256 a) {
342 uint256 x = (1 + 4) || false && (1 - 12) + -9;
343 }
344 }
345 )";
346 BOOST_CHECK(successParse(text));
347 }
348
BOOST_AUTO_TEST_CASE(complex_expression)349 BOOST_AUTO_TEST_CASE(complex_expression)
350 {
351 char const* text = R"(
352 contract test {
353 function fun(uint256 a) {
354 uint256 x = (1 + 4).member(++67)[a/=9] || true;
355 }
356 }
357 )";
358 BOOST_CHECK(successParse(text));
359 }
360
BOOST_AUTO_TEST_CASE(statement_starting_with_type_conversion)361 BOOST_AUTO_TEST_CASE(statement_starting_with_type_conversion)
362 {
363 char const* text = R"(
364 contract test {
365 function fun() {
366 uint64(2);
367 uint64[7](3);
368 uint64[](3);
369 }
370 }
371 )";
372 BOOST_CHECK(successParse(text));
373 }
374
BOOST_AUTO_TEST_CASE(type_conversion_to_dynamic_array)375 BOOST_AUTO_TEST_CASE(type_conversion_to_dynamic_array)
376 {
377 char const* text = R"(
378 contract test {
379 function fun() {
380 uint x = uint64[](3);
381 }
382 }
383 )";
384 BOOST_CHECK(successParse(text));
385 }
386
BOOST_AUTO_TEST_CASE(import_directive)387 BOOST_AUTO_TEST_CASE(import_directive)
388 {
389 char const* text = R"(
390 import "abc";
391 contract test {
392 function fun() {
393 uint64(2);
394 }
395 }
396 )";
397 BOOST_CHECK(successParse(text));
398 }
399
BOOST_AUTO_TEST_CASE(multiple_contracts)400 BOOST_AUTO_TEST_CASE(multiple_contracts)
401 {
402 char const* text = R"(
403 contract test {
404 function fun() {
405 uint64(2);
406 }
407 }
408 contract test2 {
409 function fun() {
410 uint64(2);
411 }
412 }
413 )";
414 BOOST_CHECK(successParse(text));
415 }
416
BOOST_AUTO_TEST_CASE(multiple_contracts_and_imports)417 BOOST_AUTO_TEST_CASE(multiple_contracts_and_imports)
418 {
419 char const* text = R"(
420 import "abc";
421 contract test {
422 function fun() {
423 uint64(2);
424 }
425 }
426 import "def";
427 contract test2 {
428 function fun() {
429 uint64(2);
430 }
431 }
432 import "ghi";
433 )";
434 BOOST_CHECK(successParse(text));
435 }
436
BOOST_AUTO_TEST_CASE(contract_inheritance)437 BOOST_AUTO_TEST_CASE(contract_inheritance)
438 {
439 char const* text = R"(
440 contract base {
441 function fun() {
442 uint64(2);
443 }
444 }
445 contract derived is base {
446 function fun() {
447 uint64(2);
448 }
449 }
450 )";
451 BOOST_CHECK(successParse(text));
452 }
453
BOOST_AUTO_TEST_CASE(contract_multiple_inheritance)454 BOOST_AUTO_TEST_CASE(contract_multiple_inheritance)
455 {
456 char const* text = R"(
457 contract base {
458 function fun() {
459 uint64(2);
460 }
461 }
462 contract derived is base, nonExisting {
463 function fun() {
464 uint64(2);
465 }
466 }
467 )";
468 BOOST_CHECK(successParse(text));
469 }
470
BOOST_AUTO_TEST_CASE(contract_multiple_inheritance_with_arguments)471 BOOST_AUTO_TEST_CASE(contract_multiple_inheritance_with_arguments)
472 {
473 char const* text = R"(
474 contract base {
475 function fun() {
476 uint64(2);
477 }
478 }
479 contract derived is base(2), nonExisting("abc", "def", base.fun()) {
480 function fun() {
481 uint64(2);
482 }
483 }
484 )";
485 BOOST_CHECK(successParse(text));
486 }
487
BOOST_AUTO_TEST_CASE(keyword_is_reserved)488 BOOST_AUTO_TEST_CASE(keyword_is_reserved)
489 {
490 auto keywords = {
491 "after",
492 "alias",
493 "apply",
494 "auto",
495 "byte",
496 "case",
497 "copyof",
498 "default",
499 "define",
500 "final",
501 "implements",
502 "in",
503 "inline",
504 "let",
505 "macro",
506 "match",
507 "mutable",
508 "null",
509 "of",
510 "partial",
511 "promise",
512 "reference",
513 "relocatable",
514 "sealed",
515 "sizeof",
516 "static",
517 "supports",
518 "switch",
519 "typedef",
520 "typeof",
521 "var"
522 };
523
524 BOOST_CHECK_EQUAL(std::size(keywords), static_cast<int>(Token::Var) - static_cast<int>(Token::After) + 1);
525
526 for (auto const& keyword: keywords)
527 {
528 auto text = std::string("contract ") + keyword + " {}";
529 CHECK_PARSE_ERROR(text.c_str(), string("Expected identifier but got reserved keyword '") + keyword + "'");
530 }
531 }
532
BOOST_AUTO_TEST_CASE(member_access_parser_ambiguity)533 BOOST_AUTO_TEST_CASE(member_access_parser_ambiguity)
534 {
535 char const* text = R"(
536 contract C {
537 struct S { uint a; uint b; uint[][][] c; }
538 function f() {
539 C.S x;
540 C.S memory y;
541 C.S[10] memory z;
542 C.S[10](x);
543 x.a = 2;
544 x.c[1][2][3] = 9;
545 }
546 }
547 )";
548 BOOST_CHECK(successParse(text));
549 }
550
BOOST_AUTO_TEST_CASE(using_for)551 BOOST_AUTO_TEST_CASE(using_for)
552 {
553 char const* text = R"(
554 contract C {
555 struct s { uint a; }
556 using LibraryName for uint;
557 using Library2 for *;
558 using Lib for s;
559 function f() {
560 }
561 }
562 )";
563 BOOST_CHECK(successParse(text));
564 }
565
BOOST_AUTO_TEST_CASE(complex_import)566 BOOST_AUTO_TEST_CASE(complex_import)
567 {
568 char const* text = R"(
569 import "abc" as x;
570 import * as x from "abc";
571 import {a as b, c as d, f} from "def";
572 contract x {}
573 )";
574 BOOST_CHECK(successParse(text));
575 }
576
BOOST_AUTO_TEST_CASE(inline_asm_end_location)577 BOOST_AUTO_TEST_CASE(inline_asm_end_location)
578 {
579 auto sourceCode = std::string(R"(
580 contract C {
581 function f() public pure returns (uint y) {
582 uint a;
583 assembly { a := 0x12345678 }
584 uint z = a;
585 y = z;
586 }
587 }
588 )");
589 ErrorList errors;
590 auto contract = parseText(sourceCode, errors);
591
592 class CheckInlineAsmLocation: public ASTConstVisitor
593 {
594 public:
595 explicit CheckInlineAsmLocation(string _sourceCode): m_sourceCode(_sourceCode) {}
596 bool visited = false;
597 bool visit(InlineAssembly const& _inlineAsm) override
598 {
599 auto loc = _inlineAsm.location();
600 auto asmStr = m_sourceCode.substr(static_cast<size_t>(loc.start), static_cast<size_t>(loc.end - loc.start));
601 BOOST_CHECK_EQUAL(asmStr, "assembly { a := 0x12345678 }");
602 visited = true;
603
604 return false;
605 }
606 string m_sourceCode;
607 };
608
609 CheckInlineAsmLocation visitor{sourceCode};
610 contract->accept(visitor);
611
612 BOOST_CHECK_MESSAGE(visitor.visited, "No inline asm block found?!");
613 }
614
615 BOOST_AUTO_TEST_SUITE_END()
616
617 } // end namespaces
618