diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index b9be4d91b..800f102ba 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -1546,7 +1546,7 @@ BOOST_AUTO_TEST_CASE(exp_warn_literal_base) { char const* sourceCode = R"( contract test { - function f() returns(uint) { + function f() pure returns(uint) { uint8 x = 100; return 10**x; } @@ -1555,7 +1555,7 @@ BOOST_AUTO_TEST_CASE(exp_warn_literal_base) CHECK_WARNING(sourceCode, "might overflow"); sourceCode = R"( contract test { - function f() returns(uint) { + function f() pure returns(uint) { uint8 x = 100; return uint8(10)**x; } @@ -1564,7 +1564,7 @@ BOOST_AUTO_TEST_CASE(exp_warn_literal_base) CHECK_SUCCESS(sourceCode); sourceCode = R"( contract test { - function f() returns(uint) { + function f() pure returns(uint) { return 2**80; } } @@ -1576,7 +1576,7 @@ BOOST_AUTO_TEST_CASE(shift_warn_literal_base) { char const* sourceCode = R"( contract test { - function f() returns(uint) { + function f() pure returns(uint) { uint8 x = 100; return 10 << x; } @@ -1585,7 +1585,7 @@ BOOST_AUTO_TEST_CASE(shift_warn_literal_base) CHECK_WARNING(sourceCode, "might overflow"); sourceCode = R"( contract test { - function f() returns(uint) { + function f() pure returns(uint) { uint8 x = 100; return uint8(10) << x; } @@ -1594,7 +1594,7 @@ BOOST_AUTO_TEST_CASE(shift_warn_literal_base) CHECK_SUCCESS(sourceCode); sourceCode = R"( contract test { - function f() returns(uint) { + function f() pure returns(uint) { return 2 << 80; } } @@ -1602,7 +1602,7 @@ BOOST_AUTO_TEST_CASE(shift_warn_literal_base) CHECK_SUCCESS(sourceCode); sourceCode = R"( contract test { - function f() returns(uint) { + function f() pure returns(uint) { uint8 x = 100; return 10 >> x; } @@ -1624,7 +1624,7 @@ BOOST_AUTO_TEST_CASE(warn_var_from_zero) CHECK_WARNING(sourceCode, "uint8, which can hold values between 0 and 255"); sourceCode = R"( contract test { - function f() { + function f() pure { var i = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; i; } diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp new file mode 100644 index 000000000..7099ffd7a --- /dev/null +++ b/test/libsolidity/ViewPureChecker.cpp @@ -0,0 +1,323 @@ +/* + 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 . +*/ +/** + * Unit tests for the view and pure checker. + */ + +#include + +#include + +#include + +using namespace std; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +BOOST_FIXTURE_TEST_SUITE(ViewPureChecker, AnalysisFramework) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + char const* text = R"( + contract C { + uint x; + function g() pure {} + function f() view returns (uint) { return now; } + function h() { x = 2; } + function i() payable { x = 2; } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(call_internal_functions_success) +{ + char const* text = R"( + contract C { + function g() pure { g(); } + function f() view returns (uint) { f(); g(); } + function h() { h(); g(); f(); } + function i() payable { i(); h(); g(); f(); } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(suggest_pure) +{ + char const* text = R"( + contract C { + function g() view { } + } + )"; + CHECK_WARNING(text, "can be restricted to pure"); +} + +BOOST_AUTO_TEST_CASE(suggest_view) +{ + char const* text = R"( + contract C { + uint x; + function g() returns (uint) { return x; } + } + )"; + CHECK_WARNING(text, "can be restricted to view"); +} + +BOOST_AUTO_TEST_CASE(call_internal_functions_fail) +{ + CHECK_ERROR( + "contract C{ function f() pure { g(); } function g() view {} }", + TypeError, + "Function declared as pure, but this expression reads from the environment or state and thus requires \"view\"" + ); +} + +BOOST_AUTO_TEST_CASE(write_storage_fail) +{ + CHECK_WARNING( + "contract C{ uint x; function f() view { x = 2; } }", + "Function declared as view, but this expression modifies the state and thus requires non-payable (the default) or payable." + ); +} + +BOOST_AUTO_TEST_CASE(environment_access) +{ + vector view{ + "block.coinbase", + "block.timestamp", + "block.blockhash(7)", + "block.difficulty", + "block.number", + "block.gaslimit", + "msg.gas", + "msg.value", + "msg.sender", + "tx.origin", + "tx.gasprice", + "this", + "address(1).balance" + }; + vector pure{ + "msg.data", + "msg.data[0]", + "msg.sig", + "block.blockhash", // Not evaluating the function + "msg", + "block", + "tx" + }; + for (string const& x: view) + { + CHECK_ERROR( + "contract C { function f() pure { var x = " + x + "; x; } }", + TypeError, + "Function declared as pure, but this expression reads from the environment or state and thus requires \"view\"" + ); + } + for (string const& x: pure) + { + CHECK_WARNING( + "contract C { function f() view { var x = " + x + "; x; } }", + "restricted to pure" + ); + } +} + +BOOST_AUTO_TEST_CASE(modifiers) +{ + string text = R"( + contract D { + uint x; + modifier purem(uint) { _; } + modifier viewm(uint) { uint a = x; _; a; } + modifier nonpayablem(uint) { x = 2; _; } + } + contract C is D { + function f() purem(0) pure {} + function g() viewm(0) view {} + function h() nonpayablem(0) {} + function i() purem(x) view {} + function j() viewm(x) view {} + function k() nonpayablem(x) {} + function l() purem(x = 2) {} + function m() viewm(x = 2) {} + function n() nonpayablem(x = 2) {} + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(interface) +{ + string text = R"( + interface D { + function f() view; + } + contract C is D { + function f() view {} + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(overriding) +{ + string text = R"( + contract D { + uint x; + function f() { x = 2; } + } + contract C is D { + function f() {} + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(returning_structs) +{ + string text = R"( + contract C { + struct S { uint x; } + S s; + function f() view internal returns (S storage) { + return s; + } + function g() + { + f().x = 2; + } + function h() view + { + f(); + f().x; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(mappings) +{ + string text = R"( + contract C { + mapping(uint => uint) a; + function f() view { + a; + } + function g() view { + a[2]; + } + function h() { + a[2] = 3; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(local_storage_variables) +{ + string text = R"( + contract C { + struct S { uint a; } + S s; + function f() view { + S storage x = s; + x; + } + function g() view { + S storage x = s; + x = s; + } + function i() { + s.a = 2; + } + function h() { + S storage x = s; + x.a = 2; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(builtin_functions) +{ + string text = R"( + contract C { + function f() { + this.transfer(1); + require(this.send(2)); + selfdestruct(this); + require(this.delegatecall()); + require(this.call()); + } + function g() pure { + var x = keccak256("abc"); + var y = sha256("abc"); + var z = ecrecover(1, 2, 3, 4); + require(true); + assert(true); + x; y; z; + } + function() payable {} + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(function_types) +{ + string text = R"( + contract C { + function f() pure { + function () external nonpayFun; + function () external view viewFun; + function () external pure pureFun; + + nonpayFun; + viewFun; + pureFun; + pureFun(); + } + function g() view { + function () external view viewFun; + + viewFun(); + } + function h() { + function () external nonpayFun; + + nonpayFun(); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +}