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 name and type resolution of the solidity parser.
21  */
22 
23 #include <test/libsolidity/AnalysisFramework.h>
24 
25 #include <test/Common.h>
26 
27 #include <libsolidity/ast/AST.h>
28 
29 #include <libsolutil/Keccak256.h>
30 
31 #include <boost/test/unit_test.hpp>
32 
33 #include <string>
34 
35 using namespace std;
36 using namespace solidity::langutil;
37 
38 namespace solidity::frontend::test
39 {
40 
BOOST_FIXTURE_TEST_SUITE(SolidityNameAndTypeResolution,AnalysisFramework)41 BOOST_FIXTURE_TEST_SUITE(SolidityNameAndTypeResolution, AnalysisFramework)
42 
43 BOOST_AUTO_TEST_CASE(function_no_implementation)
44 {
45 	SourceUnit const* sourceUnit = nullptr;
46 	char const* text = R"(
47 		abstract contract test {
48 			function functionName(bytes32 input) public virtual returns (bytes32 out);
49 		}
50 	)";
51 	sourceUnit = parseAndAnalyse(text);
52 	std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes();
53 	ContractDefinition* contract = dynamic_cast<ContractDefinition*>(nodes[1].get());
54 	BOOST_REQUIRE(contract);
55 	BOOST_CHECK(!contract->annotation().unimplementedDeclarations->empty());
56 	BOOST_CHECK(!contract->definedFunctions()[0]->isImplemented());
57 }
58 
BOOST_AUTO_TEST_CASE(abstract_contract)59 BOOST_AUTO_TEST_CASE(abstract_contract)
60 {
61 	SourceUnit const* sourceUnit = nullptr;
62 	char const* text = R"(
63 		abstract contract base { function foo() public virtual; }
64 		contract derived is base { function foo() public override {} }
65 	)";
66 	sourceUnit = parseAndAnalyse(text);
67 	std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes();
68 	ContractDefinition* base = dynamic_cast<ContractDefinition*>(nodes[1].get());
69 	ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get());
70 	BOOST_REQUIRE(base);
71 	BOOST_CHECK(!base->annotation().unimplementedDeclarations->empty());
72 	BOOST_CHECK(!base->definedFunctions()[0]->isImplemented());
73 	BOOST_REQUIRE(derived);
74 	BOOST_CHECK(derived->annotation().unimplementedDeclarations->empty());
75 	BOOST_CHECK(derived->definedFunctions()[0]->isImplemented());
76 }
77 
BOOST_AUTO_TEST_CASE(abstract_contract_with_overload)78 BOOST_AUTO_TEST_CASE(abstract_contract_with_overload)
79 {
80 	SourceUnit const* sourceUnit = nullptr;
81 	char const* text = R"(
82 		abstract contract base { function foo(bool) public virtual; }
83 		abstract contract derived is base { function foo(uint) public {} }
84 	)";
85 	sourceUnit = parseAndAnalyse(text);
86 	std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes();
87 	ContractDefinition* base = dynamic_cast<ContractDefinition*>(nodes[1].get());
88 	ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get());
89 	BOOST_REQUIRE(base);
90 	BOOST_CHECK(!base->annotation().unimplementedDeclarations->empty());
91 	BOOST_REQUIRE(derived);
92 	BOOST_CHECK(!derived->annotation().unimplementedDeclarations->empty());
93 }
94 
BOOST_AUTO_TEST_CASE(implement_abstract_via_constructor)95 BOOST_AUTO_TEST_CASE(implement_abstract_via_constructor)
96 {
97 	SourceUnit const* sourceUnit = nullptr;
98 	char const* text = R"(
99 		abstract contract base { function foo() public virtual; }
100 		abstract contract foo is base { constructor() {} }
101 	)";
102 	sourceUnit = parseAndAnalyse(text);
103 	std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes();
104 	BOOST_CHECK_EQUAL(nodes.size(), 3);
105 	ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get());
106 	BOOST_REQUIRE(derived);
107 	BOOST_CHECK(!derived->annotation().unimplementedDeclarations->empty());
108 }
109 
BOOST_AUTO_TEST_CASE(function_canonical_signature)110 BOOST_AUTO_TEST_CASE(function_canonical_signature)
111 {
112 	SourceUnit const* sourceUnit = nullptr;
113 	char const* text = R"(
114 		contract Test {
115 			function foo(uint256 arg1, uint64 arg2, bool arg3) public returns (uint256 ret) {
116 				ret = arg1 + arg2;
117 			}
118 		}
119 	)";
120 	sourceUnit = parseAndAnalyse(text);
121 	for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
122 		if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
123 		{
124 			auto functions = contract->definedFunctions();
125 			BOOST_CHECK_EQUAL("foo(uint256,uint64,bool)", functions[0]->externalSignature());
126 		}
127 }
128 
BOOST_AUTO_TEST_CASE(function_canonical_signature_type_aliases)129 BOOST_AUTO_TEST_CASE(function_canonical_signature_type_aliases)
130 {
131 	SourceUnit const* sourceUnit = nullptr;
132 	char const* text = R"(
133 		contract Test {
134 			function boo(uint, bytes32, address) public returns (uint ret) {
135 				ret = 5;
136 			}
137 		}
138 	)";
139 	sourceUnit = parseAndAnalyse(text);
140 	for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
141 		if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
142 		{
143 			auto functions = contract->definedFunctions();
144 			if (functions.empty())
145 				continue;
146 			BOOST_CHECK_EQUAL("boo(uint256,bytes32,address)", functions[0]->externalSignature());
147 		}
148 }
149 
BOOST_AUTO_TEST_CASE(function_external_types)150 BOOST_AUTO_TEST_CASE(function_external_types)
151 {
152 	SourceUnit const* sourceUnit = nullptr;
153 	char const* text = R"(
154 		contract C {
155 			uint a;
156 		}
157 		contract Test {
158 			function boo(uint, bool, bytes8, bool[2] calldata, uint[] calldata, C, address[] calldata) external returns (uint ret) {
159 				ret = 5;
160 			}
161 		}
162 	)";
163 	sourceUnit = parseAndAnalyse(text);
164 	for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
165 		if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
166 		{
167 			auto functions = contract->definedFunctions();
168 			if (functions.empty())
169 				continue;
170 			BOOST_CHECK_EQUAL("boo(uint256,bool,bytes8,bool[2],uint256[],address,address[])", functions[0]->externalSignature());
171 		}
172 }
173 
BOOST_AUTO_TEST_CASE(enum_external_type)174 BOOST_AUTO_TEST_CASE(enum_external_type)
175 {
176 	SourceUnit const* sourceUnit = nullptr;
177 	char const* text = R"(
178 		// test for bug #1801
179 		contract Test {
180 			enum ActionChoices { GoLeft, GoRight, GoStraight, Sit }
181 			function boo(ActionChoices enumArg) external returns (uint ret) {
182 				ret = 5;
183 			}
184 		}
185 	)";
186 	sourceUnit = parseAndAnalyse(text);
187 	for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
188 		if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
189 		{
190 			auto functions = contract->definedFunctions();
191 			if (functions.empty())
192 				continue;
193 			BOOST_CHECK_EQUAL("boo(uint8)", functions[0]->externalSignature());
194 		}
195 }
196 
BOOST_AUTO_TEST_CASE(external_struct_signatures)197 BOOST_AUTO_TEST_CASE(external_struct_signatures)
198 {
199 	char const* text = R"(
200 		pragma abicoder v2;
201 		contract Test {
202 			enum ActionChoices { GoLeft, GoRight, GoStraight, Sit }
203 			struct Simple { uint i; }
204 			struct Nested { X[2][] a; uint y; }
205 			struct X { bytes32 x; Test t; Simple[] s; }
206 			function f(ActionChoices, uint, Simple calldata) external {}
207 			function g(Test, Nested calldata) external {}
208 			function h(function(Nested memory) external returns (uint)[] calldata) external {}
209 			function i(Nested[] calldata) external {}
210 		}
211 	)";
212 	// Ignore analysis errors. This test only checks that correct signatures
213 	// are generated for external structs, but they are not yet supported
214 	// in code generation and therefore cause an error in the TypeChecker.
215 	SourceUnit const* sourceUnit = parseAnalyseAndReturnError(text, false, true, true).first;
216 	for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
217 		if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
218 		{
219 			auto functions = contract->definedFunctions();
220 			BOOST_REQUIRE(!functions.empty());
221 			BOOST_CHECK_EQUAL("f(uint8,uint256,(uint256))", functions[0]->externalSignature());
222 			BOOST_CHECK_EQUAL("g(address,((bytes32,address,(uint256)[])[2][],uint256))", functions[1]->externalSignature());
223 			BOOST_CHECK_EQUAL("h(function[])", functions[2]->externalSignature());
224 			BOOST_CHECK_EQUAL("i(((bytes32,address,(uint256)[])[2][],uint256)[])", functions[3]->externalSignature());
225 		}
226 }
227 
BOOST_AUTO_TEST_CASE(external_struct_signatures_in_libraries)228 BOOST_AUTO_TEST_CASE(external_struct_signatures_in_libraries)
229 {
230 	char const* text = R"(
231 		pragma abicoder v2;
232 		library Test {
233 			enum ActionChoices { GoLeft, GoRight, GoStraight, Sit }
234 			struct Simple { uint i; }
235 			struct Nested { X[2][] a; uint y; }
236 			struct X { bytes32 x; Test t; Simple[] s; }
237 			function f(ActionChoices, uint, Simple calldata) external {}
238 			function g(Test, Nested calldata) external {}
239 			function h(function(Nested memory) external returns (uint)[] calldata) external {}
240 			function i(Nested[] calldata) external {}
241 		}
242 	)";
243 	// Ignore analysis errors. This test only checks that correct signatures
244 	// are generated for external structs, but calldata structs are not yet supported
245 	// in code generation and therefore cause an error in the TypeChecker.
246 	SourceUnit const* sourceUnit = parseAnalyseAndReturnError(text, false, true, true).first;
247 	for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
248 		if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
249 		{
250 			auto functions = contract->definedFunctions();
251 			BOOST_REQUIRE(!functions.empty());
252 			BOOST_CHECK_EQUAL("f(Test.ActionChoices,uint256,Test.Simple)", functions[0]->externalSignature());
253 			BOOST_CHECK_EQUAL("g(Test,Test.Nested)", functions[1]->externalSignature());
254 			BOOST_CHECK_EQUAL("h(function[])", functions[2]->externalSignature());
255 			BOOST_CHECK_EQUAL("i(Test.Nested[])", functions[3]->externalSignature());
256 		}
257 }
258 
BOOST_AUTO_TEST_CASE(struct_with_mapping_in_library)259 BOOST_AUTO_TEST_CASE(struct_with_mapping_in_library)
260 {
261 	char const* text = R"(
262 		library Test {
263 			struct Nested { mapping(uint => uint)[2][] a; uint y; }
264 			struct X { Nested n; }
265 			function f(X storage x) external {}
266 		}
267 	)";
268 	SourceUnit const* sourceUnit = parseAndAnalyse(text);
269 	for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
270 		if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
271 		{
272 			auto functions = contract->definedFunctions();
273 			BOOST_REQUIRE(!functions.empty());
274 			BOOST_CHECK_EQUAL("f(Test.X storage)", functions[0]->externalSignature());
275 		}
276 }
277 
BOOST_AUTO_TEST_CASE(state_variable_accessors)278 BOOST_AUTO_TEST_CASE(state_variable_accessors)
279 {
280 	char const* text = R"(
281 		contract test {
282 			function fun() public {
283 				uint64(2);
284 			}
285 			uint256 public foo;
286 			mapping(uint=>bytes4) public map;
287 			mapping(uint=>mapping(uint=>bytes4)) public multiple_map;
288 		}
289 	)";
290 
291 	SourceUnit const* source;
292 	ContractDefinition const* contract;
293 	source = parseAndAnalyse(text);
294 	BOOST_REQUIRE((contract = retrieveContractByName(*source, "test")) != nullptr);
295 	FunctionTypePointer function = retrieveFunctionBySignature(*contract, "foo()");
296 	BOOST_REQUIRE(function && function->hasDeclaration());
297 	auto returnParams = function->returnParameterTypes();
298 	BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(), "uint256");
299 	BOOST_CHECK(function->stateMutability() == StateMutability::View);
300 
301 	function = retrieveFunctionBySignature(*contract, "map(uint256)");
302 	BOOST_REQUIRE(function && function->hasDeclaration());
303 	auto params = function->parameterTypes();
304 	BOOST_CHECK_EQUAL(params.at(0)->canonicalName(), "uint256");
305 	returnParams = function->returnParameterTypes();
306 	BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(), "bytes4");
307 	BOOST_CHECK(function->stateMutability() == StateMutability::View);
308 
309 	function = retrieveFunctionBySignature(*contract, "multiple_map(uint256,uint256)");
310 	BOOST_REQUIRE(function && function->hasDeclaration());
311 	params = function->parameterTypes();
312 	BOOST_CHECK_EQUAL(params.at(0)->canonicalName(), "uint256");
313 	BOOST_CHECK_EQUAL(params.at(1)->canonicalName(), "uint256");
314 	returnParams = function->returnParameterTypes();
315 	BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(), "bytes4");
316 	BOOST_CHECK(function->stateMutability() == StateMutability::View);
317 }
318 
BOOST_AUTO_TEST_CASE(private_state_variable)319 BOOST_AUTO_TEST_CASE(private_state_variable)
320 {
321 	char const* text = R"(
322 		contract test {
323 			function fun() public {
324 				uint64(2);
325 			}
326 			uint256 private foo;
327 			uint256 internal bar;
328 		}
329 	)";
330 
331 	ContractDefinition const* contract;
332 	SourceUnit const* source = parseAndAnalyse(text);
333 	BOOST_CHECK((contract = retrieveContractByName(*source, "test")) != nullptr);
334 	FunctionTypePointer function;
335 	function = retrieveFunctionBySignature(*contract, "foo()");
336 	BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of a private variable should not exist");
337 	function = retrieveFunctionBySignature(*contract, "bar()");
338 	BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of an internal variable should not exist");
339 }
340 
BOOST_AUTO_TEST_CASE(string)341 BOOST_AUTO_TEST_CASE(string)
342 {
343 	char const* sourceCode = R"(
344 		contract C {
345 			string s;
346 			function f(string calldata x) external { s = x; }
347 		}
348 	)";
349 	BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode));
350 }
351 
BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible)352 BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible)
353 {
354 	char const* sourceCode = R"(
355 		abstract contract C {
356 			function f(uint) public virtual returns (string memory);
357 			function g() public {
358 				string memory x = this.f(2);
359 				// we can assign to x but it is not usable.
360 				bytes(x).length;
361 			}
362 		}
363 	)";
364 	if (solidity::test::CommonOptions::get().evmVersion() == EVMVersion::homestead())
365 		CHECK_ERROR(sourceCode, TypeError, "Type inaccessible dynamic type is not implicitly convertible to expected type string memory.");
366 	else
367 		CHECK_SUCCESS_NO_WARNINGS(sourceCode);
368 }
369 
BOOST_AUTO_TEST_CASE(warn_nonpresent_pragma)370 BOOST_AUTO_TEST_CASE(warn_nonpresent_pragma)
371 {
372 	char const* text = R"(
373 		// SPDX-License-Identifier: GPL-3.0
374 		contract C {}
375 	)";
376 	auto sourceAndError = parseAnalyseAndReturnError(text, true, false);
377 	BOOST_REQUIRE(!sourceAndError.second.empty());
378 	BOOST_REQUIRE(!!sourceAndError.first);
379 	BOOST_CHECK(searchErrorMessage(*sourceAndError.second.front(), "Source file does not specify required compiler version!"));
380 }
381 
BOOST_AUTO_TEST_CASE(getter_is_memory_type)382 BOOST_AUTO_TEST_CASE(getter_is_memory_type)
383 {
384 	char const* text = R"(
385 		contract C {
386 			struct S { string m; }
387 			string[] public x;
388 			S[] public y;
389 		}
390 	)";
391 	CHECK_SUCCESS_NO_WARNINGS(text);
392 	// Check that the getters return a memory strings, not a storage strings.
393 	ContractDefinition const& c = dynamic_cast<ContractDefinition const&>(*compiler().ast("").nodes().at(1));
394 	BOOST_CHECK(c.interfaceFunctions().size() == 2);
395 	for (auto const& f: c.interfaceFunctions())
396 	{
397 		auto const& retType = f.second->returnParameterTypes().at(0);
398 		BOOST_CHECK(retType->dataStoredIn(DataLocation::Memory));
399 	}
400 }
401 
BOOST_AUTO_TEST_CASE(address_staticcall)402 BOOST_AUTO_TEST_CASE(address_staticcall)
403 {
404 	char const* sourceCode = R"(
405 		contract C {
406 			function f() public view returns(bool) {
407 				(bool success,) = address(0x4242).staticcall("");
408 				return success;
409 			}
410 		}
411 	)";
412 
413 	if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall())
414 		CHECK_SUCCESS_NO_WARNINGS(sourceCode);
415 	else
416 		CHECK_ERROR(sourceCode, TypeError, "\"staticcall\" is not supported by the VM version.");
417 }
418 
BOOST_AUTO_TEST_CASE(address_staticcall_value)419 BOOST_AUTO_TEST_CASE(address_staticcall_value)
420 {
421 	if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall())
422 	{
423 		char const* sourceCode = R"(
424 			contract C {
425 				function f() public view {
426 					address(0x4242).staticcall.value;
427 				}
428 			}
429 		)";
430 		CHECK_ERROR(sourceCode, TypeError, "Member \"value\" is only available for payable functions.");
431 	}
432 }
433 
BOOST_AUTO_TEST_CASE(address_call_full_return_type)434 BOOST_AUTO_TEST_CASE(address_call_full_return_type)
435 {
436 	char const* sourceCode = R"(
437 		contract C {
438 			function f() public {
439 				(bool success, bytes memory m) = address(0x4242).call("");
440 				success; m;
441 			}
442 		}
443 	)";
444 
445 	if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata())
446 		CHECK_SUCCESS_NO_WARNINGS(sourceCode);
447 	else
448 		CHECK_ERROR(sourceCode, TypeError, "Type inaccessible dynamic type is not implicitly convertible to expected type bytes memory.");
449 }
450 
BOOST_AUTO_TEST_CASE(address_delegatecall_full_return_type)451 BOOST_AUTO_TEST_CASE(address_delegatecall_full_return_type)
452 {
453 	char const* sourceCode = R"(
454 		contract C {
455 			function f() public {
456 				(bool success, bytes memory m) = address(0x4242).delegatecall("");
457 				success; m;
458 			}
459 		}
460 	)";
461 
462 	if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata())
463 		CHECK_SUCCESS_NO_WARNINGS(sourceCode);
464 	else
465 		CHECK_ERROR(sourceCode, TypeError, "Type inaccessible dynamic type is not implicitly convertible to expected type bytes memory.");
466 }
467 
468 
BOOST_AUTO_TEST_CASE(address_staticcall_full_return_type)469 BOOST_AUTO_TEST_CASE(address_staticcall_full_return_type)
470 {
471 	if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall())
472 	{
473 		char const* sourceCode = R"(
474 			contract C {
475 				function f() public view {
476 					(bool success, bytes memory m) = address(0x4242).staticcall("");
477 					success; m;
478 				}
479 			}
480 		)";
481 
482 		CHECK_SUCCESS_NO_WARNINGS(sourceCode);
483 	}
484 }
485 
486 BOOST_AUTO_TEST_SUITE_END()
487 
488 } // end namespaces
489