diff options
31 files changed, 452 insertions, 243 deletions
diff --git a/Changelog.md b/Changelog.md index 7a4149f3..9235ed3a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Features: Bugfixes: * Code Generator: Allow ``block.blockhash`` without being called. + * Code Generator: Do not include internal functions in the runtime bytecode which are only referenced in the constructor. * Code Generator: Properly skip unneeded storage array cleanup when not reducing length. * Code Generator: Bugfix in modifier lookup in libraries. * Commandline interface: Support ``--evm-version constantinople`` properly. diff --git a/docs/contributing.rst b/docs/contributing.rst index 8b4695e4..1bcaed7c 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -91,6 +91,85 @@ Alternatively, there is a testing script at ``scripts/test.sh`` which executes a Travis CI even runs some additional tests (including ``solc-js`` and testing third party Solidity frameworks) that require compiling the Emscripten target. +Writing and running syntax tests +-------------------------------- + +As mentioned above, syntax tests are stored in individual contracts. These files must contain annotations, stating the expected result(s) of the respective test. +The test suite will compile and check them against the given expectations. + +Example: ``./test/libsolidity/syntaxTests/double_stateVariable_declaration.sol`` + +:: + + contract test { + uint256 variable; + uint128 variable; + } + // ---- + // DeclarationError: Identifier already declared. + +A syntax test must contain at least the contract under test itself, followed by the seperator ``----``. The additional comments above are used to describe the +expected compiler errors or warnings. This section can be empty in case that the contract should compile without any errors or warnings. + +In the above example, the state variable ``variable`` was declared twice, which is not allowed. This will result in a ``DeclarationError`` stating that the identifer was already declared. + +The tool that is being used for those tests is called ``isoltest`` and can be found under ``./test/tools/``. It is an interactive tool which allows +editing of failing contracts using your prefered text editor. Let's try to break this test by removing the second declaration of ``variable``: + +:: + + contract test { + uint256 variable; + } + // ---- + // DeclarationError: Identifier already declared. + +Running ``./test/isoltest`` again will result in a test failure: + +:: + + syntaxTests/double_stateVariable_declaration.sol: FAIL + Contract: + contract test { + uint256 variable; + } + + Expected result: + DeclarationError: Identifier already declared. + Obtained result: + Success + + +which prints the expected result next to the obtained result, but also provides a way to change edit / update / skip the current contract or to even quit. +``isoltest`` offers several options for failing tests: + +- edit: ``isoltest`` will try to open the editor that was specified before using ``isoltest --editor /path/to/editor``. If no path was set, this will result in a runtime error. In case an editor was specified, this will open it such that the contract can be adjusted. +- update: Updates the contract under test. This will either remove the annotation which contains the exception not met or will add missing expectations. The test will then be run again. +- skip: Skips the execution of this particular test. +- quit: Quits ``isoltest``. + +Automatically updating the test above will change it to + +:: + + contract test { + uint256 variable; + } + // ---- + +and re-run the test. It will now pass again: + +:: + + Re-running test case... + syntaxTests/double_stateVariable_declaration.sol: OK + + +.. note:: + + Please choose a name for the contract file, that is self-explainatory in the sense of what is been tested, e.g. ``double_variable_declaration.sol``. + Do not put more than one contract into a single file. ``isoltest`` is currently not able to recognize them individually. + Whiskers ======== diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index a7d5c445..01154854 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -314,7 +314,7 @@ The following is the order of precedence for operators, listed in order of evalu Global Variables ================ -- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks +- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent, excluding current, blocks - deprecated in version 0.4.22 and replaced by ``blockhash(uint blockNumber)``. - ``block.coinbase`` (``address``): current block miner's address - ``block.difficulty`` (``uint``): current block difficulty - ``block.gaslimit`` (``uint``): current block gaslimit @@ -331,6 +331,7 @@ Global Variables - ``assert(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for internal error) - ``require(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component) - ``revert()``: abort execution and revert state changes +- ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks - ``keccak256(...) returns (bytes32)``: compute the Ethereum-SHA-3 (Keccak-256) hash of the :ref:`(tightly packed) arguments <abi_packed_mode>` - ``sha3(...) returns (bytes32)``: an alias to ``keccak256`` - ``sha256(...) returns (bytes32)``: compute the SHA-256 hash of the :ref:`(tightly packed) arguments <abi_packed_mode>` @@ -346,6 +347,23 @@ Global Variables - ``<address>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure - ``<address>.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure +.. note:: + Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness, + unless you know what you are doing. + + Both the timestamp and the block hash can be influenced by miners to some degree. + Bad actors in the mining community can for example run a casino payout function on a chosen hash + and just retry a different hash if they did not receive any money. + + The current block timestamp must be strictly larger than the timestamp of the last block, + but the only guarantee is that it will be somewhere between the timestamps of two + consecutive blocks in the canonical chain. + +.. note:: + The block hashes are not available for all blocks for scalability reasons. + You can only access the hashes of the most recent 256 blocks, all other + values will be zero. + .. index:: visibility, public, private, external, internal Function Visibility Specifiers diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index d789e87f..2571f20a 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -52,7 +52,7 @@ namespace and are mainly used to provide information about the blockchain. Block and Transaction Properties -------------------------------- -- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks excluding current +- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent, excluding current, blocks - deprecated in version 0.4.22 and replaced by ``blockhash(uint blockNumber)``. - ``block.coinbase`` (``address``): current block miner's address - ``block.difficulty`` (``uint``): current block difficulty - ``block.gaslimit`` (``uint``): current block gaslimit @@ -74,7 +74,7 @@ Block and Transaction Properties This includes calls to library functions. .. note:: - Do not rely on ``block.timestamp``, ``now`` and ``block.blockhash`` as a source of randomness, + Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness, unless you know what you are doing. Both the timestamp and the block hash can be influenced by miners to some degree. diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index 34cb61d8..6a858d36 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -38,6 +38,7 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{ make_shared<MagicVariableDeclaration>("addmod", make_shared<FunctionType>(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::AddMod, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("assert", make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Kind::Assert, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::Block)), + make_shared<MagicVariableDeclaration>("blockhash", make_shared<FunctionType>(strings{"uint256"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)), make_shared<MagicVariableDeclaration>("ecrecover", make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("gasleft", make_shared<FunctionType>(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, false, StateMutability::View)), make_shared<MagicVariableDeclaration>("keccak256", make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true, StateMutability::Pure)), diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index d4de219a..6aee260e 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<MagicType const*>(_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<MagicType const*>(_memberAccess.expression().annotation().type.get())) diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 1bd1103b..68f0b3a1 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -585,7 +585,7 @@ void CompilerUtils::combineExternalFunctionType(bool _leftAligned) leftShiftNumberOnStack(64); } -void CompilerUtils::pushCombinedFunctionEntryLabel(Declaration const& _function) +void CompilerUtils::pushCombinedFunctionEntryLabel(Declaration const& _function, bool _runtimeOnly) { m_context << m_context.functionEntryLabel(_function).pushTag(); // If there is a runtime context, we have to merge both labels into the same @@ -593,9 +593,10 @@ void CompilerUtils::pushCombinedFunctionEntryLabel(Declaration const& _function) if (CompilerContext* rtc = m_context.runtimeContext()) { leftShiftNumberOnStack(32); - m_context << - rtc->functionEntryLabel(_function).toSubAssemblyTag(m_context.runtimeSub()) << - Instruction::OR; + if (_runtimeOnly) + m_context << + rtc->functionEntryLabel(_function).toSubAssemblyTag(m_context.runtimeSub()) << + Instruction::OR; } } diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 76670d47..389673ef 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -188,7 +188,8 @@ public: /// Appends code that combines the construction-time (if available) and runtime function /// entry label of the given function into a single stack slot. /// Note: This might cause the compilation queue of the runtime context to be extended. - void pushCombinedFunctionEntryLabel(Declaration const& _function); + /// If @a _runtimeOnly, the entry label will include the runtime assembly tag. + void pushCombinedFunctionEntryLabel(Declaration const& _function, bool _runtimeOnly = true); /// Appends code for an implicit or explicit type conversion. This includes erasing higher /// order bits (@see appendHighBitCleanup) when widening integer but also copy to memory diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 37069c3e..9e2d30d5 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -518,7 +518,23 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) arguments[i]->accept(*this); utils().convertType(*arguments[i]->annotation().type, *function.parameterTypes()[i]); } - _functionCall.expression().accept(*this); + + { + bool shortcutTaken = false; + if (auto identifier = dynamic_cast<Identifier const*>(&_functionCall.expression())) + if (auto functionDef = dynamic_cast<FunctionDefinition const*>(identifier->annotation().referencedDeclaration)) + { + // Do not directly visit the identifier, because this way, we can avoid + // the runtime entry label to be created at the creation time context. + CompilerContext::LocationSetter locationSetter2(m_context, *identifier); + utils().pushCombinedFunctionEntryLabel(m_context.resolveVirtualFunction(*functionDef), false); + shortcutTaken = true; + } + + if (!shortcutTaken) + _functionCall.expression().accept(*this); + } + unsigned parameterSize = CompilerUtils::sizeOnStack(function.parameterTypes()); if (function.bound()) { @@ -1359,6 +1375,10 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) } } else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration)) + // If the identifier is called right away, this code is executed in visit(FunctionCall...), because + // we want to avoid having a reference to the runtime function entry point in the + // constructor context, since this would force the compiler to include unreferenced + // internal functions in the runtime contex. utils().pushCombinedFunctionEntryLabel(m_context.resolveVirtualFunction(*functionDef)); else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration)) appendVariable(*variable, static_cast<Expression const&>(_identifier)); diff --git a/scripts/extract_test_cases.py b/scripts/extract_test_cases.py new file mode 100755 index 00000000..07ef9a96 --- /dev/null +++ b/scripts/extract_test_cases.py @@ -0,0 +1,49 @@ +#!/usr/bin/python +# +# This script reads C++ or RST source files and writes all +# multi-line strings into individual files. +# This can be used to extract the Solidity test cases +# into files for e.g. fuzz testing as +# scripts/isolate_tests.py test/libsolidity/* + +import sys +import re +import os +import hashlib +from os.path import join + +def extract_test_cases(path): + lines = open(path, 'rb').read().splitlines() + + inside = False + delimiter = '' + test = '' + + ctr = 1 + test_name = '' + + for l in lines: + if inside: + if l.strip().endswith(')' + delimiter + '";'): + open('%03d_%s.sol' % (ctr, test_name), 'wb').write(test) + ctr += 1 + inside = False + test = '' + else: + l = re.sub('^\t\t', '', l) + l = l.replace('\t', ' ') + test += l + '\n' + else: + m = re.search(r'BOOST_AUTO_TEST_CASE\(([^(]*)\)', l.strip()) + if m: + test_name = m.group(1) + m = re.search(r'R"([^(]*)\($', l.strip()) + if m: + inside = True + delimiter = m.group(1) + + +if __name__ == '__main__': + path = sys.argv[1] + extract_test_cases(path) + diff --git a/test/libsolidity/SolidityCompiler.cpp b/test/libsolidity/SolidityCompiler.cpp new file mode 100644 index 00000000..e87ab603 --- /dev/null +++ b/test/libsolidity/SolidityCompiler.cpp @@ -0,0 +1,60 @@ +/* + 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/>. + */ +/** + * Unit tests for the compiler itself. + */ + +#include <test/libsolidity/AnalysisFramework.h> + +#include <test/Options.h> + +using namespace std; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +BOOST_FIXTURE_TEST_SUITE(Compiler, AnalysisFramework) + +BOOST_AUTO_TEST_CASE(does_not_include_creation_time_only_internal_functions) +{ + char const* sourceCode = R"( + contract C { + uint x; + function C() { f(); } + function f() internal { for (uint i = 0; i < 10; ++i) x += 3 + i; } + } + )"; + BOOST_REQUIRE(success(sourceCode)); + m_compiler.setOptimiserSettings(dev::test::Options::get().optimize); + BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed"); + bytes const& creationBytecode = m_compiler.object("C").bytecode; + bytes const& runtimeBytecode = m_compiler.runtimeObject("C").bytecode; + BOOST_CHECK(creationBytecode.size() >= 120); + BOOST_CHECK(creationBytecode.size() <= 150); + BOOST_CHECK(runtimeBytecode.size() >= 50); + BOOST_CHECK(runtimeBytecode.size() <= 70); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 44dc40f7..a866e46c 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1805,6 +1805,18 @@ BOOST_AUTO_TEST_CASE(uncalled_blockhash) BOOST_CHECK(result[0] != 0 || result[1] != 0 || result[2] != 0); } +BOOST_AUTO_TEST_CASE(blockhash_shadow_resolution) +{ + 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(code, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0)); +} + BOOST_AUTO_TEST_CASE(log0) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index c8adfc6e..90d8265c 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -503,12 +503,15 @@ BOOST_AUTO_TEST_CASE(blockhash) char const* sourceCode = R"( contract test { function f() { - block.blockhash(3); + blockhash(3); } } )"; - bytes code = compileFirstExpression(sourceCode, {}, {}, - {make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::Block))}); + + auto blockhashFun = make_shared<FunctionType>(strings{"uint256"}, strings{"bytes32"}, + FunctionType::Kind::BlockHash, false, StateMutability::View); + + bytes code = compileFirstExpression(sourceCode, {}, {}, {make_shared<MagicVariableDeclaration>("blockhash", blockhashFun)}); bytes expectation({byte(Instruction::PUSH1), 0x03, byte(Instruction::BLOCKHASH)}); diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index e30ea8a4..c5abac03 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -43,229 +43,6 @@ namespace test BOOST_FIXTURE_TEST_SUITE(SolidityNameAndTypeResolution, AnalysisFramework) - -BOOST_AUTO_TEST_CASE(double_function_declaration) -{ - char const* text = R"( - contract test { - function fun() public { } - function fun() public { } - } - )"; - CHECK_ERROR(text, DeclarationError, "Function with same name and arguments defined twice."); -} - -BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope) -{ - string text = R"( - contract test { - function f() pure public { - { uint x; } - { uint x; } - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier already declared"); -} - -BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_050) -{ - string text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - { uint x; } - { uint x; } - } - } - )"; - CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{ - "Unused local variable", - "Unused local variable" - })); -} - -BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_activation) -{ - string text = R"( - contract test { - function f() pure public { - { uint x; } - uint x; - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier already declared"); -} - -BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_activation_050) -{ - string text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - { uint x; } - uint x; - } - } - )"; - CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{ - "Unused local variable", - "Unused local variable" - })); -} -BOOST_AUTO_TEST_CASE(scoping_old) -{ - char const* text = R"( - contract test { - function f() pure public { - x = 4; - uint256 x = 2; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(scoping) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() public { - { - uint256 x; - } - x = 2; - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); -} - -BOOST_AUTO_TEST_CASE(scoping_activation_old) -{ - char const* text = R"( - contract test { - function f() pure public { - x = 3; - uint x; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(scoping_activation) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - x = 3; - uint x; - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); -} - -BOOST_AUTO_TEST_CASE(scoping_self_use) -{ - char const* text = R"( - contract test { - function f() pure public { - uint a = a; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(scoping_self_use_050) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - uint a = a; - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); -} - -BOOST_AUTO_TEST_CASE(scoping_for) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - for (uint x = 0; x < 10; x ++){ - x = 2; - } - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(scoping_for2) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - for (uint x = 0; x < 10; x ++) - x = 2; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(scoping_for3) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - for (uint x = 0; x < 10; x ++){ - x = 2; - } - x = 4; - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); -} - -BOOST_AUTO_TEST_CASE(scoping_for_decl_in_body) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - for (;; y++){ - uint y = 3; - } - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); -} - -BOOST_AUTO_TEST_CASE(name_shadowing) -{ - char const* text = R"( - contract test { - uint256 variable; - function f() public { uint32 variable; variable = 2; } - } - )"; - CHECK_SUCCESS(text); -} - BOOST_AUTO_TEST_CASE(name_references) { char const* text = R"( @@ -8569,6 +8346,33 @@ BOOST_AUTO_TEST_CASE(require_visibility_specifiers) CHECK_ERROR(text, SyntaxError, "No visibility specified."); } +BOOST_AUTO_TEST_CASE(blockhash) +{ + char const* code = R"( + contract C { + function f() public view returns (bytes32) { + return block.blockhash(3); + } + } + )"; + 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 a6ce6d91..cd0a0b01 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -25,6 +25,7 @@ #include <boost/test/unit_test.hpp> #include <string> +#include <tuple> using namespace std; @@ -111,6 +112,7 @@ BOOST_AUTO_TEST_CASE(environment_access) "block.difficulty", "block.number", "block.gaslimit", + "blockhash(7)", "gasleft()", "msg.gas", "msg.value", @@ -120,11 +122,12 @@ BOOST_AUTO_TEST_CASE(environment_access) "this", "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<string> pure{ "msg.data", "msg.data[0]", "msg.sig", - "block.blockhash", // Not evaluating the function "msg", "block", "tx" @@ -132,20 +135,32 @@ BOOST_AUTO_TEST_CASE(environment_access) 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<std::string>{ - "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<std::string>{ + "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<std::string>{ + "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) diff --git a/test/libsolidity/syntaxTests/scoping/double_function_declaration.sol b/test/libsolidity/syntaxTests/scoping/double_function_declaration.sol new file mode 100644 index 00000000..2841fd38 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/double_function_declaration.sol @@ -0,0 +1,6 @@ +contract test { + function fun() public { } + function fun() public { } +} +// ---- +// DeclarationError: Function with same name and arguments defined twice. diff --git a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope.sol b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope.sol new file mode 100644 index 00000000..ea61d0f3 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope.sol @@ -0,0 +1,8 @@ +contract test { + function f() pure public { + { uint x; } + { uint x; } + } +} +// ---- +// DeclarationError: Identifier already declared. diff --git a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_050.sol b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_050.sol new file mode 100644 index 00000000..22195963 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_050.sol @@ -0,0 +1,10 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + { uint x; } + { uint x; } + } +} +// ---- +// Warning: Unused local variable. +// Warning: Unused local variable. diff --git a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation.sol b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation.sol new file mode 100644 index 00000000..6af89c93 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation.sol @@ -0,0 +1,8 @@ +contract test { + function f() pure public { + { uint x; } + uint x; + } +} +// ---- +// DeclarationError: Identifier already declared. diff --git a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation_050.sol b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation_050.sol new file mode 100644 index 00000000..73cddfed --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation_050.sol @@ -0,0 +1,10 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + { uint x; } + uint x; + } +} +// ---- +// Warning: Unused local variable. +// Warning: Unused local variable. diff --git a/test/libsolidity/syntaxTests/scoping/name_shadowing.sol b/test/libsolidity/syntaxTests/scoping/name_shadowing.sol new file mode 100644 index 00000000..d16877f9 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/name_shadowing.sol @@ -0,0 +1,7 @@ +contract test { + uint256 variable; + function f() pure public { uint32 variable; variable = 2; } +} +// ---- +// Warning: This declaration shadows an existing declaration. + diff --git a/test/libsolidity/syntaxTests/scoping/scoping.sol b/test/libsolidity/syntaxTests/scoping/scoping.sol new file mode 100644 index 00000000..f47a3e99 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping.sol @@ -0,0 +1,11 @@ +pragma experimental "v0.5.0"; +contract test { + function f() public { + { + uint256 x; + } + x = 2; + } +} +// ---- +// DeclarationError: Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/scoping/scoping_activation.sol b/test/libsolidity/syntaxTests/scoping/scoping_activation.sol new file mode 100644 index 00000000..0ed74a00 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_activation.sol @@ -0,0 +1,9 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + x = 3; + uint x; + } +} +// ---- +// DeclarationError: Undeclared identifier. Did you mean "x"? diff --git a/test/libsolidity/syntaxTests/scoping/scoping_activation_old.sol b/test/libsolidity/syntaxTests/scoping/scoping_activation_old.sol new file mode 100644 index 00000000..d893a889 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_activation_old.sol @@ -0,0 +1,6 @@ +contract test { + function f() pure public { + x = 3; + uint x; + } +} diff --git a/test/libsolidity/syntaxTests/scoping/scoping_for.sol b/test/libsolidity/syntaxTests/scoping/scoping_for.sol new file mode 100644 index 00000000..6e5b7095 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_for.sol @@ -0,0 +1,8 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + for (uint x = 0; x < 10; x ++){ + x = 2; + } + } +} diff --git a/test/libsolidity/syntaxTests/scoping/scoping_for2.sol b/test/libsolidity/syntaxTests/scoping/scoping_for2.sol new file mode 100644 index 00000000..eb74b8ab --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_for2.sol @@ -0,0 +1,7 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + for (uint x = 0; x < 10; x ++) + x = 2; + } +} diff --git a/test/libsolidity/syntaxTests/scoping/scoping_for3.sol b/test/libsolidity/syntaxTests/scoping/scoping_for3.sol new file mode 100644 index 00000000..9bc7d569 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_for3.sol @@ -0,0 +1,11 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + for (uint x = 0; x < 10; x ++){ + x = 2; + } + x = 4; + } +} +// ---- +// DeclarationError: Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/scoping/scoping_for_decl_in_body.sol b/test/libsolidity/syntaxTests/scoping/scoping_for_decl_in_body.sol new file mode 100644 index 00000000..07503983 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_for_decl_in_body.sol @@ -0,0 +1,10 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + for (;; y++){ + uint y = 3; + } + } +} +// ---- +// DeclarationError: Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/scoping/scoping_old.sol b/test/libsolidity/syntaxTests/scoping/scoping_old.sol new file mode 100644 index 00000000..83f6b60b --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_old.sol @@ -0,0 +1,6 @@ +contract test { + function f() pure public { + x = 4; + uint256 x = 2; + } +} diff --git a/test/libsolidity/syntaxTests/scoping/scoping_self_use.sol b/test/libsolidity/syntaxTests/scoping/scoping_self_use.sol new file mode 100644 index 00000000..9e2c0171 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_self_use.sol @@ -0,0 +1,5 @@ +contract test { + function f() pure public { + uint a = a; + } +} diff --git a/test/libsolidity/syntaxTests/scoping/scoping_self_use_050.sol b/test/libsolidity/syntaxTests/scoping/scoping_self_use_050.sol new file mode 100644 index 00000000..e942020e --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_self_use_050.sol @@ -0,0 +1,8 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + uint a = a; + } +} +// ---- +// DeclarationError: Undeclared identifier. Did you mean "a"? |