diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index d4de219af..6aee260e7 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -142,6 +142,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) bool const v050 = m_currentContract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); if (MagicType const* type = dynamic_cast(_memberAccess.expression().annotation().type.get())) + { if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "gas") { if (v050) @@ -155,6 +156,20 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) "\"msg.gas\" has been deprecated in favor of \"gasleft()\"" ); } + if (type->kind() == MagicType::Kind::Block && _memberAccess.memberName() == "blockhash") + { + if (v050) + m_errorReporter.typeError( + _memberAccess.location(), + "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\"" + ); + else + m_errorReporter.warning( + _memberAccess.location(), + "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\"" + ); + } + } if (m_nonPayablePublic && !m_library) if (MagicType const* type = dynamic_cast(_memberAccess.expression().annotation().type.get())) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index f4559edae..41700e283 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3002,30 +3002,19 @@ bool MagicType::operator==(Type const& _other) const return other.m_kind == m_kind; } -MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const* _contract) const +MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const { - const bool v050 = _contract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); switch (m_kind) { case Kind::Block: - { - std::vector members = { - {"coinbase", make_shared(160, IntegerType::Modifier::Address)}, - {"timestamp", make_shared(256)}, - {"difficulty", make_shared(256)}, - {"number", make_shared(256)}, - {"gaslimit", make_shared(256)} - }; - - if (!v050) - { - auto blockhashFun = make_shared(strings{"uint256"}, strings{"bytes32"}, - FunctionType::Kind::BlockHash, false, StateMutability::View); - - members.emplace_back("blockhash", blockhashFun); - } - return MemberList::MemberMap(members); - } + return MemberList::MemberMap({ + {"coinbase", make_shared(160, IntegerType::Modifier::Address)}, + {"timestamp", make_shared(256)}, + {"blockhash", make_shared(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)}, + {"difficulty", make_shared(256)}, + {"number", make_shared(256)}, + {"gaslimit", make_shared(256)} + }); case Kind::Message: return MemberList::MemberMap({ {"sender", make_shared(160, IntegerType::Modifier::Address)}, diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 43d390f56..a866e46c3 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1805,33 +1805,16 @@ BOOST_AUTO_TEST_CASE(uncalled_blockhash) BOOST_CHECK(result[0] != 0 || result[1] != 0 || result[2] != 0); } -BOOST_AUTO_TEST_CASE(blockhash_global_level) +BOOST_AUTO_TEST_CASE(blockhash_shadow_resolution) { - char const* sourceCode = R"( - contract Test { - function a() public returns (bytes32) { - return blockhash(0); - } + char const* code = R"( + contract C { + function blockhash(uint256 blockNumber) public returns(bytes32) { bytes32 x; return x; } + function f() public returns(bytes32) { return blockhash(3); } } )"; - compileAndRun(sourceCode); - BOOST_CHECK(!callContractFunction("a()").empty()); -} - -BOOST_AUTO_TEST_CASE(blockhash_shadow) -{ - char const* sourceCode = R"( - contract Test { - function blockhash(uint256 blockNumber) public returns (bytes32) { - return "abc"; - } - function f() returns (bytes32) { - return blockhash(3); - } - } - )"; - compileAndRun(sourceCode); - BOOST_REQUIRE(callContractFunction("f()") != encodeArgs("abc")); + compileAndRun(code, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0)); } BOOST_AUTO_TEST_CASE(log0) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 579fb2396..e4e7b8d82 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -8555,16 +8555,31 @@ BOOST_AUTO_TEST_CASE(require_visibility_specifiers) CHECK_ERROR(text, SyntaxError, "No visibility specified."); } -BOOST_AUTO_TEST_CASE(blockhash_not_available_in_block) +BOOST_AUTO_TEST_CASE(blockhash) { - char const* text = R"( - contract Test { - function a() public returns (bytes32) { - return block.blockhash(0); + char const* code = R"( + contract C { + function f() public view returns (bytes32) { + return block.blockhash(3); } } )"; - CHECK_ERROR(text, TypeError, "Member \"blockhash\" not found or not visible after argument-dependent lookup in block"); + CHECK_WARNING(code, "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\""); + + code = R"( + contract C { + function f() public view returns (bytes32) { return blockhash(3); } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(code); + + code = R"( + pragma experimental "v0.5.0"; + contract C { + function f() public returns (bytes32) { return block.blockhash(3); } + } + )"; + CHECK_ERROR(code, TypeError, "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\""); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index de25cfcfd..cd0a0b01c 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -25,6 +25,7 @@ #include #include +#include using namespace std; @@ -107,9 +108,11 @@ BOOST_AUTO_TEST_CASE(environment_access) vector view{ "block.coinbase", "block.timestamp", + "block.blockhash(7)", "block.difficulty", "block.number", "block.gaslimit", + "blockhash(7)", "gasleft()", "msg.gas", "msg.value", @@ -117,35 +120,47 @@ BOOST_AUTO_TEST_CASE(environment_access) "tx.origin", "tx.gasprice", "this", - "blockhash(7)", "address(1).balance" }; + // ``block.blockhash`` and ``blockhash`` are tested seperately below because their usage will + // produce warnings that can't be handled in a generic way. vector pure{ "msg.data", "msg.data[0]", "msg.sig", "msg", "block", - "blockhash", // Not evaluating the function "tx" }; for (string const& x: view) { CHECK_ERROR( - "contract C { function f() pure public { var x = " + x + "; x; } }", + "contract C { function f() pure public { " + x + "; } }", TypeError, "Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires \"view\"" ); } for (string const& x: pure) { - CHECK_WARNING_ALLOW_MULTI( - "contract C { function f() view public { var x = " + x + "; x; } }", - (std::vector{ - "Function state mutability can be restricted to pure", - "Use of the \"var\" keyword is deprecated." - })); + CHECK_WARNING( + "contract C { function f() view public { " + x + "; } }", + "Function state mutability can be restricted to pure" + ); } + + CHECK_WARNING_ALLOW_MULTI( + "contract C { function f() view public { blockhash; } }", + (std::vector{ + "Function state mutability can be restricted to pure", + "Statement has no effect." + })); + + CHECK_WARNING_ALLOW_MULTI( + "contract C { function f() view public { block.blockhash; } }", + (std::vector{ + "Function state mutability can be restricted to pure", + "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\"" + })); } BOOST_AUTO_TEST_CASE(view_error_for_050)