mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
b3230b0bdc
test: updat filereader test deprecate namespace std deprecate namespace std deprecate namespace std deprecate namespace std deprecate namespace std deprecate namespace std deprecate namespace std deprecate namespace std deprecated std namespace deprecated std namespace deprecated std namespace deprecated std namespace deprecated std namespace deprecated std namespace deprecated std namespace deprecated std namespace deprecated std namespace depecrate namespace std deprecated namespace std check ci clean line Co-authored-by: Nikola Matić <nikola.matic@ethereum.org> purge line pure line deprecate std deprecate std deprecate std deprecate std deprecate std deprecate deprecate std bye namespace
488 lines
16 KiB
C++
488 lines
16 KiB
C++
/*
|
|
This file is part of solidity.
|
|
|
|
solidity is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
solidity is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
/**
|
|
* @author Christian <c@ethdev.com>
|
|
* @date 2014
|
|
* Unit tests for the name and type resolution of the solidity parser.
|
|
*/
|
|
|
|
#include <test/libsolidity/AnalysisFramework.h>
|
|
|
|
#include <test/Common.h>
|
|
|
|
#include <libsolidity/ast/AST.h>
|
|
|
|
#include <libsolutil/Keccak256.h>
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
|
|
#include <string>
|
|
|
|
using namespace solidity::langutil;
|
|
|
|
namespace solidity::frontend::test
|
|
{
|
|
|
|
BOOST_FIXTURE_TEST_SUITE(SolidityNameAndTypeResolution, AnalysisFramework)
|
|
|
|
BOOST_AUTO_TEST_CASE(function_no_implementation)
|
|
{
|
|
SourceUnit const* sourceUnit = nullptr;
|
|
char const* text = R"(
|
|
abstract contract test {
|
|
function functionName(bytes32 input) public virtual returns (bytes32 out);
|
|
}
|
|
)";
|
|
sourceUnit = parseAndAnalyse(text);
|
|
std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes();
|
|
ContractDefinition* contract = dynamic_cast<ContractDefinition*>(nodes[1].get());
|
|
BOOST_REQUIRE(contract);
|
|
BOOST_CHECK(!contract->annotation().unimplementedDeclarations->empty());
|
|
BOOST_CHECK(!contract->definedFunctions()[0]->isImplemented());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(abstract_contract)
|
|
{
|
|
SourceUnit const* sourceUnit = nullptr;
|
|
char const* text = R"(
|
|
abstract contract base { function foo() public virtual; }
|
|
contract derived is base { function foo() public override {} }
|
|
)";
|
|
sourceUnit = parseAndAnalyse(text);
|
|
std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes();
|
|
ContractDefinition* base = dynamic_cast<ContractDefinition*>(nodes[1].get());
|
|
ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get());
|
|
BOOST_REQUIRE(base);
|
|
BOOST_CHECK(!base->annotation().unimplementedDeclarations->empty());
|
|
BOOST_CHECK(!base->definedFunctions()[0]->isImplemented());
|
|
BOOST_REQUIRE(derived);
|
|
BOOST_CHECK(derived->annotation().unimplementedDeclarations->empty());
|
|
BOOST_CHECK(derived->definedFunctions()[0]->isImplemented());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(abstract_contract_with_overload)
|
|
{
|
|
SourceUnit const* sourceUnit = nullptr;
|
|
char const* text = R"(
|
|
abstract contract base { function foo(bool) public virtual; }
|
|
abstract contract derived is base { function foo(uint) public {} }
|
|
)";
|
|
sourceUnit = parseAndAnalyse(text);
|
|
std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes();
|
|
ContractDefinition* base = dynamic_cast<ContractDefinition*>(nodes[1].get());
|
|
ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get());
|
|
BOOST_REQUIRE(base);
|
|
BOOST_CHECK(!base->annotation().unimplementedDeclarations->empty());
|
|
BOOST_REQUIRE(derived);
|
|
BOOST_CHECK(!derived->annotation().unimplementedDeclarations->empty());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(implement_abstract_via_constructor)
|
|
{
|
|
SourceUnit const* sourceUnit = nullptr;
|
|
char const* text = R"(
|
|
abstract contract base { function foo() public virtual; }
|
|
abstract contract foo is base { constructor() {} }
|
|
)";
|
|
sourceUnit = parseAndAnalyse(text);
|
|
std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes();
|
|
BOOST_CHECK_EQUAL(nodes.size(), 3);
|
|
ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get());
|
|
BOOST_REQUIRE(derived);
|
|
BOOST_CHECK(!derived->annotation().unimplementedDeclarations->empty());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(function_canonical_signature)
|
|
{
|
|
SourceUnit const* sourceUnit = nullptr;
|
|
char const* text = R"(
|
|
contract Test {
|
|
function foo(uint256 arg1, uint64 arg2, bool arg3) public returns (uint256 ret) {
|
|
ret = arg1 + arg2;
|
|
}
|
|
}
|
|
)";
|
|
sourceUnit = parseAndAnalyse(text);
|
|
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
|
|
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
|
{
|
|
auto functions = contract->definedFunctions();
|
|
BOOST_CHECK_EQUAL("foo(uint256,uint64,bool)", functions[0]->externalSignature());
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(function_canonical_signature_type_aliases)
|
|
{
|
|
SourceUnit const* sourceUnit = nullptr;
|
|
char const* text = R"(
|
|
contract Test {
|
|
function boo(uint, bytes32, address) public returns (uint ret) {
|
|
ret = 5;
|
|
}
|
|
}
|
|
)";
|
|
sourceUnit = parseAndAnalyse(text);
|
|
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
|
|
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
|
{
|
|
auto functions = contract->definedFunctions();
|
|
if (functions.empty())
|
|
continue;
|
|
BOOST_CHECK_EQUAL("boo(uint256,bytes32,address)", functions[0]->externalSignature());
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(function_external_types)
|
|
{
|
|
SourceUnit const* sourceUnit = nullptr;
|
|
char const* text = R"(
|
|
contract C {
|
|
uint a;
|
|
}
|
|
contract Test {
|
|
function boo(uint, bool, bytes8, bool[2] calldata, uint[] calldata, C, address[] calldata) external returns (uint ret) {
|
|
ret = 5;
|
|
}
|
|
}
|
|
)";
|
|
sourceUnit = parseAndAnalyse(text);
|
|
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
|
|
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
|
{
|
|
auto functions = contract->definedFunctions();
|
|
if (functions.empty())
|
|
continue;
|
|
BOOST_CHECK_EQUAL("boo(uint256,bool,bytes8,bool[2],uint256[],address,address[])", functions[0]->externalSignature());
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(enum_external_type)
|
|
{
|
|
SourceUnit const* sourceUnit = nullptr;
|
|
char const* text = R"(
|
|
// test for bug #1801
|
|
contract Test {
|
|
enum ActionChoices { GoLeft, GoRight, GoStraight, Sit }
|
|
function boo(ActionChoices enumArg) external returns (uint ret) {
|
|
ret = 5;
|
|
}
|
|
}
|
|
)";
|
|
sourceUnit = parseAndAnalyse(text);
|
|
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
|
|
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
|
{
|
|
auto functions = contract->definedFunctions();
|
|
if (functions.empty())
|
|
continue;
|
|
BOOST_CHECK_EQUAL("boo(uint8)", functions[0]->externalSignature());
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(external_struct_signatures)
|
|
{
|
|
char const* text = R"(
|
|
pragma abicoder v2;
|
|
contract Test {
|
|
enum ActionChoices { GoLeft, GoRight, GoStraight, Sit }
|
|
struct Simple { uint i; }
|
|
struct Nested { X[2][] a; uint y; }
|
|
struct X { bytes32 x; Test t; Simple[] s; }
|
|
function f(ActionChoices, uint, Simple calldata) external {}
|
|
function g(Test, Nested calldata) external {}
|
|
function h(function(Nested memory) external returns (uint)[] calldata) external {}
|
|
function i(Nested[] calldata) external {}
|
|
}
|
|
)";
|
|
// Ignore analysis errors. This test only checks that correct signatures
|
|
// are generated for external structs, but they are not yet supported
|
|
// in code generation and therefore cause an error in the TypeChecker.
|
|
SourceUnit const* sourceUnit = parseAnalyseAndReturnError(text, false, true, true).first;
|
|
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
|
|
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
|
{
|
|
auto functions = contract->definedFunctions();
|
|
BOOST_REQUIRE(!functions.empty());
|
|
BOOST_CHECK_EQUAL("f(uint8,uint256,(uint256))", functions[0]->externalSignature());
|
|
BOOST_CHECK_EQUAL("g(address,((bytes32,address,(uint256)[])[2][],uint256))", functions[1]->externalSignature());
|
|
BOOST_CHECK_EQUAL("h(function[])", functions[2]->externalSignature());
|
|
BOOST_CHECK_EQUAL("i(((bytes32,address,(uint256)[])[2][],uint256)[])", functions[3]->externalSignature());
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(external_struct_signatures_in_libraries)
|
|
{
|
|
char const* text = R"(
|
|
pragma abicoder v2;
|
|
library Test {
|
|
enum ActionChoices { GoLeft, GoRight, GoStraight, Sit }
|
|
struct Simple { uint i; }
|
|
struct Nested { X[2][] a; uint y; }
|
|
struct X { bytes32 x; Test t; Simple[] s; }
|
|
function f(ActionChoices, uint, Simple calldata) external {}
|
|
function g(Test, Nested calldata) external {}
|
|
function h(function(Nested memory) external returns (uint)[] calldata) external {}
|
|
function i(Nested[] calldata) external {}
|
|
}
|
|
)";
|
|
// Ignore analysis errors. This test only checks that correct signatures
|
|
// are generated for external structs, but calldata structs are not yet supported
|
|
// in code generation and therefore cause an error in the TypeChecker.
|
|
SourceUnit const* sourceUnit = parseAnalyseAndReturnError(text, false, true, true).first;
|
|
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
|
|
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
|
{
|
|
auto functions = contract->definedFunctions();
|
|
BOOST_REQUIRE(!functions.empty());
|
|
BOOST_CHECK_EQUAL("f(Test.ActionChoices,uint256,Test.Simple)", functions[0]->externalSignature());
|
|
BOOST_CHECK_EQUAL("g(Test,Test.Nested)", functions[1]->externalSignature());
|
|
BOOST_CHECK_EQUAL("h(function[])", functions[2]->externalSignature());
|
|
BOOST_CHECK_EQUAL("i(Test.Nested[])", functions[3]->externalSignature());
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(struct_with_mapping_in_library)
|
|
{
|
|
char const* text = R"(
|
|
library Test {
|
|
struct Nested { mapping(uint => uint)[2][] a; uint y; }
|
|
struct X { Nested n; }
|
|
function f(X storage x) external {}
|
|
}
|
|
)";
|
|
SourceUnit const* sourceUnit = parseAndAnalyse(text);
|
|
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
|
|
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
|
{
|
|
auto functions = contract->definedFunctions();
|
|
BOOST_REQUIRE(!functions.empty());
|
|
BOOST_CHECK_EQUAL("f(Test.X storage)", functions[0]->externalSignature());
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(state_variable_accessors)
|
|
{
|
|
char const* text = R"(
|
|
contract test {
|
|
function fun() public {
|
|
uint64(2);
|
|
}
|
|
uint256 public foo;
|
|
mapping(uint=>bytes4) public map;
|
|
mapping(uint=>mapping(uint=>bytes4)) public multiple_map;
|
|
}
|
|
)";
|
|
|
|
SourceUnit const* source;
|
|
ContractDefinition const* contract;
|
|
source = parseAndAnalyse(text);
|
|
BOOST_REQUIRE((contract = retrieveContractByName(*source, "test")) != nullptr);
|
|
FunctionTypePointer function = retrieveFunctionBySignature(*contract, "foo()");
|
|
BOOST_REQUIRE(function && function->hasDeclaration());
|
|
auto returnParams = function->returnParameterTypes();
|
|
BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(), "uint256");
|
|
BOOST_CHECK(function->stateMutability() == StateMutability::View);
|
|
|
|
function = retrieveFunctionBySignature(*contract, "map(uint256)");
|
|
BOOST_REQUIRE(function && function->hasDeclaration());
|
|
auto params = function->parameterTypes();
|
|
BOOST_CHECK_EQUAL(params.at(0)->canonicalName(), "uint256");
|
|
returnParams = function->returnParameterTypes();
|
|
BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(), "bytes4");
|
|
BOOST_CHECK(function->stateMutability() == StateMutability::View);
|
|
|
|
function = retrieveFunctionBySignature(*contract, "multiple_map(uint256,uint256)");
|
|
BOOST_REQUIRE(function && function->hasDeclaration());
|
|
params = function->parameterTypes();
|
|
BOOST_CHECK_EQUAL(params.at(0)->canonicalName(), "uint256");
|
|
BOOST_CHECK_EQUAL(params.at(1)->canonicalName(), "uint256");
|
|
returnParams = function->returnParameterTypes();
|
|
BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(), "bytes4");
|
|
BOOST_CHECK(function->stateMutability() == StateMutability::View);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(private_state_variable)
|
|
{
|
|
char const* text = R"(
|
|
contract test {
|
|
function fun() public {
|
|
uint64(2);
|
|
}
|
|
uint256 private foo;
|
|
uint256 internal bar;
|
|
}
|
|
)";
|
|
|
|
ContractDefinition const* contract;
|
|
SourceUnit const* source = parseAndAnalyse(text);
|
|
BOOST_CHECK((contract = retrieveContractByName(*source, "test")) != nullptr);
|
|
FunctionTypePointer function;
|
|
function = retrieveFunctionBySignature(*contract, "foo()");
|
|
BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of a private variable should not exist");
|
|
function = retrieveFunctionBySignature(*contract, "bar()");
|
|
BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of an internal variable should not exist");
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(string)
|
|
{
|
|
char const* sourceCode = R"(
|
|
contract C {
|
|
string s;
|
|
function f(string calldata x) external { s = x; }
|
|
}
|
|
)";
|
|
BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible)
|
|
{
|
|
char const* sourceCode = R"(
|
|
abstract contract C {
|
|
function f(uint) public virtual returns (string memory);
|
|
function g() public {
|
|
string memory x = this.f(2);
|
|
// we can assign to x but it is not usable.
|
|
bytes(x).length;
|
|
}
|
|
}
|
|
)";
|
|
if (solidity::test::CommonOptions::get().evmVersion() == EVMVersion::homestead())
|
|
CHECK_ERROR(sourceCode, TypeError, "Type inaccessible dynamic type is not implicitly convertible to expected type string memory.");
|
|
else
|
|
CHECK_SUCCESS_NO_WARNINGS(sourceCode);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(warn_nonpresent_pragma)
|
|
{
|
|
char const* text = R"(
|
|
// SPDX-License-Identifier: GPL-3.0
|
|
contract C {}
|
|
)";
|
|
auto sourceAndError = parseAnalyseAndReturnError(text, true, false);
|
|
BOOST_REQUIRE(!sourceAndError.second.empty());
|
|
BOOST_REQUIRE(!!sourceAndError.first);
|
|
BOOST_CHECK(searchErrorMessage(*sourceAndError.second.front(), "Source file does not specify required compiler version!"));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(getter_is_memory_type)
|
|
{
|
|
char const* text = R"(
|
|
contract C {
|
|
struct S { string m; }
|
|
string[] public x;
|
|
S[] public y;
|
|
}
|
|
)";
|
|
CHECK_SUCCESS_NO_WARNINGS(text);
|
|
// Check that the getters return a memory strings, not a storage strings.
|
|
ContractDefinition const& c = dynamic_cast<ContractDefinition const&>(*compiler().ast("").nodes().at(1));
|
|
BOOST_CHECK(c.interfaceFunctions().size() == 2);
|
|
for (auto const& f: c.interfaceFunctions())
|
|
{
|
|
auto const& retType = f.second->returnParameterTypes().at(0);
|
|
BOOST_CHECK(retType->dataStoredIn(DataLocation::Memory));
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(address_staticcall)
|
|
{
|
|
char const* sourceCode = R"(
|
|
contract C {
|
|
function f() public view returns(bool) {
|
|
(bool success,) = address(0x4242).staticcall("");
|
|
return success;
|
|
}
|
|
}
|
|
)";
|
|
|
|
if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall())
|
|
CHECK_SUCCESS_NO_WARNINGS(sourceCode);
|
|
else
|
|
CHECK_ERROR(sourceCode, TypeError, "\"staticcall\" is not supported by the VM version.");
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(address_staticcall_value)
|
|
{
|
|
if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall())
|
|
{
|
|
char const* sourceCode = R"(
|
|
contract C {
|
|
function f() public view {
|
|
address(0x4242).staticcall.value;
|
|
}
|
|
}
|
|
)";
|
|
CHECK_ERROR(sourceCode, TypeError, "Member \"value\" is only available for payable functions.");
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(address_call_full_return_type)
|
|
{
|
|
char const* sourceCode = R"(
|
|
contract C {
|
|
function f() public {
|
|
(bool success, bytes memory m) = address(0x4242).call("");
|
|
success; m;
|
|
}
|
|
}
|
|
)";
|
|
|
|
if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata())
|
|
CHECK_SUCCESS_NO_WARNINGS(sourceCode);
|
|
else
|
|
CHECK_ERROR(sourceCode, TypeError, "Type inaccessible dynamic type is not implicitly convertible to expected type bytes memory.");
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(address_delegatecall_full_return_type)
|
|
{
|
|
char const* sourceCode = R"(
|
|
contract C {
|
|
function f() public {
|
|
(bool success, bytes memory m) = address(0x4242).delegatecall("");
|
|
success; m;
|
|
}
|
|
}
|
|
)";
|
|
|
|
if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata())
|
|
CHECK_SUCCESS_NO_WARNINGS(sourceCode);
|
|
else
|
|
CHECK_ERROR(sourceCode, TypeError, "Type inaccessible dynamic type is not implicitly convertible to expected type bytes memory.");
|
|
}
|
|
|
|
|
|
BOOST_AUTO_TEST_CASE(address_staticcall_full_return_type)
|
|
{
|
|
if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall())
|
|
{
|
|
char const* sourceCode = R"(
|
|
contract C {
|
|
function f() public view {
|
|
(bool success, bytes memory m) = address(0x4242).staticcall("");
|
|
success; m;
|
|
}
|
|
}
|
|
)";
|
|
|
|
CHECK_SUCCESS_NO_WARNINGS(sourceCode);
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|
|
|
|
} // end namespaces
|