diff options
author | chriseth <chris@ethereum.org> | 2017-09-14 00:29:13 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-09-14 00:29:13 +0800 |
commit | 3f3bcc4f8a0d12e9b92d6b63e7cfd92cbbfa775d (patch) | |
tree | 5a8f36e2b2491e07534d9c1570b8f40a2235c4c5 | |
parent | 72b7e001aa837ab59b3b14bfbabf69bbd102ded1 (diff) | |
parent | 172704a58fa2b7562107b8df299c5a81ba702d12 (diff) | |
download | dexon-solidity-3f3bcc4f8a0d12e9b92d6b63e7cfd92cbbfa775d.tar.gz dexon-solidity-3f3bcc4f8a0d12e9b92d6b63e7cfd92cbbfa775d.tar.zst dexon-solidity-3f3bcc4f8a0d12e9b92d6b63e7cfd92cbbfa775d.zip |
Merge pull request #2848 from ethereum/checkViewPure
Enforce view and pure.
-rw-r--r-- | Changelog.md | 1 | ||||
-rw-r--r-- | libevmasm/SemanticInformation.cpp | 53 | ||||
-rw-r--r-- | libevmasm/SemanticInformation.h | 2 | ||||
-rw-r--r-- | libsolidity/analysis/StaticAnalyzer.cpp | 2 | ||||
-rw-r--r-- | libsolidity/analysis/StaticAnalyzer.h | 4 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 3 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.h | 1 | ||||
-rw-r--r-- | libsolidity/analysis/ViewPureChecker.cpp | 313 | ||||
-rw-r--r-- | libsolidity/analysis/ViewPureChecker.h | 79 | ||||
-rw-r--r-- | libsolidity/ast/ASTAnnotations.h | 3 | ||||
-rw-r--r-- | libsolidity/ast/ASTJsonConverter.cpp | 1 | ||||
-rw-r--r-- | libsolidity/ast/Types.h | 1 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.cpp | 11 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 30 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 142 | ||||
-rw-r--r-- | test/libsolidity/ViewPureChecker.cpp | 398 |
16 files changed, 954 insertions, 90 deletions
diff --git a/Changelog.md b/Changelog.md index 71f873a9..62e4eb09 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Features: * Type Checker: Do not show the same error multiple times for events. * Type Checker: Greatly reduce the number of duplicate errors shown for duplicate constructors and functions. * Type Checker: Warn on using literals as tight packing parameters in ``keccak256``, ``sha3``, ``sha256`` and ``ripemd160``. + * Type Checker: Enforce ``view`` and ``pure``. Bugfixes: * ABI JSON: Include all overloaded events. diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index f63f0c61..ceb3fbdd 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -188,3 +188,56 @@ bool SemanticInformation::invalidatesStorage(Instruction _instruction) return false; } } + +bool SemanticInformation::invalidInPureFunctions(Instruction _instruction) +{ + switch (_instruction) + { + case Instruction::ADDRESS: + case Instruction::BALANCE: + case Instruction::ORIGIN: + case Instruction::CALLER: + case Instruction::CALLVALUE: + case Instruction::GASPRICE: + case Instruction::EXTCODESIZE: + case Instruction::EXTCODECOPY: + case Instruction::BLOCKHASH: + case Instruction::COINBASE: + case Instruction::TIMESTAMP: + case Instruction::NUMBER: + case Instruction::DIFFICULTY: + case Instruction::GASLIMIT: + case Instruction::STATICCALL: + case Instruction::SLOAD: + return true; + default: + break; + } + return invalidInViewFunctions(_instruction); +} + +bool SemanticInformation::invalidInViewFunctions(Instruction _instruction) +{ + switch (_instruction) + { + case Instruction::SSTORE: + case Instruction::JUMP: + case Instruction::JUMPI: + case Instruction::GAS: + case Instruction::LOG0: + case Instruction::LOG1: + case Instruction::LOG2: + case Instruction::LOG3: + case Instruction::LOG4: + case Instruction::CREATE: + case Instruction::CALL: + case Instruction::CALLCODE: + case Instruction::DELEGATECALL: + case Instruction::CREATE2: + case Instruction::SELFDESTRUCT: + return true; + default: + break; + } + return false; +} diff --git a/libevmasm/SemanticInformation.h b/libevmasm/SemanticInformation.h index 5b02061f..e5ea7c18 100644 --- a/libevmasm/SemanticInformation.h +++ b/libevmasm/SemanticInformation.h @@ -53,6 +53,8 @@ struct SemanticInformation static bool invalidatesMemory(solidity::Instruction _instruction); /// @returns true if the given instruction modifies storage (even indirectly). static bool invalidatesStorage(solidity::Instruction _instruction); + static bool invalidInPureFunctions(solidity::Instruction _instruction); + static bool invalidInViewFunctions(solidity::Instruction _instruction); }; } diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index d012c25d..ffa538b6 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -57,8 +57,6 @@ bool StaticAnalyzer::visit(FunctionDefinition const& _function) solAssert(m_localVarUseCount.empty(), ""); m_nonPayablePublic = _function.isPublic() && !_function.isPayable(); m_constructor = _function.isConstructor(); - if (_function.stateMutability() == StateMutability::Pure) - m_errorReporter.warning(_function.location(), "Function is marked pure. Be careful, pureness is not enforced yet."); return true; } diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h index a3080b42..24ed119f 100644 --- a/libsolidity/analysis/StaticAnalyzer.h +++ b/libsolidity/analysis/StaticAnalyzer.h @@ -37,8 +37,8 @@ namespace solidity /** * The module that performs static analysis on the AST. * In this context, static analysis is anything that can produce warnings which can help - * programmers write cleaner code. For every warning generated eher, it has to be possible to write - * equivalent code that does generate the warning. + * programmers write cleaner code. For every warning generated here, it has to be possible to write + * equivalent code that does not generate the warning. */ class StaticAnalyzer: private ASTConstVisitor { diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index c54f4c87..26529c22 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -339,6 +339,9 @@ void TypeChecker::checkFunctionOverride(FunctionDefinition const& function, Func if (!functionType.hasEqualArgumentTypes(superType)) return; + if (!function.annotation().superFunction) + function.annotation().superFunction = &super; + if (function.visibility() != super.visibility()) overrideError(function, super, "Overriding function visibility differs."); diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index f2e13765..0c6f54d3 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -63,6 +63,7 @@ private: void checkContractDuplicateFunctions(ContractDefinition const& _contract); void checkContractIllegalOverrides(ContractDefinition const& _contract); /// Reports a type error with an appropiate message if overriden function signature differs. + /// Also stores the direct super function in the AST annotations. void checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super); void overrideError(FunctionDefinition const& function, FunctionDefinition const& super, std::string message); void checkContractAbstractFunctions(ContractDefinition const& _contract); diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp new file mode 100644 index 00000000..8f9d41c9 --- /dev/null +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -0,0 +1,313 @@ +/* + 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/>. +*/ + +#include <libsolidity/analysis/ViewPureChecker.h> + +#include <libevmasm/SemanticInformation.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <functional> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +namespace +{ + +class AssemblyViewPureChecker: public boost::static_visitor<void> +{ +public: + explicit AssemblyViewPureChecker(std::function<void(StateMutability, SourceLocation const&)> _reportMutability): + m_reportMutability(_reportMutability) {} + + void operator()(assembly::Label const&) { } + void operator()(assembly::Instruction const& _instruction) + { + if (eth::SemanticInformation::invalidInViewFunctions(_instruction.instruction)) + m_reportMutability(StateMutability::NonPayable, _instruction.location); + else if (eth::SemanticInformation::invalidInPureFunctions(_instruction.instruction)) + m_reportMutability(StateMutability::View, _instruction.location); + } + void operator()(assembly::Literal const&) {} + void operator()(assembly::Identifier const&) {} + void operator()(assembly::FunctionalInstruction const& _instr) + { + (*this)(_instr.instruction); + for (auto const& arg: _instr.arguments) + boost::apply_visitor(*this, arg); + } + void operator()(assembly::StackAssignment const&) {} + void operator()(assembly::Assignment const& _assignment) + { + boost::apply_visitor(*this, *_assignment.value); + } + void operator()(assembly::VariableDeclaration const& _varDecl) + { + if (_varDecl.value) + boost::apply_visitor(*this, *_varDecl.value); + } + void operator()(assembly::FunctionDefinition const& _funDef) + { + (*this)(_funDef.body); + } + void operator()(assembly::FunctionCall const& _funCall) + { + for (auto const& arg: _funCall.arguments) + boost::apply_visitor(*this, arg); + } + void operator()(assembly::Switch const& _switch) + { + boost::apply_visitor(*this, *_switch.expression); + for (auto const& _case: _switch.cases) + { + if (_case.value) + (*this)(*_case.value); + (*this)(_case.body); + } + } + void operator()(assembly::ForLoop const& _for) + { + (*this)(_for.pre); + boost::apply_visitor(*this, *_for.condition); + (*this)(_for.body); + (*this)(_for.post); + } + void operator()(assembly::Block const& _block) + { + for (auto const& s: _block.statements) + boost::apply_visitor(*this, s); + } + +private: + std::function<void(StateMutability, SourceLocation const&)> m_reportMutability; +}; + +} + +bool ViewPureChecker::check() +{ + vector<ContractDefinition const*> contracts; + + for (auto const& node: m_ast) + { + SourceUnit const* source = dynamic_cast<SourceUnit const*>(node.get()); + solAssert(source, ""); + contracts += source->filteredNodes<ContractDefinition>(source->nodes()); + } + + // Check modifiers first to infer their state mutability. + for (auto const* contract: contracts) + for (ModifierDefinition const* mod: contract->functionModifiers()) + mod->accept(*this); + + for (auto const* contract: contracts) + contract->accept(*this); + + return !m_errors; +} + + + +bool ViewPureChecker::visit(FunctionDefinition const& _funDef) +{ + solAssert(!m_currentFunction, ""); + m_currentFunction = &_funDef; + m_currentBestMutability = StateMutability::Pure; + return true; +} + +void ViewPureChecker::endVisit(FunctionDefinition const& _funDef) +{ + solAssert(m_currentFunction == &_funDef, ""); + if ( + m_currentBestMutability < _funDef.stateMutability() && + _funDef.stateMutability() != StateMutability::Payable && + _funDef.isImplemented() && + !_funDef.isConstructor() && + !_funDef.isFallback() && + !_funDef.annotation().superFunction + ) + m_errorReporter.warning( + _funDef.location(), + "Function state mutability can be restricted to " + stateMutabilityToString(m_currentBestMutability) + ); + m_currentFunction = nullptr; +} + +bool ViewPureChecker::visit(ModifierDefinition const&) +{ + solAssert(m_currentFunction == nullptr, ""); + m_currentBestMutability = StateMutability::Pure; + return true; +} + +void ViewPureChecker::endVisit(ModifierDefinition const& _modifierDef) +{ + solAssert(m_currentFunction == nullptr, ""); + m_inferredMutability[&_modifierDef] = m_currentBestMutability; +} + +void ViewPureChecker::endVisit(Identifier const& _identifier) +{ + Declaration const* declaration = _identifier.annotation().referencedDeclaration; + solAssert(declaration, ""); + + StateMutability mutability = StateMutability::Pure; + + bool writes = _identifier.annotation().lValueRequested; + if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration)) + { + if (varDecl->isStateVariable() && !varDecl->isConstant()) + mutability = writes ? StateMutability::NonPayable : StateMutability::View; + } + else if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration)) + { + switch (magicVar->type()->category()) + { + case Type::Category::Contract: + solAssert(_identifier.name() == "this" || _identifier.name() == "super", ""); + if (!dynamic_cast<ContractType const&>(*magicVar->type()).isSuper()) + // reads the address + mutability = StateMutability::View; + break; + case Type::Category::Integer: + solAssert(_identifier.name() == "now", ""); + mutability = StateMutability::View; + break; + default: + break; + } + } + + reportMutability(mutability, _identifier.location()); +} + +void ViewPureChecker::endVisit(InlineAssembly const& _inlineAssembly) +{ + AssemblyViewPureChecker{ + [=](StateMutability _mutability, SourceLocation const& _location) { reportMutability(_mutability, _location); } + }(_inlineAssembly.operations()); +} + +void ViewPureChecker::reportMutability(StateMutability _mutability, SourceLocation const& _location) +{ + if (m_currentFunction && m_currentFunction->stateMutability() < _mutability) + { + string text; + if (_mutability == StateMutability::View) + text = + "Function declared as pure, but this expression (potentially) reads from the " + "environment or state and thus requires \"view\"."; + else if (_mutability == StateMutability::NonPayable) + text = + "Function declared as " + + stateMutabilityToString(m_currentFunction->stateMutability()) + + ", but this expression (potentially) modifies the state and thus " + "requires non-payable (the default) or payable."; + else + solAssert(false, ""); + + if (m_currentFunction->stateMutability() == StateMutability::View) + // TODO Change this to error with 0.5.0 + m_errorReporter.warning(_location, text); + else if (m_currentFunction->stateMutability() == StateMutability::Pure) + { + m_errors = true; + m_errorReporter.typeError(_location, text); + } + else + solAssert(false, ""); + } + if (_mutability > m_currentBestMutability) + m_currentBestMutability = _mutability; +} + +void ViewPureChecker::endVisit(FunctionCall const& _functionCall) +{ + if (_functionCall.annotation().kind != FunctionCallKind::FunctionCall) + return; + + StateMutability mut = dynamic_cast<FunctionType const&>(*_functionCall.expression().annotation().type).stateMutability(); + // We only require "nonpayable" to call a payble function. + if (mut == StateMutability::Payable) + mut = StateMutability::NonPayable; + reportMutability(mut, _functionCall.location()); +} + +void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) +{ + StateMutability mutability = StateMutability::Pure; + bool writes = _memberAccess.annotation().lValueRequested; + + ASTString const& member = _memberAccess.memberName(); + switch (_memberAccess.expression().annotation().type->category()) + { + case Type::Category::Contract: + case Type::Category::Integer: + if (member == "balance" && !_memberAccess.annotation().referencedDeclaration) + mutability = StateMutability::View; + break; + case Type::Category::Magic: + // we can ignore the kind of magic and only look at the name of the member + if (member != "data" && member != "sig" && member != "blockhash") + mutability = StateMutability::View; + break; + case Type::Category::Struct: + { + if (_memberAccess.expression().annotation().type->dataStoredIn(DataLocation::Storage)) + mutability = writes ? StateMutability::NonPayable : StateMutability::View; + break; + } + case Type::Category::Array: + { + auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.expression().annotation().type); + if (member == "length" && type.isDynamicallySized() && type.dataStoredIn(DataLocation::Storage)) + mutability = writes ? StateMutability::NonPayable : StateMutability::View; + break; + } + default: + break; + } + reportMutability(mutability, _memberAccess.location()); +} + +void ViewPureChecker::endVisit(IndexAccess const& _indexAccess) +{ + if (!_indexAccess.indexExpression()) + solAssert(_indexAccess.annotation().type->category() == Type::Category::TypeType, ""); + else + { + bool writes = _indexAccess.annotation().lValueRequested; + if (_indexAccess.baseExpression().annotation().type->dataStoredIn(DataLocation::Storage)) + reportMutability(writes ? StateMutability::NonPayable : StateMutability::View, _indexAccess.location()); + } +} + +void ViewPureChecker::endVisit(ModifierInvocation const& _modifier) +{ + solAssert(_modifier.name(), ""); + if (ModifierDefinition const* mod = dynamic_cast<decltype(mod)>(_modifier.name()->annotation().referencedDeclaration)) + { + solAssert(m_inferredMutability.count(mod), ""); + reportMutability(m_inferredMutability.at(mod), _modifier.location()); + } + else + solAssert(dynamic_cast<ContractDefinition const*>(_modifier.name()->annotation().referencedDeclaration), ""); +} + diff --git a/libsolidity/analysis/ViewPureChecker.h b/libsolidity/analysis/ViewPureChecker.h new file mode 100644 index 00000000..ae303533 --- /dev/null +++ b/libsolidity/analysis/ViewPureChecker.h @@ -0,0 +1,79 @@ +/* + 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/>. +*/ + +#pragma once + +#include <libsolidity/ast/ASTEnums.h> +#include <libsolidity/ast/ASTForward.h> +#include <libsolidity/ast/ASTVisitor.h> + +#include <libsolidity/interface/ErrorReporter.h> + +#include <map> +#include <memory> + +namespace dev +{ +namespace solidity +{ + +class ASTNode; +class FunctionDefinition; +class ModifierDefinition; +class Identifier; +class MemberAccess; +class IndexAccess; +class ModifierInvocation; +class FunctionCall; +class InlineAssembly; + +class ViewPureChecker: private ASTConstVisitor +{ +public: + ViewPureChecker(std::vector<std::shared_ptr<ASTNode>> const& _ast, ErrorReporter& _errorReporter): + m_ast(_ast), m_errorReporter(_errorReporter) {} + + bool check(); + +private: + + virtual bool visit(FunctionDefinition const& _funDef) override; + virtual void endVisit(FunctionDefinition const& _funDef) override; + virtual bool visit(ModifierDefinition const& _modifierDef) override; + virtual void endVisit(ModifierDefinition const& _modifierDef) override; + virtual void endVisit(Identifier const& _identifier) override; + virtual void endVisit(MemberAccess const& _memberAccess) override; + virtual void endVisit(IndexAccess const& _indexAccess) override; + virtual void endVisit(ModifierInvocation const& _modifier) override; + virtual void endVisit(FunctionCall const& _functionCall) override; + virtual void endVisit(InlineAssembly const& _inlineAssembly) override; + + /// Called when an element of mutability @a _mutability is encountered. + /// Creates appropriate warnings and errors and sets @a m_currentBestMutability. + void reportMutability(StateMutability _mutability, SourceLocation const& _location); + + std::vector<std::shared_ptr<ASTNode>> const& m_ast; + ErrorReporter& m_errorReporter; + + bool m_errors = false; + StateMutability m_currentBestMutability = StateMutability::Payable; + FunctionDefinition const* m_currentFunction = nullptr; + std::map<ModifierDefinition const*, StateMutability> m_inferredMutability; +}; + +} +} diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index fd9efb4d..3d4236cc 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -94,6 +94,9 @@ struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, DocumentedAnnota struct FunctionDefinitionAnnotation: ASTAnnotation, DocumentedAnnotation { + /// The function this function overrides, if any. This is always the closest + /// in the linearized inheritance hierarchy. + FunctionDefinition const* superFunction = nullptr; }; struct EventDefinitionAnnotation: ASTAnnotation, DocumentedAnnotation diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index afc53bfe..6811d3e4 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -328,6 +328,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node) make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() <= StateMutability::View), make_pair("payable", _node.isPayable()), make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())), + make_pair("superFunction", idOrNull(_node.annotation().superFunction)), make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair("parameters", toJson(_node.parameterList())), make_pair("isConstructor", _node.isConstructor()), diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index de6dcee9..d4d6da69 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -1064,6 +1064,7 @@ public: { return _inLibrary ? shared_from_this() : TypePointer(); } + virtual bool dataStoredIn(DataLocation _location) const override { return _location == DataLocation::Storage; } TypePointer const& keyType() const { return m_keyType; } TypePointer const& valueType() const { return m_valueType; } diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 41bbf687..51544f8a 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -36,6 +36,7 @@ #include <libsolidity/analysis/StaticAnalyzer.h> #include <libsolidity/analysis/PostTypeChecker.h> #include <libsolidity/analysis/SyntaxChecker.h> +#include <libsolidity/analysis/ViewPureChecker.h> #include <libsolidity/codegen/Compiler.h> #include <libsolidity/formal/SMTChecker.h> #include <libsolidity/interface/ABI.h> @@ -222,6 +223,16 @@ bool CompilerStack::analyze() if (noErrors) { + vector<ASTPointer<ASTNode>> ast; + for (Source const* source: m_sourceOrder) + ast.push_back(source->ast); + + if (!ViewPureChecker(ast, m_errorReporter).check()) + noErrors = false; + } + + if (noErrors) + { SMTChecker smtChecker(m_errorReporter, m_smtQuery); for (Source const* source: m_sourceOrder) smtChecker.analyze(*source->ast); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 73dd7d22..fa4d675c 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -6525,7 +6525,7 @@ BOOST_AUTO_TEST_CASE(state_variable_under_contract_name) contract Scope { uint stateVar = 42; - function getStateVar() constant returns (uint stateVar) { + function getStateVar() view returns (uint stateVar) { stateVar = Scope.stateVar; } } @@ -6791,7 +6791,7 @@ BOOST_AUTO_TEST_CASE(fixed_arrays_as_return_type) { char const* sourceCode = R"( contract A { - function f(uint16 input) constant returns (uint16[5] arr) + function f(uint16 input) pure returns (uint16[5] arr) { arr[0] = input; arr[1] = ++input; @@ -6820,7 +6820,7 @@ BOOST_AUTO_TEST_CASE(internal_types_in_library) { char const* sourceCode = R"( library Lib { - function find(uint16[] storage _haystack, uint16 _needle) constant returns (uint) + function find(uint16[] storage _haystack, uint16 _needle) view returns (uint) { for (uint i = 0; i < _haystack.length; ++i) if (_haystack[i] == _needle) @@ -9913,12 +9913,12 @@ BOOST_AUTO_TEST_CASE(keccak256_assembly) { char const* sourceCode = R"( contract C { - function f() returns (bytes32 ret) { + function f() pure returns (bytes32 ret) { assembly { ret := keccak256(0, 0) } } - function g() returns (bytes32 ret) { + function g() pure returns (bytes32 ret) { assembly { 0 0 @@ -9926,12 +9926,12 @@ BOOST_AUTO_TEST_CASE(keccak256_assembly) =: ret } } - function h() returns (bytes32 ret) { + function h() pure returns (bytes32 ret) { assembly { ret := sha3(0, 0) } } - function i() returns (bytes32 ret) { + function i() pure returns (bytes32 ret) { assembly { 0 0 @@ -9979,7 +9979,7 @@ BOOST_AUTO_TEST_CASE(inlineasm_empty_let) { char const* sourceCode = R"( contract C { - function f() returns (uint a, uint b) { + function f() pure returns (uint a, uint b) { assembly { let x let y, z @@ -9998,13 +9998,13 @@ BOOST_AUTO_TEST_CASE(bare_call_invalid_address) char const* sourceCode = R"( contract C { /// Calling into non-existant account is successful (creates the account) - function f() external constant returns (bool) { + function f() external view returns (bool) { return address(0x4242).call(); } - function g() external constant returns (bool) { + function g() external view returns (bool) { return address(0x4242).callcode(); } - function h() external constant returns (bool) { + function h() external view returns (bool) { return address(0x4242).delegatecall(); } } @@ -10023,16 +10023,16 @@ BOOST_AUTO_TEST_CASE(delegatecall_return_value) function set(uint _value) external { value = _value; } - function get() external constant returns (uint) { + function get() external view returns (uint) { return value; } - function get_delegated() external constant returns (bool) { + function get_delegated() external view returns (bool) { return this.delegatecall(bytes4(sha3("get()"))); } - function assert0() external constant { + function assert0() external view { assert(value == 0); } - function assert0_delegated() external constant returns (bool) { + function assert0_delegated() external view returns (bool) { return this.delegatecall(bytes4(sha3("assert0()"))); } } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 1fbc55a2..04d3d2d3 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -1546,7 +1546,7 @@ BOOST_AUTO_TEST_CASE(exp_warn_literal_base) { char const* sourceCode = R"( contract test { - function f() returns(uint) { + function f() pure returns(uint) { uint8 x = 100; return 10**x; } @@ -1555,7 +1555,7 @@ BOOST_AUTO_TEST_CASE(exp_warn_literal_base) CHECK_WARNING(sourceCode, "might overflow"); sourceCode = R"( contract test { - function f() returns(uint) { + function f() pure returns(uint) { uint8 x = 100; return uint8(10)**x; } @@ -1564,7 +1564,7 @@ BOOST_AUTO_TEST_CASE(exp_warn_literal_base) CHECK_SUCCESS(sourceCode); sourceCode = R"( contract test { - function f() returns(uint) { + function f() pure returns(uint) { return 2**80; } } @@ -1576,7 +1576,7 @@ BOOST_AUTO_TEST_CASE(shift_warn_literal_base) { char const* sourceCode = R"( contract test { - function f() returns(uint) { + function f() pure returns(uint) { uint8 x = 100; return 10 << x; } @@ -1585,7 +1585,7 @@ BOOST_AUTO_TEST_CASE(shift_warn_literal_base) CHECK_WARNING(sourceCode, "might overflow"); sourceCode = R"( contract test { - function f() returns(uint) { + function f() pure returns(uint) { uint8 x = 100; return uint8(10) << x; } @@ -1594,7 +1594,7 @@ BOOST_AUTO_TEST_CASE(shift_warn_literal_base) CHECK_SUCCESS(sourceCode); sourceCode = R"( contract test { - function f() returns(uint) { + function f() pure returns(uint) { return 2 << 80; } } @@ -1602,7 +1602,7 @@ BOOST_AUTO_TEST_CASE(shift_warn_literal_base) CHECK_SUCCESS(sourceCode); sourceCode = R"( contract test { - function f() returns(uint) { + function f() pure returns(uint) { uint8 x = 100; return 10 >> x; } @@ -1615,7 +1615,7 @@ BOOST_AUTO_TEST_CASE(warn_var_from_zero) { char const* sourceCode = R"( contract test { - function f() returns (uint) { + function f() pure returns (uint) { var i = 1; return i; } @@ -1624,7 +1624,7 @@ BOOST_AUTO_TEST_CASE(warn_var_from_zero) CHECK_WARNING(sourceCode, "uint8, which can hold values between 0 and 255"); sourceCode = R"( contract test { - function f() { + function f() pure { var i = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; i; } @@ -1633,7 +1633,7 @@ BOOST_AUTO_TEST_CASE(warn_var_from_zero) CHECK_WARNING(sourceCode, "uint256, which can hold values between 0 and 115792089237316195423570985008687907853269984665640564039457584007913129639935"); sourceCode = R"( contract test { - function f() { + function f() pure { var i = -2; i; } @@ -1642,7 +1642,7 @@ BOOST_AUTO_TEST_CASE(warn_var_from_zero) CHECK_WARNING(sourceCode, "int8, which can hold values between -128 and 127"); sourceCode = R"( contract test { - function f() { + function f() pure { for (var i = 0; i < msg.data.length; i++) { } } } @@ -2642,7 +2642,7 @@ BOOST_AUTO_TEST_CASE(uninitialized_mapping_array_variable) { char const* sourceCode = R"( contract C { - function f() { + function f() pure { mapping(uint => uint)[] storage x; x; } @@ -3973,7 +3973,7 @@ BOOST_AUTO_TEST_CASE(rational_unary_operation) { char const* text = R"( contract test { - function f() { + function f() pure { ufixed16x2 a = 3.25; fixed16x2 b = -3.25; a; b; @@ -3983,7 +3983,7 @@ BOOST_AUTO_TEST_CASE(rational_unary_operation) CHECK_SUCCESS_NO_WARNINGS(text); text = R"( contract test { - function f() { + function f() pure { ufixed16x2 a = +3.25; fixed16x2 b = -3.25; a; b; @@ -3993,7 +3993,7 @@ BOOST_AUTO_TEST_CASE(rational_unary_operation) CHECK_WARNING(text, "Use of unary + is deprecated"); text = R"( contract test { - function f(uint x) { + function f(uint x) pure { uint y = +x; y; } @@ -4006,7 +4006,7 @@ BOOST_AUTO_TEST_CASE(leading_zero_rationals_convert) { char const* text = R"( contract A { - function f() { + function f() pure { ufixed16x2 a = 0.5; ufixed256x52 b = 0.0000000000000006661338147750939242541790008544921875; fixed16x2 c = -0.5; @@ -4519,7 +4519,7 @@ BOOST_AUTO_TEST_CASE(warn_about_callcode) { char const* text = R"( contract test { - function f() { + function f() pure { var x = address(0x12).callcode; x; } @@ -4532,7 +4532,7 @@ BOOST_AUTO_TEST_CASE(no_warn_about_callcode_as_function) { char const* text = R"( contract test { - function callcode() { + function callcode() pure { test.callcode(); } } @@ -5234,7 +5234,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_variable_access_out_of_functions) char const* text = R"( contract test { uint a; - function f() { + function f() pure { assembly { function g() -> x { x := a_slot } } @@ -5275,7 +5275,7 @@ BOOST_AUTO_TEST_CASE(warns_msg_value_in_non_payable_public_function) { char const* text = R"( contract C { - function f() { + function f() view { msg.value; } } @@ -5299,7 +5299,7 @@ BOOST_AUTO_TEST_CASE(does_not_warn_msg_value_in_internal_function) { char const* text = R"( contract C { - function f() internal { + function f() view internal { msg.value; } } @@ -5311,7 +5311,7 @@ BOOST_AUTO_TEST_CASE(does_not_warn_msg_value_in_library) { char const* text = R"( library C { - function f() { + function f() view { msg.value; } } @@ -5323,7 +5323,7 @@ BOOST_AUTO_TEST_CASE(does_not_warn_msg_value_in_modifier_following_non_payable_p { char const* text = R"( contract c { - function f() { } + function f() pure { } modifier m() { msg.value; _; } } )"; @@ -5402,7 +5402,7 @@ BOOST_AUTO_TEST_CASE(invalid_address_checksum) { char const* text = R"( contract C { - function f() { + function f() pure { address x = 0xFA0bFc97E48458494Ccd857e1A85DC91F7F0046E; x; } @@ -5415,7 +5415,7 @@ BOOST_AUTO_TEST_CASE(invalid_address_no_checksum) { char const* text = R"( contract C { - function f() { + function f() pure { address x = 0xfa0bfc97e48458494ccd857e1a85dc91f7f0046e; x; } @@ -5428,7 +5428,7 @@ BOOST_AUTO_TEST_CASE(invalid_address_length) { char const* text = R"( contract C { - function f() { + function f() pure { address x = 0xA0bFc97E48458494Ccd857e1A85DC91F7F0046E; x; } @@ -5678,7 +5678,7 @@ BOOST_AUTO_TEST_CASE(warn_about_throw) { char const* text = R"( contract C { - function f() { + function f() pure { throw; } } @@ -5690,7 +5690,7 @@ BOOST_AUTO_TEST_CASE(bare_revert) { char const* text = R"( contract C { - function f(uint x) { + function f(uint x) pure { if (x > 7) revert; } @@ -5701,17 +5701,17 @@ BOOST_AUTO_TEST_CASE(bare_revert) BOOST_AUTO_TEST_CASE(bare_others) { - CHECK_WARNING("contract C { function f() { selfdestruct; } }", "Statement has no effect."); - CHECK_WARNING("contract C { function f() { assert; } }", "Statement has no effect."); - CHECK_WARNING("contract C { function f() { require; } }", "Statement has no effect."); - CHECK_WARNING("contract C { function f() { suicide; } }", "Statement has no effect."); + CHECK_WARNING("contract C { function f() pure { selfdestruct; } }", "Statement has no effect."); + CHECK_WARNING("contract C { function f() pure { assert; } }", "Statement has no effect."); + CHECK_WARNING("contract C { function f() pure { require; } }", "Statement has no effect."); + CHECK_WARNING("contract C { function f() pure { suicide; } }", "Statement has no effect."); } BOOST_AUTO_TEST_CASE(pure_statement_in_for_loop) { char const* text = R"( contract C { - function f() { + function f() pure { for (uint x = 0; x < 10; true) x++; } @@ -5724,7 +5724,7 @@ BOOST_AUTO_TEST_CASE(pure_statement_check_for_regular_for_loop) { char const* text = R"( contract C { - function f() { + function f() pure { for (uint x = 0; true; x++) {} } @@ -5780,7 +5780,7 @@ BOOST_AUTO_TEST_CASE(nowarn_swap_memory) char const* text = R"( contract C { struct S { uint a; uint b; } - function f() { + function f() pure { S memory x; S memory y; (x, y) = (y, x); @@ -5811,7 +5811,7 @@ BOOST_AUTO_TEST_CASE(warn_unused_local) { char const* text = R"( contract C { - function f() { + function f() pure { uint a; } } @@ -5823,7 +5823,7 @@ BOOST_AUTO_TEST_CASE(warn_unused_local_assigned) { char const* text = R"( contract C { - function f() { + function f() pure { uint a = 1; } } @@ -5835,14 +5835,14 @@ BOOST_AUTO_TEST_CASE(warn_unused_function_parameter) { char const* text = R"( contract C { - function f(uint a) { + function f(uint a) pure { } } )"; CHECK_WARNING(text, "Unused function parameter. Remove or comment out the variable name to silence this warning."); text = R"( contract C { - function f(uint a) { + function f(uint a) pure { } } )"; @@ -5853,14 +5853,14 @@ BOOST_AUTO_TEST_CASE(warn_unused_return_parameter) { char const* text = R"( contract C { - function f() returns (uint a) { + function f() pure returns (uint a) { } } )"; CHECK_WARNING(text, "Unused function parameter. Remove or comment out the variable name to silence this warning."); text = R"( contract C { - function f() returns (uint a) { + function f() pure returns (uint a) { return; } } @@ -5868,14 +5868,14 @@ BOOST_AUTO_TEST_CASE(warn_unused_return_parameter) CHECK_WARNING(text, "Unused function parameter. Remove or comment out the variable name to silence this warning."); text = R"( contract C { - function f() returns (uint) { + function f() pure returns (uint) { } } )"; CHECK_SUCCESS_NO_WARNINGS(text); text = R"( contract C { - function f() returns (uint a) { + function f() pure returns (uint a) { a = 1; } } @@ -5883,7 +5883,7 @@ BOOST_AUTO_TEST_CASE(warn_unused_return_parameter) CHECK_SUCCESS_NO_WARNINGS(text); text = R"( contract C { - function f() returns (uint a) { + function f() pure returns (uint a) { return 1; } } @@ -5895,7 +5895,7 @@ BOOST_AUTO_TEST_CASE(no_unused_warnings) { char const* text = R"( contract C { - function f(uint a) returns (uint b) { + function f(uint a) pure returns (uint b) { uint c = 1; b = a + c; } @@ -5908,7 +5908,7 @@ BOOST_AUTO_TEST_CASE(no_unused_dec_after_use) { char const* text = R"( contract C { - function f() { + function f() pure { a = 7; uint a; } @@ -5921,7 +5921,7 @@ BOOST_AUTO_TEST_CASE(no_unused_inline_asm) { char const* text = R"( contract C { - function f() { + function f() pure { uint a; assembly { a := 1 @@ -5936,7 +5936,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_functions) { char const* text = R"( contract C { - function keccak256() {} + function keccak256() pure {} } )"; CHECK_WARNING(text, "shadows a builtin symbol"); @@ -5946,7 +5946,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_variables) { char const* text = R"( contract C { - function f() { + function f() pure { uint msg; msg; } @@ -5978,7 +5978,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_parameters) { char const* text = R"( contract C { - function f(uint require) { + function f(uint require) pure { require = 2; } } @@ -5990,7 +5990,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_return_parameters) { char const* text = R"( contract C { - function f() returns (uint require) { + function f() pure returns (uint require) { require = 2; } } @@ -6034,8 +6034,8 @@ BOOST_AUTO_TEST_CASE(function_overload_is_not_shadowing) { char const* text = R"( contract C { - function f() {} - function f(uint) {} + function f() pure {} + function f(uint) pure {} } )"; CHECK_SUCCESS_NO_WARNINGS(text); @@ -6044,9 +6044,9 @@ BOOST_AUTO_TEST_CASE(function_overload_is_not_shadowing) BOOST_AUTO_TEST_CASE(function_override_is_not_shadowing) { char const* text = R"( - contract D { function f() {} } + contract D { function f() pure {} } contract C is D { - function f(uint) {} + function f(uint) pure {} } )"; CHECK_SUCCESS_NO_WARNINGS(text); @@ -6140,7 +6140,7 @@ BOOST_AUTO_TEST_CASE(does_not_error_transfer_regular_function) { char const* text = R"( contract A { - function transfer() {} + function transfer() pure {} } contract B { @@ -6176,7 +6176,7 @@ BOOST_AUTO_TEST_CASE(warn_unspecified_storage) contract C { struct S { uint a; string b; } S x; - function f() { + function f() view { S storage y = x; y; } @@ -6187,7 +6187,7 @@ BOOST_AUTO_TEST_CASE(warn_unspecified_storage) contract C { struct S { uint a; } S x; - function f() { + function f() view { S y = x; y; } @@ -6213,21 +6213,21 @@ BOOST_AUTO_TEST_CASE(too_large_arrays_for_calldata) { char const* text = R"( contract C { - function f(uint[85678901234] a) external { + function f(uint[85678901234] a) pure external { } } )"; CHECK_ERROR(text, TypeError, "Array is too large to be encoded."); text = R"( contract C { - function f(uint[85678901234] a) internal { + function f(uint[85678901234] a) pure internal { } } )"; CHECK_ERROR(text, TypeError, "Array is too large to be encoded."); text = R"( contract C { - function f(uint[85678901234] a) { + function f(uint[85678901234] a) pure { } } )"; @@ -6238,7 +6238,7 @@ BOOST_AUTO_TEST_CASE(explicit_literal_to_storage_string) { char const* text = R"( contract C { - function f() { + function f() pure { string memory x = "abc"; x; } @@ -6247,7 +6247,7 @@ BOOST_AUTO_TEST_CASE(explicit_literal_to_storage_string) CHECK_SUCCESS_NO_WARNINGS(text); text = R"( contract C { - function f() { + function f() pure { string storage x = "abc"; } } @@ -6255,7 +6255,7 @@ BOOST_AUTO_TEST_CASE(explicit_literal_to_storage_string) CHECK_ERROR(text, TypeError, "Type literal_string \"abc\" is not implicitly convertible to expected type string storage pointer."); text = R"( contract C { - function f() { + function f() pure { string x = "abc"; } } @@ -6263,7 +6263,7 @@ BOOST_AUTO_TEST_CASE(explicit_literal_to_storage_string) CHECK_ERROR(text, TypeError, "Type literal_string \"abc\" is not implicitly convertible to expected type string storage pointer."); text = R"( contract C { - function f() { + function f() pure { string("abc"); } } @@ -6292,7 +6292,7 @@ BOOST_AUTO_TEST_CASE(using_this_in_constructor) function C() { this.f(); } - function f() { + function f() pure { } } )"; @@ -6559,7 +6559,7 @@ BOOST_AUTO_TEST_CASE(tight_packing_literals) { char const* text = R"( contract C { - function f() returns (bytes32) { + function f() pure returns (bytes32) { return keccak256(1); } } @@ -6567,7 +6567,7 @@ BOOST_AUTO_TEST_CASE(tight_packing_literals) CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); text = R"( contract C { - function f() returns (bytes32) { + function f() pure returns (bytes32) { return keccak256(uint8(1)); } } @@ -6575,7 +6575,7 @@ BOOST_AUTO_TEST_CASE(tight_packing_literals) CHECK_SUCCESS_NO_WARNINGS(text); text = R"( contract C { - function f() returns (bytes32) { + function f() pure returns (bytes32) { return sha3(1); } } @@ -6583,7 +6583,7 @@ BOOST_AUTO_TEST_CASE(tight_packing_literals) CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); text = R"( contract C { - function f() returns (bytes32) { + function f() pure returns (bytes32) { return sha256(1); } } @@ -6591,7 +6591,7 @@ BOOST_AUTO_TEST_CASE(tight_packing_literals) CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); text = R"( contract C { - function f() returns (bytes32) { + function f() pure returns (bytes32) { return ripemd160(1); } } diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp new file mode 100644 index 00000000..9cea9850 --- /dev/null +++ b/test/libsolidity/ViewPureChecker.cpp @@ -0,0 +1,398 @@ +/* + 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 view and pure checker. + */ + +#include <test/libsolidity/AnalysisFramework.h> + +#include <boost/test/unit_test.hpp> + +#include <string> + +using namespace std; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +BOOST_FIXTURE_TEST_SUITE(ViewPureChecker, AnalysisFramework) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + char const* text = R"( + contract C { + uint x; + function g() pure {} + function f() view returns (uint) { return now; } + function h() { x = 2; } + function i() payable { x = 2; } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(call_internal_functions_success) +{ + char const* text = R"( + contract C { + function g() pure { g(); } + function f() view returns (uint) { f(); g(); } + function h() { h(); g(); f(); } + function i() payable { i(); h(); g(); f(); } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(suggest_pure) +{ + char const* text = R"( + contract C { + function g() view { } + } + )"; + CHECK_WARNING(text, "can be restricted to pure"); +} + +BOOST_AUTO_TEST_CASE(suggest_view) +{ + char const* text = R"( + contract C { + uint x; + function g() returns (uint) { return x; } + } + )"; + CHECK_WARNING(text, "can be restricted to view"); +} + +BOOST_AUTO_TEST_CASE(call_internal_functions_fail) +{ + CHECK_ERROR( + "contract C{ function f() pure { g(); } function g() view {} }", + TypeError, + "Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires \"view\"" + ); +} + +BOOST_AUTO_TEST_CASE(write_storage_fail) +{ + CHECK_WARNING( + "contract C{ uint x; function f() view { x = 2; } }", + "Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable." + ); +} + +BOOST_AUTO_TEST_CASE(environment_access) +{ + vector<string> view{ + "block.coinbase", + "block.timestamp", + "block.blockhash(7)", + "block.difficulty", + "block.number", + "block.gaslimit", + "msg.gas", + "msg.value", + "msg.sender", + "tx.origin", + "tx.gasprice", + "this", + "address(1).balance" + }; + vector<string> pure{ + "msg.data", + "msg.data[0]", + "msg.sig", + "block.blockhash", // Not evaluating the function + "msg", + "block", + "tx" + }; + for (string const& x: view) + { + CHECK_ERROR( + "contract C { function f() pure { var x = " + x + "; x; } }", + TypeError, + "Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires \"view\"" + ); + } + for (string const& x: pure) + { + CHECK_WARNING( + "contract C { function f() view { var x = " + x + "; x; } }", + "restricted to pure" + ); + } +} + +BOOST_AUTO_TEST_CASE(modifiers) +{ + string text = R"( + contract D { + uint x; + modifier purem(uint) { _; } + modifier viewm(uint) { uint a = x; _; a; } + modifier nonpayablem(uint) { x = 2; _; } + } + contract C is D { + function f() purem(0) pure {} + function g() viewm(0) view {} + function h() nonpayablem(0) {} + function i() purem(x) view {} + function j() viewm(x) view {} + function k() nonpayablem(x) {} + function l() purem(x = 2) {} + function m() viewm(x = 2) {} + function n() nonpayablem(x = 2) {} + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(interface) +{ + string text = R"( + interface D { + function f() view; + } + contract C is D { + function f() view {} + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(overriding) +{ + string text = R"( + contract D { + uint x; + function f() { x = 2; } + } + contract C is D { + function f() {} + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(returning_structs) +{ + string text = R"( + contract C { + struct S { uint x; } + S s; + function f() view internal returns (S storage) { + return s; + } + function g() + { + f().x = 2; + } + function h() view + { + f(); + f().x; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(mappings) +{ + string text = R"( + contract C { + mapping(uint => uint) a; + function f() view { + a; + } + function g() view { + a[2]; + } + function h() { + a[2] = 3; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(local_storage_variables) +{ + string text = R"( + contract C { + struct S { uint a; } + S s; + function f() view { + S storage x = s; + x; + } + function g() view { + S storage x = s; + x = s; + } + function i() { + s.a = 2; + } + function h() { + S storage x = s; + x.a = 2; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(builtin_functions) +{ + string text = R"( + contract C { + function f() { + this.transfer(1); + require(this.send(2)); + selfdestruct(this); + require(this.delegatecall()); + require(this.call()); + } + function g() pure { + var x = keccak256("abc"); + var y = sha256("abc"); + var z = ecrecover(1, 2, 3, 4); + require(true); + assert(true); + x; y; z; + } + function() payable {} + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(function_types) +{ + string text = R"( + contract C { + function f() pure { + function () external nonpayFun; + function () external view viewFun; + function () external pure pureFun; + + nonpayFun; + viewFun; + pureFun; + pureFun(); + } + function g() view { + function () external view viewFun; + + viewFun(); + } + function h() { + function () external nonpayFun; + + nonpayFun(); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(creation) +{ + string text = R"( + contract D {} + contract C { + function f() { new D(); } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(assembly) +{ + string text = R"( + contract C { + struct S { uint x; } + S s; + function e() pure { + assembly { mstore(keccak256(0, 20), mul(s_slot, 2)) } + } + function f() pure { + uint x; + assembly { x := 7 } + } + function g() view { + assembly { for {} 1 { pop(sload(0)) } { } } + } + function h() view { + assembly { function g() { pop(blockhash(20)) } } + } + function j() { + assembly { pop(call(0, 1, 2, 3, 4, 5, 6)) } + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(assembly_staticcall) +{ + string text = R"( + contract C { + function i() view { + assembly { pop(staticcall(0, 1, 2, 3, 4, 5)) } + } + } + )"; + CHECK_WARNING(text, "only available after the Metropolis"); +} + +BOOST_AUTO_TEST_CASE(assembly_jump) +{ + string text = R"( + contract C { + function k() { + assembly { jump(2) } + } + } + )"; + CHECK_WARNING(text, "low-level EVM features"); +} + +BOOST_AUTO_TEST_CASE(constant) +{ + string text = R"( + contract C { + uint constant x = 2; + function k() pure returns (uint) { + return x; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} |