diff options
Diffstat (limited to 'test/libsolidity')
-rw-r--r-- | test/libsolidity/ASTJSON.cpp | 44 | ||||
-rw-r--r-- | test/libsolidity/AnalysisFramework.cpp | 3 | ||||
-rw-r--r-- | test/libsolidity/Assembly.cpp | 17 | ||||
-rw-r--r-- | test/libsolidity/GasMeter.cpp | 17 | ||||
-rw-r--r-- | test/libsolidity/Imports.cpp | 50 | ||||
-rw-r--r-- | test/libsolidity/InlineAssembly.cpp | 26 | ||||
-rw-r--r-- | test/libsolidity/JSONCompiler.cpp | 6 | ||||
-rw-r--r-- | test/libsolidity/Metadata.cpp | 9 | ||||
-rw-r--r-- | test/libsolidity/SMTChecker.cpp | 13 | ||||
-rw-r--r-- | test/libsolidity/SolidityABIJSON.cpp | 7 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 248 | ||||
-rw-r--r-- | test/libsolidity/SolidityExecutionFramework.h | 24 | ||||
-rw-r--r-- | test/libsolidity/SolidityExpressionCompiler.cpp | 56 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 907 | ||||
-rw-r--r-- | test/libsolidity/SolidityNatspecJSON.cpp | 7 | ||||
-rw-r--r-- | test/libsolidity/SolidityOptimizer.cpp | 4 | ||||
-rw-r--r-- | test/libsolidity/SolidityParser.cpp | 43 | ||||
-rw-r--r-- | test/libsolidity/SolidityTypes.cpp | 15 | ||||
-rw-r--r-- | test/libsolidity/StandardCompiler.cpp | 253 | ||||
-rw-r--r-- | test/libsolidity/ViewPureChecker.cpp | 20 |
20 files changed, 1666 insertions, 103 deletions
diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp index 31165922..9bf60b64 100644 --- a/test/libsolidity/ASTJSON.cpp +++ b/test/libsolidity/ASTJSON.cpp @@ -20,12 +20,16 @@ * Tests for the json ast output. */ -#include <string> -#include <boost/test/unit_test.hpp> +#include <test/TestHelper.h> + #include <libsolidity/interface/Exceptions.h> #include <libsolidity/interface/CompilerStack.h> #include <libsolidity/ast/ASTJsonConverter.h> +#include <boost/test/unit_test.hpp> + +#include <string> + using namespace std; namespace dev @@ -41,6 +45,7 @@ BOOST_AUTO_TEST_CASE(smoke_test) { CompilerStack c; c.addSource("a", "contract C {}"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; @@ -52,6 +57,7 @@ BOOST_AUTO_TEST_CASE(source_location) { CompilerStack c; c.addSource("a", "contract C { function f() { var x = 2; x++; } }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; @@ -66,6 +72,7 @@ BOOST_AUTO_TEST_CASE(inheritance_specifier) { CompilerStack c; c.addSource("a", "contract C1 {} contract C2 is C1 {}"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; @@ -81,6 +88,7 @@ BOOST_AUTO_TEST_CASE(using_for_directive) { CompilerStack c; c.addSource("a", "library L {} contract C { using L for uint; }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; @@ -98,6 +106,7 @@ BOOST_AUTO_TEST_CASE(enum_value) { CompilerStack c; c.addSource("a", "contract C { enum E { A, B } }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; @@ -115,6 +124,7 @@ BOOST_AUTO_TEST_CASE(modifier_definition) { CompilerStack c; c.addSource("a", "contract C { modifier M(uint i) { _; } function F() M(1) {} }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; @@ -129,6 +139,7 @@ BOOST_AUTO_TEST_CASE(modifier_invocation) { CompilerStack c; c.addSource("a", "contract C { modifier M(uint i) { _; } function F() M(1) {} }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; @@ -145,6 +156,7 @@ BOOST_AUTO_TEST_CASE(event_definition) { CompilerStack c; c.addSource("a", "contract C { event E(); }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; @@ -159,6 +171,7 @@ BOOST_AUTO_TEST_CASE(array_type_name) { CompilerStack c; c.addSource("a", "contract C { uint[] i; }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; @@ -172,6 +185,7 @@ BOOST_AUTO_TEST_CASE(placeholder_statement) { CompilerStack c; c.addSource("a", "contract C { modifier M { _; } }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; @@ -185,6 +199,7 @@ BOOST_AUTO_TEST_CASE(non_utf8) { CompilerStack c; c.addSource("a", "contract C { function f() { var x = hex\"ff\"; } }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; @@ -204,6 +219,7 @@ BOOST_AUTO_TEST_CASE(function_type) "contract C { function f(function() external payable returns (uint) x) " "returns (function() external constant returns (uint)) {} }" ); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; @@ -237,16 +253,32 @@ BOOST_AUTO_TEST_CASE(documentation) " and has a line-breaking comment.*/" "contract C {}" ); + c.addSource("c", + "contract C {" + " /** Some comment on Evt.*/ event Evt();" + " /** Some comment on mod.*/ modifier mod() { _; }" + " /** Some comment on fn.*/ function fn() public {}" + "}" + ); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 0; sourceIndices["b"] = 1; + sourceIndices["c"] = 2; Json::Value astJsonA = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); Json::Value documentationA = astJsonA["children"][0]["attributes"]["documentation"]; BOOST_CHECK_EQUAL(documentationA, "This contract is empty"); Json::Value astJsonB = ASTJsonConverter(true, sourceIndices).toJson(c.ast("b")); Json::Value documentationB = astJsonB["children"][0]["attributes"]["documentation"]; BOOST_CHECK_EQUAL(documentationB, "This contract is empty and has a line-breaking comment."); + Json::Value astJsonC = ASTJsonConverter(true, sourceIndices).toJson(c.ast("c")); + Json::Value documentationC0 = astJsonC["children"][0]["children"][0]["attributes"]["documentation"]; + Json::Value documentationC1 = astJsonC["children"][0]["children"][1]["attributes"]["documentation"]; + Json::Value documentationC2 = astJsonC["children"][0]["children"][2]["attributes"]["documentation"]; + BOOST_CHECK_EQUAL(documentationC0, "Some comment on Evt."); + BOOST_CHECK_EQUAL(documentationC1, "Some comment on mod."); + BOOST_CHECK_EQUAL(documentationC2, "Some comment on fn."); //same tests for non-legacy mode astJsonA = ASTJsonConverter(false, sourceIndices).toJson(c.ast("a")); documentationA = astJsonA["nodes"][0]["documentation"]; @@ -254,7 +286,13 @@ BOOST_AUTO_TEST_CASE(documentation) astJsonB = ASTJsonConverter(false, sourceIndices).toJson(c.ast("b")); documentationB = astJsonB["nodes"][0]["documentation"]; BOOST_CHECK_EQUAL(documentationB, "This contract is empty and has a line-breaking comment."); - + astJsonC = ASTJsonConverter(false, sourceIndices).toJson(c.ast("c")); + documentationC0 = astJsonC["nodes"][0]["nodes"][0]["documentation"]; + documentationC1 = astJsonC["nodes"][0]["nodes"][1]["documentation"]; + documentationC2 = astJsonC["nodes"][0]["nodes"][2]["documentation"]; + BOOST_CHECK_EQUAL(documentationC0, "Some comment on Evt."); + BOOST_CHECK_EQUAL(documentationC1, "Some comment on mod."); + BOOST_CHECK_EQUAL(documentationC2, "Some comment on fn."); } diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp index a27e3222..7c335a48 100644 --- a/test/libsolidity/AnalysisFramework.cpp +++ b/test/libsolidity/AnalysisFramework.cpp @@ -20,6 +20,8 @@ #include <test/libsolidity/AnalysisFramework.h> +#include <test/TestHelper.h> + #include <libsolidity/interface/CompilerStack.h> #include <libsolidity/interface/SourceReferenceFormatter.h> @@ -46,6 +48,7 @@ AnalysisFramework::parseAnalyseAndReturnError( { m_compiler.reset(); m_compiler.addSource("", _insertVersionPragma ? "pragma solidity >=0.0;\n" + _source : _source); + m_compiler.setEVMVersion(dev::test::Options::get().evmVersion()); if (!m_compiler.parse()) { BOOST_ERROR("Parsing contract failed in analysis test suite:" + formatErrors()); diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 59af6d41..aff610a4 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -20,11 +20,11 @@ * Unit tests for Assembly Items from evmasm/Assembly.h */ -#include <string> -#include <iostream> -#include <boost/test/unit_test.hpp> +#include <test/TestHelper.h> + #include <libevmasm/SourceLocation.h> #include <libevmasm/Assembly.h> + #include <libsolidity/parsing/Scanner.h> #include <libsolidity/parsing/Parser.h> #include <libsolidity/analysis/NameAndTypeResolver.h> @@ -33,6 +33,11 @@ #include <libsolidity/analysis/TypeChecker.h> #include <libsolidity/interface/ErrorReporter.h> +#include <boost/test/unit_test.hpp> + +#include <string> +#include <iostream> + using namespace std; using namespace dev::eth; @@ -46,7 +51,7 @@ namespace test namespace { -eth::AssemblyItems compileContract(const string& _sourceCode) +eth::AssemblyItems compileContract(string const& _sourceCode) { ErrorList errors; ErrorReporter errorReporter(errors); @@ -69,7 +74,7 @@ eth::AssemblyItems compileContract(const string& _sourceCode) for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) { - TypeChecker checker(errorReporter); + TypeChecker checker(dev::test::Options::get().evmVersion(), errorReporter); BOOST_REQUIRE_NO_THROW(checker.checkTypeRequirements(*contract)); if (!Error::containsOnlyWarnings(errorReporter.errors())) return AssemblyItems(); @@ -77,7 +82,7 @@ eth::AssemblyItems compileContract(const string& _sourceCode) for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) { - Compiler compiler; + Compiler compiler(dev::test::Options::get().evmVersion()); compiler.compileContract(*contract, map<ContractDefinition const*, Assembly const*>{}, bytes()); return compiler.runtimeAssemblyItems(); diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index 86e8201b..fd2017f9 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -49,13 +49,14 @@ public: m_compiler.reset(false); m_compiler.addSource("", "pragma solidity >=0.0;\n" + _sourceCode); m_compiler.setOptimiserSettings(dev::test::Options::get().optimize); + m_compiler.setEVMVersion(m_evmVersion); BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed"); AssemblyItems const* items = m_compiler.runtimeAssemblyItems(m_compiler.lastContractName()); ASTNode const& sourceUnit = m_compiler.ast(""); BOOST_REQUIRE(items != nullptr); m_gasCosts = GasEstimator::breakToStatementLevel( - GasEstimator::structuralEstimation(*items, vector<ASTNode const*>({&sourceUnit})), + GasEstimator(dev::test::Options::get().evmVersion()).structuralEstimation(*items, vector<ASTNode const*>({&sourceUnit})), {&sourceUnit} ); } @@ -64,7 +65,7 @@ public: { compileAndRun(_sourceCode); auto state = make_shared<KnownState>(); - PathGasMeter meter(*m_compiler.assemblyItems(m_compiler.lastContractName())); + PathGasMeter meter(*m_compiler.assemblyItems(m_compiler.lastContractName()), dev::test::Options::get().evmVersion()); GasMeter::GasConsumption gas = meter.estimateMax(0, state); u256 bytecodeSize(m_compiler.runtimeObject(m_compiler.lastContractName()).bytecode.size()); // costs for deployment @@ -73,7 +74,7 @@ public: gas += gasForTransaction(m_compiler.object(m_compiler.lastContractName()).bytecode, true); BOOST_REQUIRE(!gas.isInfinite); - BOOST_CHECK(gas.value == m_gasUsed); + BOOST_CHECK_EQUAL(gas.value, m_gasUsed); } /// Compares the gas computed by PathGasMeter for the given signature (but unknown arguments) @@ -90,12 +91,12 @@ public: gas = max(gas, gasForTransaction(hash.asBytes() + arguments, false)); } - gas += GasEstimator::functionalEstimation( + gas += GasEstimator(dev::test::Options::get().evmVersion()).functionalEstimation( *m_compiler.runtimeAssemblyItems(m_compiler.lastContractName()), _sig ); BOOST_REQUIRE(!gas.isInfinite); - BOOST_CHECK(gas.value == m_gasUsed); + BOOST_CHECK_EQUAL(gas.value, m_gasUsed); } static GasMeter::GasConsumption gasForTransaction(bytes const& _data, bool _isCreation) @@ -136,8 +137,10 @@ BOOST_AUTO_TEST_CASE(non_overlapping_filtered_costs) { BOOST_CHECK_MESSAGE(false, "Source locations should not overlap!"); auto scannerFromSource = [&](string const& _sourceName) -> Scanner const& { return m_compiler.scanner(_sourceName); }; - SourceReferenceFormatter::printSourceLocation(cout, &first->first->location(), scannerFromSource); - SourceReferenceFormatter::printSourceLocation(cout, &second->first->location(), scannerFromSource); + SourceReferenceFormatter formatter(cout, scannerFromSource); + + formatter.printSourceLocation(&first->first->location()); + formatter.printSourceLocation(&second->first->location()); } } } diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp index 03287b28..bc81b3b1 100644 --- a/test/libsolidity/Imports.cpp +++ b/test/libsolidity/Imports.cpp @@ -21,6 +21,7 @@ */ #include <test/libsolidity/ErrorCheck.h> +#include <test/TestHelper.h> #include <libsolidity/interface/Exceptions.h> #include <libsolidity/interface/CompilerStack.h> @@ -44,6 +45,7 @@ BOOST_AUTO_TEST_CASE(smoke_test) { CompilerStack c; c.addSource("a", "contract C {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -52,6 +54,7 @@ BOOST_AUTO_TEST_CASE(regular_import) CompilerStack c; c.addSource("a", "contract C {} pragma solidity >=0.0;"); c.addSource("b", "import \"a\"; contract D is C {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -60,6 +63,7 @@ BOOST_AUTO_TEST_CASE(import_does_not_clutter_importee) CompilerStack c; c.addSource("a", "contract C { D d; } pragma solidity >=0.0;"); c.addSource("b", "import \"a\"; contract D is C {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); } @@ -69,6 +73,7 @@ BOOST_AUTO_TEST_CASE(import_is_transitive) c.addSource("a", "contract C { } pragma solidity >=0.0;"); c.addSource("b", "import \"a\"; pragma solidity >=0.0;"); c.addSource("c", "import \"b\"; contract D is C {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -77,6 +82,7 @@ BOOST_AUTO_TEST_CASE(circular_import) CompilerStack c; c.addSource("a", "import \"b\"; contract C { D d; } pragma solidity >=0.0;"); c.addSource("b", "import \"a\"; contract D { C c; } pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -86,6 +92,7 @@ BOOST_AUTO_TEST_CASE(relative_import) c.addSource("a", "import \"./dir/b\"; contract A is B {} pragma solidity >=0.0;"); c.addSource("dir/b", "contract B {} pragma solidity >=0.0;"); c.addSource("dir/c", "import \"../a\"; contract C is A {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -94,6 +101,7 @@ BOOST_AUTO_TEST_CASE(relative_import_multiplex) CompilerStack c; c.addSource("a", "contract A {} pragma solidity >=0.0;"); c.addSource("dir/a/b/c", "import \"../../.././a\"; contract B is A {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -102,6 +110,7 @@ BOOST_AUTO_TEST_CASE(simple_alias) CompilerStack c; c.addSource("a", "contract A {} pragma solidity >=0.0;"); c.addSource("dir/a/b/c", "import \"../../.././a\" as x; contract B is x.A { function() { x.A r = x.A(20); } } pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -111,6 +120,7 @@ BOOST_AUTO_TEST_CASE(library_name_clash) c.addSource("a", "library A {} pragma solidity >=0.0;"); c.addSource("b", "library A {} pragma solidity >=0.0;"); c.addSource("c", "import {A} from \"./a\"; import {A} from \"./b\";"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); } @@ -119,6 +129,7 @@ BOOST_AUTO_TEST_CASE(library_name_clash_with_contract) CompilerStack c; c.addSource("a", "contract A {} pragma solidity >=0.0;"); c.addSource("b", "library A {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -128,6 +139,7 @@ BOOST_AUTO_TEST_CASE(complex_import) c.addSource("a", "contract A {} contract B {} contract C { struct S { uint a; } } pragma solidity >=0.0;"); c.addSource("b", "import \"a\" as x; import {B as b, C as c, C} from \"a\"; " "contract D is b { function f(c.S var1, x.C.S var2, C.S var3) internal {} } pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -136,14 +148,19 @@ BOOST_AUTO_TEST_CASE(name_clash_in_import) CompilerStack c; c.addSource("a", "contract A {} pragma solidity >=0.0;"); c.addSource("b", "import \"a\"; contract A {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); c.addSource("b", "import \"a\" as A; contract A {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); c.addSource("b", "import {A as b} from \"a\"; contract b {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); c.addSource("b", "import {A} from \"a\"; contract A {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); c.addSource("b", "import {A} from \"a\"; contract B {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -155,6 +172,7 @@ BOOST_AUTO_TEST_CASE(remappings) c.addSource("b", "import \"t/tee.sol\"; contract A is Tee {} pragma solidity >=0.0;"); c.addSource("s_1.4.6/s.sol", "contract S {} pragma solidity >=0.0;"); c.addSource("Tee/tee.sol", "contract Tee {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -166,6 +184,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings) c.addSource("b/b.sol", "import \"s/s.sol\"; contract B is SSeven {} pragma solidity >=0.0;"); c.addSource("s_1.4.6/s.sol", "contract SSix {} pragma solidity >=0.0;"); c.addSource("s_1.4.7/s.sol", "contract SSeven {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -174,6 +193,7 @@ BOOST_AUTO_TEST_CASE(filename_with_period) CompilerStack c; c.addSource("a/a.sol", "import \".b.sol\"; contract A is B {} pragma solidity >=0.0;"); c.addSource("a/.b.sol", "contract B {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); } @@ -185,6 +205,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings_ensure_default_and_module_pres c.addSource("vendor/bar/bar.sol", "import \"foo/foo.sol\"; contract Bar {Foo1 foo;} pragma solidity >=0.0;"); c.addSource("vendor/foo_1.0.0/foo.sol", "contract Foo1 {} pragma solidity >=0.0;"); c.addSource("vendor/foo_2.0.0/foo.sol", "contract Foo2 {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -196,6 +217,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings_order_independent) c.addSource("a/b/main.sol", "import \"x/y/z/z.sol\"; contract Main is E {} pragma solidity >=0.0;"); c.addSource("d/z.sol", "contract D {} pragma solidity >=0.0;"); c.addSource("e/y/z/z.sol", "contract E {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); CompilerStack d; d.setRemappings(vector<string>{"a/b:x=e", "a:x/y/z=d"}); @@ -203,6 +225,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings_order_independent) d.addSource("a/b/main.sol", "import \"x/y/z/z.sol\"; contract Main is E {} pragma solidity >=0.0;"); d.addSource("d/z.sol", "contract D {} pragma solidity >=0.0;"); d.addSource("e/y/z/z.sol", "contract E {} pragma solidity >=0.0;"); + d.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(d.compile()); } @@ -212,6 +235,7 @@ BOOST_AUTO_TEST_CASE(shadowing_via_import) c.addSource("a", "library A {} pragma solidity >=0.0;"); c.addSource("b", "library A {} pragma solidity >=0.0;"); c.addSource("c", "import {A} from \"./a\"; import {A} from \"./b\";"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); } @@ -225,6 +249,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_imports) contract C { } )"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); size_t errorCount = 0; for (auto const& e: c.errors()) @@ -251,6 +276,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_multiple_imports) contract C { } )"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); auto numErrors = c.errors().size(); // Sometimes we get the prerelease warning, sometimes not. @@ -266,7 +292,29 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_multiple_imports) } } - +BOOST_AUTO_TEST_CASE(shadowing_builtins_with_alias) +{ + CompilerStack c; + c.addSource("B.sol", "contract C {} pragma solidity >=0.0;"); + c.addSource("b", R"( + pragma solidity >=0.0; + import {C as msg} from "B.sol"; + )"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); + BOOST_CHECK(c.compile()); + auto numErrors = c.errors().size(); + // Sometimes we get the prerelease warning, sometimes not. + BOOST_CHECK(1 <= numErrors && numErrors <= 2); + for (auto const& e: c.errors()) + { + string const* msg = e->comment(); + BOOST_REQUIRE(msg); + BOOST_CHECK( + msg->find("pre-release") != string::npos || + msg->find("shadows a builtin symbol") != string::npos + ); + } +} BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index b09eb261..a4dcc4d5 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -55,7 +55,7 @@ boost::optional<Error> parseAndReturnFirstError( AssemblyStack::Machine _machine = AssemblyStack::Machine::EVM ) { - AssemblyStack stack(_language); + AssemblyStack stack(dev::test::Options::get().evmVersion(), _language); bool success = false; try { @@ -117,7 +117,7 @@ Error expectError( void parsePrintCompare(string const& _source, bool _canWarn = false) { - AssemblyStack stack; + AssemblyStack stack(dev::test::Options::get().evmVersion()); BOOST_REQUIRE(stack.parseAndAnalyze("", _source)); if (_canWarn) BOOST_REQUIRE(Error::containsOnlyWarnings(stack.errors())); @@ -168,6 +168,11 @@ BOOST_AUTO_TEST_CASE(smoke_test) BOOST_CHECK(successParse("{ }")); } +BOOST_AUTO_TEST_CASE(surplus_input) +{ + CHECK_PARSE_ERROR("{ } { }", ParserError, "Expected token EOS"); +} + BOOST_AUTO_TEST_CASE(simple_instructions) { BOOST_CHECK(successParse("{ dup1 dup1 mul dup1 sub pop }")); @@ -385,6 +390,7 @@ BOOST_AUTO_TEST_CASE(number_literals) CHECK_PARSE_ERROR("{ let x := .1 }", ParserError, "Invalid number literal."); CHECK_PARSE_ERROR("{ let x := 1e5 }", ParserError, "Invalid number literal."); CHECK_PARSE_ERROR("{ let x := 67.235 }", ParserError, "Invalid number literal."); + CHECK_STRICT_ERROR("{ let x := 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff }", TypeError, "Number literal too large (> 256 bits)"); } BOOST_AUTO_TEST_CASE(function_definitions) @@ -561,7 +567,7 @@ BOOST_AUTO_TEST_CASE(print_string_literal_unicode) { string source = "{ let x := \"\\u1bac\" }"; string parsed = "{\n let x := \"\\xe1\\xae\\xac\"\n}"; - AssemblyStack stack; + AssemblyStack stack(dev::test::Options::get().evmVersion()); BOOST_REQUIRE(stack.parseAndAnalyze("", source)); BOOST_REQUIRE(stack.errors().empty()); BOOST_CHECK_EQUAL(stack.print(), parsed); @@ -768,6 +774,20 @@ BOOST_AUTO_TEST_CASE(create2) BOOST_CHECK(successAssemble("{ pop(create2(10, 0x123, 32, 64)) }")); } +BOOST_AUTO_TEST_CASE(shift) +{ + BOOST_CHECK(successAssemble("{ pop(shl(10, 32)) }")); + BOOST_CHECK(successAssemble("{ pop(shr(10, 32)) }")); + BOOST_CHECK(successAssemble("{ pop(sar(10, 32)) }")); +} + +BOOST_AUTO_TEST_CASE(shift_constantinople_warning) +{ + CHECK_PARSE_WARNING("{ pop(shl(10, 32)) }", Warning, "The \"shl\" instruction is only available for Constantinople-compatible VMs."); + CHECK_PARSE_WARNING("{ pop(shr(10, 32)) }", Warning, "The \"shr\" instruction is only available for Constantinople-compatible VMs."); + CHECK_PARSE_WARNING("{ pop(sar(10, 32)) }", Warning, "The \"sar\" instruction is only available for Constantinople-compatible VMs."); +} + BOOST_AUTO_TEST_CASE(jump_warning) { CHECK_PARSE_WARNING("{ 1 jump }", Warning, "Jump instructions"); diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp index 0c904c77..285c5604 100644 --- a/test/libsolidity/JSONCompiler.cpp +++ b/test/libsolidity/JSONCompiler.cpp @@ -44,7 +44,7 @@ Json::Value compileSingle(string const& _input) { string output(compileJSON(_input.c_str(), dev::test::Options::get().optimize)); Json::Value ret; - BOOST_REQUIRE(Json::Reader().parse(output, ret, false)); + BOOST_REQUIRE(jsonParseStrict(output, ret)); return ret; } @@ -56,7 +56,7 @@ Json::Value compileMulti(string const& _input, bool _callback) compileJSONMulti(_input.c_str(), dev::test::Options::get().optimize) ); Json::Value ret; - BOOST_REQUIRE(Json::Reader().parse(output, ret, false)); + BOOST_REQUIRE(jsonParseStrict(output, ret)); return ret; } @@ -64,7 +64,7 @@ Json::Value compile(string const& _input) { string output(compileStandard(_input.c_str(), NULL)); Json::Value ret; - BOOST_REQUIRE(Json::Reader().parse(output, ret, false)); + BOOST_REQUIRE(jsonParseStrict(output, ret)); return ret; } diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index efe8faff..f1edeeb7 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -23,6 +23,7 @@ #include "../TestHelper.h" #include <libsolidity/interface/CompilerStack.h> #include <libdevcore/SwarmHash.h> +#include <libdevcore/JSON.h> namespace dev { @@ -45,6 +46,7 @@ BOOST_AUTO_TEST_CASE(metadata_stamp) )"; CompilerStack compilerStack; compilerStack.addSource("", std::string(sourceCode)); + compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); compilerStack.setOptimiserSettings(dev::test::Options::get().optimize); BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); bytes const& bytecode = compilerStack.runtimeObject("test").bytecode; @@ -71,6 +73,7 @@ BOOST_AUTO_TEST_CASE(metadata_stamp_experimental) )"; CompilerStack compilerStack; compilerStack.addSource("", std::string(sourceCode)); + compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); compilerStack.setOptimiserSettings(dev::test::Options::get().optimize); BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); bytes const& bytecode = compilerStack.runtimeObject("test").bytecode; @@ -105,13 +108,14 @@ BOOST_AUTO_TEST_CASE(metadata_relevant_sources) } )"; compilerStack.addSource("B", std::string(sourceCode)); + compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); compilerStack.setOptimiserSettings(dev::test::Options::get().optimize); BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); std::string const& serialisedMetadata = compilerStack.metadata("A"); BOOST_CHECK(dev::test::isValidMetadata(serialisedMetadata)); Json::Value metadata; - BOOST_REQUIRE(Json::Reader().parse(serialisedMetadata, metadata, false)); + BOOST_REQUIRE(jsonParseStrict(serialisedMetadata, metadata)); BOOST_CHECK_EQUAL(metadata["sources"].size(), 1); BOOST_CHECK(metadata["sources"].isMember("A")); @@ -143,13 +147,14 @@ BOOST_AUTO_TEST_CASE(metadata_relevant_sources_imports) } )"; compilerStack.addSource("C", std::string(sourceCode)); + compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); compilerStack.setOptimiserSettings(dev::test::Options::get().optimize); BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); std::string const& serialisedMetadata = compilerStack.metadata("C"); BOOST_CHECK(dev::test::isValidMetadata(serialisedMetadata)); Json::Value metadata; - BOOST_REQUIRE(Json::Reader().parse(serialisedMetadata, metadata, false)); + BOOST_REQUIRE(jsonParseStrict(serialisedMetadata, metadata)); BOOST_CHECK_EQUAL(metadata["sources"].size(), 3); BOOST_CHECK(metadata["sources"].isMember("A")); diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp index 8c955292..12b5f439 100644 --- a/test/libsolidity/SMTChecker.cpp +++ b/test/libsolidity/SMTChecker.cpp @@ -35,12 +35,6 @@ namespace test class SMTCheckerFramework: public AnalysisFramework { -public: - SMTCheckerFramework() - { - m_warningsToFilter.push_back("Experimental features are turned on."); - } - protected: virtual std::pair<SourceUnit const*, ErrorList> parseAnalyseAndReturnError( @@ -103,6 +97,7 @@ BOOST_AUTO_TEST_CASE(warn_on_struct) } )"; CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{ + "Experimental feature", "Assertion checker does not yet implement this expression.", "Assertion checker does not yet support the type of this variable." })); @@ -471,7 +466,8 @@ BOOST_AUTO_TEST_CASE(for_loop) text = R"( contract C { function f(uint x) public pure { - for (uint y = 2; x < 10; ) { + uint y; + for (y = 2; x < 10; ) { y = 3; } assert(y == 3); @@ -482,7 +478,8 @@ BOOST_AUTO_TEST_CASE(for_loop) text = R"( contract C { function f(uint x) public pure { - for (uint y = 2; x < 10; ) { + uint y; + for (y = 2; x < 10; ) { y = 3; } assert(y == 2); diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 26bfb6d0..0d471b32 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -26,7 +26,7 @@ #include <libdevcore/Exceptions.h> #include <libdevcore/SwarmHash.h> -#include <json/json.h> +#include <libdevcore/JSON.h> namespace dev { @@ -44,11 +44,13 @@ public: { m_compilerStack.reset(false); m_compilerStack.addSource("", "pragma solidity >=0.0;\n" + _code); + m_compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); + m_compilerStack.setOptimiserSettings(dev::test::Options::get().optimize); BOOST_REQUIRE_MESSAGE(m_compilerStack.parseAndAnalyze(), "Parsing contract failed"); Json::Value generatedInterface = m_compilerStack.contractABI(m_compilerStack.lastContractName()); Json::Value expectedInterface; - BOOST_REQUIRE(m_reader.parse(_expectedInterfaceString, expectedInterface)); + BOOST_REQUIRE(jsonParseStrict(_expectedInterfaceString, expectedInterface)); BOOST_CHECK_MESSAGE( expectedInterface == generatedInterface, "Expected:\n" << expectedInterface.toStyledString() << @@ -58,7 +60,6 @@ public: protected: CompilerStack m_compilerStack; - Json::Reader m_reader; }; BOOST_FIXTURE_TEST_SUITE(SolidityABIJSON, JSONInterfaceChecker) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 0611e71d..33cd1419 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -21,13 +21,20 @@ * Unit tests for the solidity expression compiler, testing the behaviour of the code. */ +#include <test/libsolidity/SolidityExecutionFramework.h> + +#include <test/TestHelper.h> + +#include <libsolidity/interface/Exceptions.h> +#include <libsolidity/interface/EVMVersion.h> + +#include <libevmasm/Assembly.h> + +#include <boost/test/unit_test.hpp> + #include <functional> #include <string> #include <tuple> -#include <boost/test/unit_test.hpp> -#include <libevmasm/Assembly.h> -#include <libsolidity/interface/Exceptions.h> -#include <test/libsolidity/SolidityExecutionFramework.h> using namespace std; using namespace std::placeholders; @@ -284,6 +291,54 @@ BOOST_AUTO_TEST_CASE(conditional_expression_functions) ABI_CHECK(callContractFunction("f(bool)", false), encodeArgs(u256(2))); } +BOOST_AUTO_TEST_CASE(C99_scoping_activation) +{ + char const* sourceCode = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public returns (uint) { + uint x = 7; + { + x = 3; // This should still assign to the outer variable + uint x; + x = 4; // This should assign to the new one + } + return x; + } + function g() pure public returns (uint x) { + x = 7; + { + x = 3; + uint x; + return x; // This returns the new variable, i.e. 0 + } + } + function h() pure public returns (uint x, uint a, uint b) { + x = 7; + { + x = 3; + a = x; // This should read from the outer + uint x = 4; + b = x; + } + } + function i() pure public returns (uint x, uint a) { + x = 7; + { + x = 3; + uint x = x; // This should read from the outer and assign to the inner + a = x; + } + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs(3)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(0)); + ABI_CHECK(callContractFunction("h()"), encodeArgs(3, 3, 4)); + ABI_CHECK(callContractFunction("i()"), encodeArgs(3, 3)); +} + BOOST_AUTO_TEST_CASE(recursive_calls) { char const* sourceCode = R"( @@ -2967,6 +3022,29 @@ BOOST_AUTO_TEST_CASE(event) } } +BOOST_AUTO_TEST_CASE(event_emit) +{ + char const* sourceCode = R"( + contract ClientReceipt { + event Deposit(address indexed _from, bytes32 indexed _id, uint _value); + function deposit(bytes32 _id) payable { + emit Deposit(msg.sender, _id, msg.value); + } + } + )"; + compileAndRun(sourceCode); + u256 value(18); + u256 id(0x1234); + callContractFunctionWithValue("deposit(bytes32)", value, id); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(value))); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 3); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,bytes32,uint256)"))); + BOOST_CHECK_EQUAL(m_logs[0].topics[1], h256(m_sender, h256::AlignRight)); + BOOST_CHECK_EQUAL(m_logs[0].topics[2], h256(id)); +} + BOOST_AUTO_TEST_CASE(event_no_arguments) { char const* sourceCode = R"( @@ -3009,6 +3087,28 @@ BOOST_AUTO_TEST_CASE(event_access_through_base_name) BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("x()"))); } +BOOST_AUTO_TEST_CASE(event_access_through_base_name_emit) +{ + char const* sourceCode = R"( + contract A { + event x(); + } + contract B is A { + function f() returns (uint) { + emit A.x(); + return 1; + } + } + )"; + compileAndRun(sourceCode); + callContractFunction("f()"); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data.empty()); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("x()"))); +} + BOOST_AUTO_TEST_CASE(events_with_same_name) { char const* sourceCode = R"( @@ -3107,6 +3207,58 @@ BOOST_AUTO_TEST_CASE(events_with_same_name_inherited) BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,uint256)"))); } +BOOST_AUTO_TEST_CASE(events_with_same_name_inherited_emit) +{ + char const* sourceCode = R"( + contract A { + event Deposit(); + } + + contract B { + event Deposit(address _addr); + } + + contract ClientReceipt is A, B { + event Deposit(address _addr, uint _amount); + function deposit() returns (uint) { + emit Deposit(); + return 1; + } + function deposit(address _addr) returns (uint) { + emit Deposit(_addr); + return 1; + } + function deposit(address _addr, uint _amount) returns (uint) { + emit Deposit(_addr, _amount); + return 1; + } + } + )"; + u160 const c_loggedAddress = m_contractAddress; + + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("deposit()"), encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data.empty()); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit()"))); + + ABI_CHECK(callContractFunction("deposit(address)", c_loggedAddress), encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(c_loggedAddress)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address)"))); + + ABI_CHECK(callContractFunction("deposit(address,uint256)", c_loggedAddress, u256(100)), encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(c_loggedAddress, 100)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,uint256)"))); +} + BOOST_AUTO_TEST_CASE(event_anonymous) { char const* sourceCode = R"( @@ -3565,8 +3717,8 @@ BOOST_AUTO_TEST_CASE(library_call_protection) } )"; compileAndRun(sourceCode, 0, "Lib"); - ABI_CHECK(callContractFunction("np(Lib.S storage)"), encodeArgs()); - ABI_CHECK(callContractFunction("v(Lib.S storage)"), encodeArgs(u160(m_sender))); + ABI_CHECK(callContractFunction("np(Lib.S storage)", 0), encodeArgs()); + ABI_CHECK(callContractFunction("v(Lib.S storage)", 0), encodeArgs(u160(m_sender))); ABI_CHECK(callContractFunction("pu()"), encodeArgs(2)); compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}}); ABI_CHECK(callContractFunction("s()"), encodeArgs(0)); @@ -5207,6 +5359,18 @@ BOOST_AUTO_TEST_CASE(super_overload) ABI_CHECK(callContractFunction("h()"), encodeArgs(2)); } +BOOST_AUTO_TEST_CASE(gasleft_shadow_resolution) +{ + char const* sourceCode = R"( + contract C { + function gasleft() returns(uint256) { return 0; } + function f() returns(uint256) { return gasleft(); } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0)); +} + BOOST_AUTO_TEST_CASE(bool_conversion) { char const* sourceCode = R"( @@ -7459,6 +7623,33 @@ BOOST_AUTO_TEST_CASE(addmod_mulmod) ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(0))); } +BOOST_AUTO_TEST_CASE(addmod_mulmod_zero) +{ + char const* sourceCode = R"( + contract C { + function f() pure returns (uint) { + addmod(1, 2, 0); + return 2; + } + function g() pure returns (uint) { + mulmod(1, 2, 0); + return 2; + } + function h() pure returns (uint) { + mulmod(0, 1, 2); + mulmod(1, 0, 2); + addmod(0, 1, 2); + addmod(1, 0, 2); + return 2; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs()); + ABI_CHECK(callContractFunction("g()"), encodeArgs()); + ABI_CHECK(callContractFunction("h()"), encodeArgs(2)); +} + BOOST_AUTO_TEST_CASE(divisiod_by_zero) { char const* sourceCode = R"( @@ -10594,6 +10785,51 @@ BOOST_AUTO_TEST_CASE(snark) BOOST_CHECK(callContractFunction("verifyTx()") == encodeArgs(true)); } +BOOST_AUTO_TEST_CASE(staticcall_for_view_and_pure) +{ + char const* sourceCode = R"( + pragma experimental "v0.5.0"; + contract C { + uint x; + function f() public returns (uint) { + x = 3; + return 1; + } + } + interface CView { + function f() view external returns (uint); + } + interface CPure { + function f() pure external returns (uint); + } + contract D { + function f() public returns (uint) { + return (new C()).f(); + } + function fview() public returns (uint) { + return (CView(new C())).f(); + } + function fpure() public returns (uint) { + return (CPure(new C())).f(); + } + } + )"; + compileAndRun(sourceCode, 0, "D"); + // This should work (called via CALL) + ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); + if (dev::test::Options::get().evmVersion().hasStaticCall()) + { + // These should throw (called via STATICCALL) + ABI_CHECK(callContractFunction("fview()"), encodeArgs()); + ABI_CHECK(callContractFunction("fpure()"), encodeArgs()); + } + else + { + ABI_CHECK(callContractFunction("fview()"), encodeArgs(1)); + ABI_CHECK(callContractFunction("fpure()"), encodeArgs(1)); + } +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index b0daaba9..12687dd1 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -52,27 +52,39 @@ public: std::map<std::string, dev::test::Address> const& _libraryAddresses = std::map<std::string, dev::test::Address>() ) override { + bytes bytecode = compileContract(_sourceCode, _contractName, _libraryAddresses); + sendMessage(bytecode + _arguments, true, _value); + return m_output; + } + + bytes compileContract( + std::string const& _sourceCode, + std::string const& _contractName = "", + std::map<std::string, dev::test::Address> const& _libraryAddresses = std::map<std::string, dev::test::Address>() + ) + { // Silence compiler version warning std::string sourceCode = "pragma solidity >=0.0;\n" + _sourceCode; m_compiler.reset(false); m_compiler.addSource("", sourceCode); m_compiler.setLibraries(_libraryAddresses); + m_compiler.setEVMVersion(m_evmVersion); m_compiler.setOptimiserSettings(m_optimize, m_optimizeRuns); if (!m_compiler.compile()) { + auto scannerFromSourceName = [&](std::string const& _sourceName) -> solidity::Scanner const& { return m_compiler.scanner(_sourceName); }; + SourceReferenceFormatter formatter(std::cerr, scannerFromSourceName); + for (auto const& error: m_compiler.errors()) - SourceReferenceFormatter::printExceptionInformation( - std::cerr, + formatter.printExceptionInformation( *error, - (error->type() == Error::Type::Warning) ? "Warning" : "Error", - [&](std::string const& _sourceName) -> solidity::Scanner const& { return m_compiler.scanner(_sourceName); } + (error->type() == Error::Type::Warning) ? "Warning" : "Error" ); BOOST_ERROR("Compiling contract failed"); } eth::LinkerObject obj = m_compiler.object(_contractName.empty() ? m_compiler.lastContractName() : _contractName); BOOST_REQUIRE(obj.linkReferences.empty()); - sendMessage(obj.bytecode + _arguments, true, _value); - return m_output; + return obj.bytecode; } protected: diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 67747386..5f044b44 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -132,7 +132,7 @@ bytes compileFirstExpression( if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) { ErrorReporter errorReporter(errors); - TypeChecker typeChecker(errorReporter); + TypeChecker typeChecker(dev::test::Options::get().evmVersion(), errorReporter); BOOST_REQUIRE(typeChecker.checkTypeRequirements(*contract)); } for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) @@ -141,7 +141,7 @@ bytes compileFirstExpression( FirstExpressionExtractor extractor(*contract); BOOST_REQUIRE(extractor.expression() != nullptr); - CompilerContext context; + CompilerContext context(dev::test::Options::get().evmVersion()); context.resetVisitedNodes(contract); context.setInheritanceHierarchy(inheritanceHierarchy); unsigned parametersSize = _localVariables.size(); // assume they are all one slot on the stack @@ -322,10 +322,10 @@ BOOST_AUTO_TEST_CASE(arithmetics) { char const* sourceCode = R"( contract test { - function f(uint y) { var x = ((((((((y ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); } + function f(uint y) { ((((((((y ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); } } )"; - bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}, {"test", "f", "x"}}); + bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}}); bytes expectation({byte(Instruction::PUSH1), 0x1, byte(Instruction::PUSH1), 0x2, byte(Instruction::PUSH1), 0x3, @@ -334,7 +334,7 @@ BOOST_AUTO_TEST_CASE(arithmetics) byte(Instruction::PUSH1), 0x6, byte(Instruction::PUSH1), 0x7, byte(Instruction::PUSH1), 0x8, - byte(Instruction::DUP10), + byte(Instruction::DUP9), byte(Instruction::XOR), byte(Instruction::AND), byte(Instruction::OR), @@ -364,13 +364,13 @@ BOOST_AUTO_TEST_CASE(unary_operators) { char const* sourceCode = R"( contract test { - function f(int y) { var x = !(~+- y == 2); } + function f(int y) { !(~+- y == 2); } } )"; - bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}, {"test", "f", "x"}}); + bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}}); bytes expectation({byte(Instruction::PUSH1), 0x2, - byte(Instruction::DUP3), + byte(Instruction::DUP2), byte(Instruction::PUSH1), 0x0, byte(Instruction::SUB), byte(Instruction::NOT), @@ -383,7 +383,7 @@ BOOST_AUTO_TEST_CASE(unary_inc_dec) { char const* sourceCode = R"( contract test { - function f(uint a) { var x = --a ^ (a-- ^ (++a ^ a++)); } + function f(uint a) returns (uint x) { x = --a ^ (a-- ^ (++a ^ a++)); } } )"; bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "x"}}); @@ -426,7 +426,10 @@ BOOST_AUTO_TEST_CASE(unary_inc_dec) byte(Instruction::POP), // second ++ // Stack here: a x a^(a+2)^(a+2) byte(Instruction::DUP3), // will change - byte(Instruction::XOR)}); + byte(Instruction::XOR), + byte(Instruction::SWAP1), + byte(Instruction::POP), + byte(Instruction::DUP1)}); // Stack here: a x a^(a+2)^(a+2)^a BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -512,6 +515,39 @@ BOOST_AUTO_TEST_CASE(blockhash) BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } +BOOST_AUTO_TEST_CASE(gas_left) +{ + char const* sourceCode = R"( + contract test { + function f() returns (uint256 val) { + return msg.gas; + } + } + )"; + bytes code = compileFirstExpression( + sourceCode, {}, {}, + {make_shared<MagicVariableDeclaration>("msg", make_shared<MagicType>(MagicType::Kind::Message))} + ); + + bytes expectation({byte(Instruction::GAS)}); + BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); + + sourceCode = R"( + contract test { + function f() returns (uint256 val) { + return gasleft(); + } + } + )"; + code = compileFirstExpression( + sourceCode, {}, {}, + {make_shared<MagicVariableDeclaration>("gasleft", make_shared<FunctionType>(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft))} + ); + + expectation = bytes({byte(Instruction::GAS)}); + BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 315c7c5f..997b610e 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -22,6 +22,8 @@ #include <test/libsolidity/AnalysisFramework.h> +#include <test/TestHelper.h> + #include <libsolidity/ast/AST.h> #include <libdevcore/SHA3.h> @@ -76,15 +78,233 @@ BOOST_AUTO_TEST_CASE(double_function_declaration) BOOST_AUTO_TEST_CASE(double_variable_declaration) { - char const* text = R"( + string text = R"( contract test { - function f() public { + function f() pure public { uint256 x; if (true) { uint256 x; } } } )"; - CHECK_ERROR(text, DeclarationError, "Identifier already declared."); + CHECK_ERROR(text, DeclarationError, "Identifier already declared"); +} + +BOOST_AUTO_TEST_CASE(double_variable_declaration_050) +{ + string text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + uint256 x; + if (true) { uint256 x; } + } + } + )"; + CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{ + "This declaration shadows an existing declaration.", + "Unused local variable", + "Unused local variable" + })); +} + +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) @@ -758,6 +978,62 @@ BOOST_AUTO_TEST_CASE(functions_with_stucts_of_non_external_types_in_interface_ne CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); } +BOOST_AUTO_TEST_CASE(returning_multi_dimensional_arrays_new_abi) +{ + char const* text = R"( + pragma experimental ABIEncoderV2; + + contract C { + function f() public pure returns (string[][]) {} + } + )"; + CHECK_WARNING(text, "Experimental features"); +} + +BOOST_AUTO_TEST_CASE(returning_multi_dimensional_arrays) +{ + char const* text = R"( + contract C { + function f() public pure returns (string[][]) {} + } + )"; + CHECK_ERROR(text, TypeError, "only supported in the new experimental ABI encoder"); +} + +BOOST_AUTO_TEST_CASE(returning_multi_dimensional_static_arrays) +{ + char const* text = R"( + contract C { + function f() public pure returns (uint[][2]) {} + } + )"; + CHECK_ERROR(text, TypeError, "only supported in the new experimental ABI encoder"); +} + +BOOST_AUTO_TEST_CASE(returning_arrays_in_structs_new_abi) +{ + char const* text = R"( + pragma experimental ABIEncoderV2; + + contract C { + struct S { string[] s; } + function f() public pure returns (S) {} + } + )"; + CHECK_WARNING(text, "Experimental features"); +} + +BOOST_AUTO_TEST_CASE(returning_arrays_in_structs_arrays) +{ + char const* text = R"( + contract C { + struct S { string[] s; } + function f() public pure returns (S x) {} + } + )"; + CHECK_ERROR(text, TypeError, "only supported in the new experimental ABI encoder"); +} + BOOST_AUTO_TEST_CASE(function_external_call_allowed_conversion) { char const* text = R"( @@ -1004,7 +1280,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation) { char const* text = R"( contract B { - function f() mod1(2, true) mod2("0123456") public { } + function f() mod1(2, true) mod2("0123456") pure public { } modifier mod1(uint a, bool b) { if (b) _; } modifier mod2(bytes7 a) { while (a == "1234567") _; } } @@ -1039,11 +1315,23 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables) { char const* text = R"( contract B { - function f() mod(x) public { uint x = 7; } + function f() mod(x) pure public { uint x = 7; } modifier mod(uint a) { if (a > 0) _; } } )"; - CHECK_SUCCESS(text); + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables050) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract B { + function f() mod(x) pure public { uint x = 7; } + modifier mod(uint a) { if (a > 0) _; } + } + )"; + CHECK_ERROR(text, DeclarationError, "Undeclared identifier."); } BOOST_AUTO_TEST_CASE(function_modifier_double_invocation) @@ -2484,6 +2772,25 @@ BOOST_AUTO_TEST_CASE(explicit_conversion_from_decimal_to_bytesxx) CHECK_SUCCESS_NO_WARNINGS(text); } +BOOST_AUTO_TEST_CASE(combining_hex_and_denomination) +{ + char const* text = R"( + contract Foo { + uint constant x = 0x01 wei; + } + )"; + CHECK_WARNING(text, "Hexadecimal numbers with unit denominations are deprecated."); + + char const* textV050 = R"( + pragma experimental "v0.5.0"; + + contract Foo { + uint constant x = 0x01 wei; + } + )"; + CHECK_ERROR(textV050, TypeError, "Hexadecimal numbers cannot be used with unit denominations."); +} + BOOST_AUTO_TEST_CASE(assigning_value_to_const_variable) { char const* text = R"( @@ -3021,6 +3328,20 @@ BOOST_AUTO_TEST_CASE(uninitialized_mapping_array_variable) CHECK_WARNING(sourceCode, "Uninitialized storage pointer"); } +BOOST_AUTO_TEST_CASE(uninitialized_mapping_array_variable_050) +{ + char const* sourceCode = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure public { + mapping(uint => uint)[] storage x; + x; + } + } + )"; + CHECK_ERROR(sourceCode, DeclarationError, "Uninitialized storage pointer"); +} + BOOST_AUTO_TEST_CASE(no_delete_on_storage_pointers) { char const* sourceCode = R"( @@ -3320,6 +3641,24 @@ BOOST_AUTO_TEST_CASE(non_initialized_references) CHECK_WARNING(text, "Uninitialized storage pointer"); } +BOOST_AUTO_TEST_CASE(non_initialized_references_050) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract c + { + struct s { + uint a; + } + function f() public { + s storage x; + } + } + )"; + + CHECK_ERROR(text, DeclarationError, "Uninitialized storage pointer"); +} + BOOST_AUTO_TEST_CASE(keccak256_with_large_integer_constant) { char const* text = R"( @@ -5607,10 +5946,11 @@ BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_negative_stack) BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_two_stack_load) { char const* text = R"( + pragma experimental "v0.5.0"; contract c { uint8 x; function f() public { - assembly { x pop } + assembly { pop(x) } } } )"; @@ -5620,6 +5960,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_two_stack_load) BOOST_AUTO_TEST_CASE(inline_assembly_in_modifier) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { modifier m { uint a = 1; @@ -5628,7 +5969,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_in_modifier) } _; } - function f() m { + function f() public m { } } )"; @@ -5638,6 +5979,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_in_modifier) BOOST_AUTO_TEST_CASE(inline_assembly_storage) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { uint x = 1; function f() public { @@ -5653,6 +5995,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage) BOOST_AUTO_TEST_CASE(inline_assembly_storage_in_modifiers) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { uint x = 1; modifier m { @@ -5661,7 +6004,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_in_modifiers) } _; } - function f() m { + function f() public m { } } )"; @@ -5671,6 +6014,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_in_modifiers) BOOST_AUTO_TEST_CASE(inline_assembly_constant_assign) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { uint constant x = 1; function f() public { @@ -5686,6 +6030,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_constant_assign) BOOST_AUTO_TEST_CASE(inline_assembly_constant_access) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { uint constant x = 1; function f() public { @@ -5701,6 +6046,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_constant_access) BOOST_AUTO_TEST_CASE(inline_assembly_local_variable_access_out_of_functions) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { function f() public { uint a; @@ -5716,6 +6062,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_local_variable_access_out_of_functions) BOOST_AUTO_TEST_CASE(inline_assembly_local_variable_access_out_of_functions_storage_ptr) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { uint[] r; function f() public { @@ -5732,6 +6079,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_local_variable_access_out_of_functions_stor BOOST_AUTO_TEST_CASE(inline_assembly_storage_variable_access_out_of_functions) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { uint a; function f() pure public { @@ -5744,9 +6092,25 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_variable_access_out_of_functions) CHECK_SUCCESS_NO_WARNINGS(text); } +BOOST_AUTO_TEST_CASE(inline_assembly_constant_variable_via_offset) +{ + char const* text = R"( + contract test { + uint constant x = 2; + function f() pure public { + assembly { + let r := x_offset + } + } + } + )"; + CHECK_ERROR(text, TypeError, "Constant variables not supported by inline assembly."); +} + BOOST_AUTO_TEST_CASE(inline_assembly_calldata_variables) { char const* text = R"( + pragma experimental "v0.5.0"; contract C { function f(bytes bytesAsCalldata) external { assembly { @@ -5758,6 +6122,182 @@ BOOST_AUTO_TEST_CASE(inline_assembly_calldata_variables) CHECK_ERROR(text, TypeError, "Call data elements cannot be accessed directly."); } +BOOST_AUTO_TEST_CASE(inline_assembly_050_literals_on_stack) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure public { + assembly { + 1 + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ + {Error::Type::SyntaxError, "are not supposed to return"}, + {Error::Type::DeclarationError, "Unbalanced stack"}, + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_literals_on_stack) +{ + char const* text = R"( + contract C { + function f() pure public { + assembly { + 1 + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ + {Error::Type::Warning, "are not supposed to return"}, + {Error::Type::DeclarationError, "Unbalanced stack"}, + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_050_bare_instructions) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() view public { + assembly { + address + pop + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ + {Error::Type::SyntaxError, "The use of non-functional"}, + {Error::Type::SyntaxError, "The use of non-functional"} + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_bare_instructions) +{ + char const* text = R"( + contract C { + function f() view public { + assembly { + address + pop + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ + {Error::Type::Warning, "The use of non-functional"}, + {Error::Type::Warning, "The use of non-functional"} + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_050_labels) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure public { + assembly { + label: + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ + {Error::Type::SyntaxError, "Jump instructions and labels are low-level"}, + {Error::Type::SyntaxError, "The use of labels is deprecated"} + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_labels) +{ + char const* text = R"( + contract C { + function f() pure public { + assembly { + label: + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ + {Error::Type::Warning, "Jump instructions and labels are low-level"}, + {Error::Type::Warning, "The use of labels is deprecated"} + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_050_jump) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure public { + assembly { + jump(2) + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ + {Error::Type::SyntaxError, "Jump instructions and labels are low-level"} + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_jump) +{ + char const* text = R"( + contract C { + function f() pure public { + assembly { + jump(2) + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ + {Error::Type::TypeError, "Function declared as pure"}, + {Error::Type::Warning, "Jump instructions and labels are low-level"} + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_050_leave_items_on_stack) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure public { + assembly { + mload(0) + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ + {Error::Type::SyntaxError, "are not supposed to return"}, + {Error::Type::DeclarationError, "Unbalanced stack"}, + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_leave_items_on_stack) +{ + char const* text = R"( + contract C { + function f() pure public { + assembly { + mload(0) + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ + {Error::Type::Warning, "are not supposed to return"}, + {Error::Type::DeclarationError, "Unbalanced stack"}, + })); +} + BOOST_AUTO_TEST_CASE(invalid_mobile_type) { char const* text = R"( @@ -6285,7 +6825,16 @@ BOOST_AUTO_TEST_CASE(warn_about_throw) } } )"; - CHECK_WARNING(text, "\"throw\" is deprecated"); + CHECK_WARNING(text, "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\""); + text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure public { + throw; + } + } + )"; + CHECK_ERROR(text, SyntaxError, "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\""); } BOOST_AUTO_TEST_CASE(bare_revert) @@ -6701,6 +7250,8 @@ BOOST_AUTO_TEST_CASE(callable_crash) BOOST_AUTO_TEST_CASE(error_transfer_non_payable_fallback) { + // This used to be a test for a.transfer to generate a warning + // because A's fallback function is not payable. char const* text = R"( contract A { function() public {} @@ -6714,12 +7265,17 @@ BOOST_AUTO_TEST_CASE(error_transfer_non_payable_fallback) } } )"; - CHECK_ERROR(text, TypeError, "Value transfer to a contract without a payable fallback function."); + CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ + {Error::Type::Warning, "Using contract member \"transfer\" inherited from the address type is deprecated"}, + {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} + })); } BOOST_AUTO_TEST_CASE(error_transfer_no_fallback) { - char const* text = R"( + // This used to be a test for a.transfer to generate a warning + // because A does not have a payable fallback function. + std::string text = R"( contract A {} contract B { @@ -6730,12 +7286,17 @@ BOOST_AUTO_TEST_CASE(error_transfer_no_fallback) } } )"; - CHECK_ERROR(text, TypeError, "Value transfer to a contract without a payable fallback function."); + CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ + {Error::Type::Warning, "Using contract member \"transfer\" inherited from the address type is deprecated"}, + {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} + })); } BOOST_AUTO_TEST_CASE(error_send_non_payable_fallback) { - char const* text = R"( + // This used to be a test for a.send to generate a warning + // because A does not have a payable fallback function. + std::string text = R"( contract A { function() public {} } @@ -6748,11 +7309,16 @@ BOOST_AUTO_TEST_CASE(error_send_non_payable_fallback) } } )"; - CHECK_ERROR(text, TypeError, "Value transfer to a contract without a payable fallback function."); + CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ + {Error::Type::Warning, "Using contract member \"send\" inherited from the address type is deprecated"}, + {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} + })); } BOOST_AUTO_TEST_CASE(does_not_error_transfer_payable_fallback) { + // This used to be a test for a.transfer to generate a warning + // because A does not have a payable fallback function. char const* text = R"( contract A { function() payable public {} @@ -6766,7 +7332,7 @@ BOOST_AUTO_TEST_CASE(does_not_error_transfer_payable_fallback) } } )"; - CHECK_SUCCESS_NO_WARNINGS(text); + CHECK_WARNING(text, "Using contract member \"transfer\" inherited from the address type is deprecated."); } BOOST_AUTO_TEST_CASE(does_not_error_transfer_regular_function) @@ -6792,11 +7358,14 @@ BOOST_AUTO_TEST_CASE(returndatacopy_as_variable) char const* text = R"( contract c { function f() public { uint returndatasize; assembly { returndatasize }}} )"; - CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ + vector<pair<Error::Type, std::string>> expectations(vector<pair<Error::Type, std::string>>{ {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"}, - {Error::Type::DeclarationError, "Unbalanced stack"}, - {Error::Type::Warning, "only available after the Metropolis"} - })); + {Error::Type::Warning, "The use of non-functional instructions is deprecated."}, + {Error::Type::DeclarationError, "Unbalanced stack"} + }); + if (!dev::test::Options::get().evmVersion().supportsReturndata()) + expectations.emplace_back(make_pair(Error::Type::Warning, std::string("\"returndatasize\" instruction is only available for Byzantium-compatible"))); + CHECK_ALLOW_MULTI(text, expectations); } BOOST_AUTO_TEST_CASE(create2_as_variable) @@ -6806,8 +7375,9 @@ BOOST_AUTO_TEST_CASE(create2_as_variable) )"; CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"}, - {Error::Type::Warning, "only available after the Metropolis"}, - {Error::Type::DeclarationError, "Unbalanced stack"} + {Error::Type::Warning, "The \"create2\" instruction is not supported by the VM version"}, + {Error::Type::DeclarationError, "Unbalanced stack"}, + {Error::Type::Warning, "not supposed to return values"} })); } @@ -7101,6 +7671,50 @@ BOOST_AUTO_TEST_CASE(builtin_reject_gas) CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup"); } +BOOST_AUTO_TEST_CASE(gasleft) +{ + char const* text = R"( + contract C { + function f() public view returns (uint256 val) { return msg.gas; } + } + )"; + CHECK_WARNING(text, "\"msg.gas\" has been deprecated in favor of \"gasleft()\""); + + text = R"( + contract C { + function f() public view returns (uint256 val) { return gasleft(); } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + + text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() public returns (uint256 val) { return msg.gas; } + } + )"; + CHECK_ERROR(text, TypeError, "\"msg.gas\" has been deprecated in favor of \"gasleft()\""); +} + +BOOST_AUTO_TEST_CASE(gasleft_shadowing) +{ + char const* text = R"( + contract C { + function gasleft() public pure returns (bytes32 val) { return "abc"; } + function f() public pure returns (bytes32 val) { return gasleft(); } + } + )"; + CHECK_WARNING(text, "This declaration shadows a builtin symbol."); + + text = R"( + contract C { + uint gasleft; + function f() public { gasleft = 42; } + } + )"; + CHECK_WARNING(text, "This declaration shadows a builtin symbol."); +} + BOOST_AUTO_TEST_CASE(builtin_reject_value) { char const* text = R"( @@ -7362,7 +7976,7 @@ BOOST_AUTO_TEST_CASE(non_external_fallback) function () external { } } )"; - CHECK_WARNING(text, "Experimental features are turned on."); + CHECK_SUCCESS_NO_WARNINGS(text); text = R"( pragma experimental "v0.5.0"; contract C { @@ -7680,12 +8294,140 @@ BOOST_AUTO_TEST_CASE(array_length_invalid_expression) CHECK_ERROR(text, TypeError, "Operator / not compatible with types int_const 3 and int_const 0"); } +BOOST_AUTO_TEST_CASE(warn_about_address_members_on_contract) +{ + std::string text = R"( + contract C { + function f() view public { + this.balance; + } + } + )"; + CHECK_WARNING(text, "Using contract member \"balance\" inherited from the address type is deprecated."); + text = R"( + contract C { + function f() view public { + this.transfer; + } + } + )"; + CHECK_ALLOW_MULTI(text, (vector<pair<Error::Type, std::string>>{ + {Error::Type::Warning, "Using contract member \"transfer\" inherited from the address type is deprecated"}, + {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} + })); + text = R"( + contract C { + function f() view public { + this.send; + } + } + )"; + CHECK_ALLOW_MULTI(text, (vector<pair<Error::Type, std::string>>{ + {Error::Type::Warning, "Using contract member \"send\" inherited from the address type is deprecated"}, + {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} + })); + text = R"( + contract C { + function f() view public { + this.call; + } + } + )"; + CHECK_WARNING(text, "Using contract member \"call\" inherited from the address type is deprecated."); + text = R"( + contract C { + function f() view public { + this.callcode; + } + } + )"; + CHECK_ALLOW_MULTI(text, (vector<pair<Error::Type, std::string>>{ + {Error::Type::Warning, "Using contract member \"callcode\" inherited from the address type is deprecated"}, + {Error::Type::Warning, "\"callcode\" has been deprecated in favour of \"delegatecall\""} + })); + text = R"( + contract C { + function f() view public { + this.delegatecall; + } + } + )"; + CHECK_WARNING(text, "Using contract member \"delegatecall\" inherited from the address type is deprecated."); +} + +BOOST_AUTO_TEST_CASE(warn_about_address_members_on_non_this_contract) +{ + std::string text = R"( + contract C { + function f() view public { + C c; + c.balance; + } + } + )"; + CHECK_WARNING(text, "Using contract member \"balance\" inherited from the address type is deprecated"); + text = R"( + contract C { + function f() view public { + C c; + c.transfer; + } + } + )"; + CHECK_ALLOW_MULTI(text, (vector<pair<Error::Type, std::string>>{ + {Error::Type::Warning, "Using contract member \"transfer\" inherited from the address type is deprecated"}, + {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} + })); + text = R"( + contract C { + function f() view public { + C c; + c.send; + } + } + )"; + CHECK_ALLOW_MULTI(text, (vector<pair<Error::Type, std::string>>{ + {Error::Type::Warning, "Using contract member \"send\" inherited from the address type is deprecated"}, + {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} + })); + text = R"( + contract C { + function f() pure public { + C c; + c.call; + } + } + )"; + CHECK_WARNING(text, "Using contract member \"call\" inherited from the address type is deprecated"); + text = R"( + contract C { + function f() pure public { + C c; + c.callcode; + } + } + )"; + CHECK_WARNING_ALLOW_MULTI(text, (std::vector<std::string>{ + "Using contract member \"callcode\" inherited from the address type is deprecated", + "\"callcode\" has been deprecated in favour of \"delegatecall\"" + })); + text = R"( + contract C { + function f() pure public { + C c; + c.delegatecall; + } + } + )"; + CHECK_WARNING(text, "Using contract member \"delegatecall\" inherited from the address type is deprecated"); +} + BOOST_AUTO_TEST_CASE(no_address_members_on_contract) { char const* text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.balance; } } @@ -7694,7 +8436,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.transfer; } } @@ -7703,7 +8445,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.send; } } @@ -7712,7 +8454,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.call; } } @@ -7721,7 +8463,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.callcode; } } @@ -7730,7 +8472,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.delegatecall; } } @@ -7738,6 +8480,113 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) CHECK_ERROR(text, TypeError, "Member \"delegatecall\" not found or not visible after argument-dependent lookup in contract"); } +BOOST_AUTO_TEST_CASE(no_warning_for_using_members_that_look_like_address_members) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract C { + function transfer(uint) public; + function f() public { + this.transfer(10); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(emit_events) +{ + char const* text = R"( + contract C { + event e(); + function f() public { + emit e(); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + event e(uint a, string b); + function f() public { + emit e(2, "abc"); + emit e({b: "abc", a: 8}); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract A { event e(uint a, string b); } + contract C is A { + function f() public { + emit A.e(2, "abc"); + emit A.e({b: "abc", a: 8}); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(old_style_events_050) +{ + char const* text = R"( + contract C { + event e(); + function f() public { + e(); + } + } + )"; + CHECK_WARNING(text, "without \"emit\" prefix"); + text = R"( + pragma experimental "v0.5.0"; + contract C { + event e(); + function f() public { + e(); + } + } + )"; + CHECK_ERROR(text, TypeError, "have to be prefixed"); +} + +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&>(*m_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(require_visibility_specifiers) +{ + char const* text = R"( + contract C { + function f() pure { } + } + )"; + CHECK_WARNING(text, "No visibility specified. Defaulting to"); + text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure { } + } + )"; + CHECK_ERROR(text, SyntaxError, "No visibility specified."); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index fb09451f..49a725e0 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -22,7 +22,7 @@ #include "../TestHelper.h" #include <string> -#include <json/json.h> +#include <libdevcore/JSON.h> #include <libsolidity/interface/CompilerStack.h> #include <libsolidity/interface/Exceptions.h> #include <libdevcore/Exceptions.h> @@ -47,6 +47,7 @@ public: { m_compilerStack.reset(false); m_compilerStack.addSource("", "pragma solidity >=0.0;\n" + _code); + m_compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_REQUIRE_MESSAGE(m_compilerStack.parseAndAnalyze(), "Parsing contract failed"); Json::Value generatedDocumentation; @@ -55,7 +56,7 @@ public: else generatedDocumentation = m_compilerStack.natspecDev(m_compilerStack.lastContractName()); Json::Value expectedDocumentation; - m_reader.parse(_expectedDocumentationString, expectedDocumentation); + jsonParseStrict(_expectedDocumentationString, expectedDocumentation); BOOST_CHECK_MESSAGE( expectedDocumentation == generatedDocumentation, "Expected:\n" << expectedDocumentation.toStyledString() << @@ -67,13 +68,13 @@ public: { m_compilerStack.reset(false); m_compilerStack.addSource("", "pragma solidity >=0.0;\n" + _code); + m_compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!m_compilerStack.parseAndAnalyze()); BOOST_REQUIRE(Error::containsErrorOfType(m_compilerStack.errors(), Error::Type::DocstringParsingError)); } private: CompilerStack m_compilerStack; - Json::Reader m_reader; }; BOOST_FIXTURE_TEST_SUITE(SolidityNatspecJSON, DocumentationChecker) diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index bd635c33..33039ca9 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -175,7 +175,7 @@ BOOST_AUTO_TEST_CASE(constant_folding_both_sides) } })"; compileBothVersions(sourceCode); - compareVersions("f(uint256)"); + compareVersions("f(uint256)", 7); } BOOST_AUTO_TEST_CASE(storage_access) @@ -190,7 +190,7 @@ BOOST_AUTO_TEST_CASE(storage_access) } )"; compileBothVersions(sourceCode); - compareVersions("f(uint256)"); + compareVersions("f(uint256)", 7); } BOOST_AUTO_TEST_CASE(array_copy) diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 861e6408..f03b30e1 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1164,6 +1164,36 @@ BOOST_AUTO_TEST_CASE(constant_is_keyword) CHECK_PARSE_ERROR(text, "Expected identifier"); } +BOOST_AUTO_TEST_CASE(keyword_is_reserved) +{ + auto keywords = { + "abstract", + "after", + "case", + "catch", + "default", + "final", + "in", + "inline", + "let", + "match", + "null", + "of", + "relocatable", + "static", + "switch", + "try", + "type", + "typeof" + }; + + for (const auto& keyword: keywords) + { + auto text = std::string("contract ") + keyword + " {}"; + CHECK_PARSE_ERROR(text.c_str(), "Expected identifier"); + } +} + BOOST_AUTO_TEST_CASE(var_array) { char const* text = R"( @@ -1708,6 +1738,19 @@ BOOST_AUTO_TEST_CASE(newInvalidTypeName) CHECK_PARSE_ERROR(text, "Expected explicit type name"); } +BOOST_AUTO_TEST_CASE(emitWithoutEvent) +{ + char const* text = R"( + contract C { + event A(); + function f() { + emit A; + } + } + )"; + CHECK_PARSE_ERROR(text, "Expected token LParen got 'Semicolon'"); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp index 9f385a04..bc9f2fe1 100644 --- a/test/libsolidity/SolidityTypes.cpp +++ b/test/libsolidity/SolidityTypes.cpp @@ -88,6 +88,21 @@ BOOST_AUTO_TEST_CASE(storage_layout_arrays) BOOST_CHECK(ArrayType(DataLocation::Storage, make_shared<FixedBytesType>(32), 9).storageSize() == 9); } +BOOST_AUTO_TEST_CASE(type_escaping) +{ + BOOST_CHECK_EQUAL(Type::escapeIdentifier("("), "$_"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier(")"), "_$"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier(","), "_$_"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier("$"), "$$$"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier("()"), "$__$"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier("(,)"), "$__$__$"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier("(,$,)"), "$__$_$$$_$__$"); + BOOST_CHECK_EQUAL( + Type::escapeIdentifier("((__(_$_$$,__($$,,,$$),$,,,)))$$,$$"), + "$_$___$__$$$_$$$$$$_$___$_$$$$$$_$__$__$_$$$$$$_$_$_$$$_$__$__$__$_$_$$$$$$$_$_$$$$$$" + ); +} + BOOST_AUTO_TEST_CASE(type_identifiers) { ASTNode::resetID(); diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index e48624e5..4c8918be 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -89,7 +89,7 @@ Json::Value compile(string const& _input) StandardCompiler compiler; string output = compiler.compile(_input); Json::Value ret; - BOOST_REQUIRE(Json::Reader().parse(output, ret, false)); + BOOST_REQUIRE(jsonParseStrict(output, ret)); return ret; } @@ -110,11 +110,11 @@ BOOST_AUTO_TEST_CASE(assume_object_input) /// Use the string interface of StandardCompiler to trigger these result = compile(""); - BOOST_CHECK(containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n")); + BOOST_CHECK(containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n* Line 1, Column 1\n A valid JSON document must be either an array or an object value.\n")); result = compile("invalid"); - BOOST_CHECK(containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n")); + BOOST_CHECK(containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n* Line 1, Column 2\n Extra non-whitespace after JSON value.\n")); result = compile("\"invalid\""); - BOOST_CHECK(containsError(result, "JSONError", "Input is not a JSON object.")); + BOOST_CHECK(containsError(result, "JSONError", "* Line 1, Column 1\n A valid JSON document must be either an array or an object value.\n")); BOOST_CHECK(!containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n")); result = compile("{}"); BOOST_CHECK(!containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n")); @@ -154,6 +154,61 @@ BOOST_AUTO_TEST_CASE(no_sources) BOOST_CHECK(containsError(result, "JSONError", "No input sources specified.")); } +BOOST_AUTO_TEST_CASE(no_sources_empty_object) +{ + char const* input = R"( + { + "language": "Solidity", + "sources": {} + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "No input sources specified.")); +} + +BOOST_AUTO_TEST_CASE(no_sources_empty_array) +{ + char const* input = R"( + { + "language": "Solidity", + "sources": [] + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "\"sources\" is not a JSON object.")); +} + +BOOST_AUTO_TEST_CASE(sources_is_array) +{ + char const* input = R"( + { + "language": "Solidity", + "sources": ["aa", "bb"] + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "\"sources\" is not a JSON object.")); +} + +BOOST_AUTO_TEST_CASE(unexpected_trailing_test) +{ + char const* input = R"( + { + "language": "Solidity", + "sources": { + "A": { + "content": "contract A { function f() {} }" + } + } + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "* Line 10, Column 2\n Extra non-whitespace after JSON value.\n")); +} + + BOOST_AUTO_TEST_CASE(smoke_test) { char const* input = R"( @@ -515,6 +570,196 @@ BOOST_AUTO_TEST_CASE(library_filename_with_colon) BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["git:library.sol"]["L"][0].isObject()); } +BOOST_AUTO_TEST_CASE(libraries_invalid_top_level) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": "42" + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "\"libraries\" is not a JSON object.")); +} + +BOOST_AUTO_TEST_CASE(libraries_invalid_entry) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": { + "L": "42" + } + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "library entry is not a JSON object.")); +} + +BOOST_AUTO_TEST_CASE(libraries_invalid_hex) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": { + "library.sol": { + "L": "0x4200000000000000000000000000000000000xx1" + } + } + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "Invalid library address (\"0x4200000000000000000000000000000000000xx1\") supplied.")); +} + +BOOST_AUTO_TEST_CASE(libraries_invalid_length) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": { + "library.sol": { + "L1": "0x42", + "L2": "0x4200000000000000000000000000000000000001ff" + } + } + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "Library address is of invalid length.")); +} + +BOOST_AUTO_TEST_CASE(libraries_missing_hex_prefix) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": { + "library.sol": { + "L": "4200000000000000000000000000000000000001" + } + } + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "Library address is not prefixed with \"0x\".")); +} + +BOOST_AUTO_TEST_CASE(library_linking) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": { + "library.sol": { + "L": "0x4200000000000000000000000000000000000001" + } + }, + "outputSelection": { + "fileA": { + "A": [ + "evm.bytecode" + ] + } + } + }, + "sources": { + "fileA": { + "content": "import \"library.sol\"; import \"library2.sol\"; contract A { function f() returns (uint) { L2.g(); return L.g(); } }" + }, + "library.sol": { + "content": "library L { function g() returns (uint) { return 1; } }" + }, + "library2.sol": { + "content": "library L2 { function g() { } }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsAtMostWarnings(result)); + Json::Value contract = getContractResult(result, "fileA", "A"); + BOOST_CHECK(contract.isObject()); + BOOST_CHECK(contract["evm"]["bytecode"].isObject()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"].isObject()); + BOOST_CHECK(!contract["evm"]["bytecode"]["linkReferences"]["library.sol"].isObject()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"].isObject()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"]["L2"].isArray()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"]["L2"][0].isObject()); +} + +BOOST_AUTO_TEST_CASE(evm_version) +{ + auto inputForVersion = [](string const& _version) + { + return R"( + { + "language": "Solidity", + "sources": { "fileA": { "content": "contract A { }" } }, + "settings": { + )" + _version + R"( + "outputSelection": { + "fileA": { + "A": [ "metadata" ] + } + } + } + } + )"; + }; + Json::Value result; + result = compile(inputForVersion("\"evmVersion\": \"homestead\",")); + BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"homestead\"") != string::npos); + result = compile(inputForVersion("\"evmVersion\": \"tangerineWhistle\",")); + BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"tangerineWhistle\"") != string::npos); + result = compile(inputForVersion("\"evmVersion\": \"spuriousDragon\",")); + BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"spuriousDragon\"") != string::npos); + result = compile(inputForVersion("\"evmVersion\": \"byzantium\",")); + BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"byzantium\"") != string::npos); + // test default + result = compile(inputForVersion("")); + BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"byzantium\"") != string::npos); + // test invalid + result = compile(inputForVersion("\"evmVersion\": \"invalid\",")); + BOOST_CHECK(result["errors"][0]["message"].asString() == "Invalid EVM version requested."); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index 3a03c877..26ff461c 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -20,6 +20,8 @@ #include <test/libsolidity/AnalysisFramework.h> +#include <test/TestHelper.h> + #include <boost/test/unit_test.hpp> #include <string> @@ -109,6 +111,7 @@ BOOST_AUTO_TEST_CASE(environment_access) "block.difficulty", "block.number", "block.gaslimit", + "gasleft()", "msg.gas", "msg.value", "msg.sender", @@ -148,7 +151,7 @@ BOOST_AUTO_TEST_CASE(environment_access) BOOST_AUTO_TEST_CASE(view_error_for_050) { CHECK_ERROR( - "pragma experimental \"v0.5.0\"; contract C { uint x; function f() view { x = 2; } }", + "pragma experimental \"v0.5.0\"; contract C { uint x; function f() view public { x = 2; } }", TypeError, "Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable." ); @@ -277,11 +280,11 @@ BOOST_AUTO_TEST_CASE(builtin_functions) string text = R"( contract C { function f() public { - this.transfer(1); - require(this.send(2)); - selfdestruct(this); - require(this.delegatecall()); - require(this.call()); + address(this).transfer(1); + require(address(this).send(2)); + selfdestruct(address(this)); + require(address(this).delegatecall()); + require(address(this).call()); } function g() pure public { bytes32 x = keccak256("abc"); @@ -423,7 +426,10 @@ BOOST_AUTO_TEST_CASE(assembly_staticcall) } } )"; - CHECK_WARNING(text, "only available after the Metropolis"); + if (!dev::test::Options::get().evmVersion().hasStaticCall()) + CHECK_WARNING(text, "\"staticcall\" instruction is only available for Byzantium-compatible"); + else + CHECK_SUCCESS_NO_WARNINGS(text); } BOOST_AUTO_TEST_CASE(assembly_jump) |