diff options
71 files changed, 385 insertions, 238 deletions
diff --git a/Changelog.md b/Changelog.md index 7d026862..efcea03f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Features: * ABI JSON: Include new field ``statemutability`` with values ``view``, ``nonpayable`` and ``payable``. * Parser: Display previous visibility specifier in error if multiple are found. + * Parser: Introduce ``view`` keyword on functions (``constant`` remains an alias for ``view``). * Syntax Checker: Support ``pragma experimental <feature>;`` to turn on experimental features. * Static Analyzer: Warn about large storage structures. * Metadata: Store experimental flag in metadata CBOR. @@ -10,8 +11,12 @@ Features: * Type Checker: Warn about shifting a literal. Bugfixes: + * Assembly Parser: Be more strict about number literals. * Parser: Enforce commas between array and tuple elements. * Parser: Limit maximum recursion depth. + * Type Checker: Crash fix related to ``using``. + * Type Checker: Disallow constructors in libraries. + * Type Checker: Reject the creation of interface contracts using the ``new`` statement. ### 0.4.15 (2017-08-08) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index a9110a0a..159bd6c7 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -293,7 +293,7 @@ The JSON format for a contract's interface is given by an array of function and/ * `name`: the name of the parameter; * `type`: the canonical type of the parameter. - `outputs`: an array of objects similar to `inputs`, can be omitted if function doesn't return anything; -- `constant`: `true` if function is :ref:`specified to not modify blockchain state <constant-functions>`); +- `constant`: `true` if function is :ref:`specified to not modify blockchain state <view-functions>`); - `payable`: `true` if function accepts ether, defaults to `false`; - `statemutability`: a string with one of the following values: `view` (same as `constant` above), `nonpayable` and `payable` (same as `payable` above). diff --git a/docs/contracts.rst b/docs/contracts.rst index 7b972e17..0f1a882c 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -461,29 +461,32 @@ value types and strings. } -.. _constant-functions: +.. _view-functions: -****************** -Constant Functions -****************** +************** +View Functions +************** -Functions can be declared constant in which case they promise not to modify the state. +Functions can be declared ``view`` in which case they promise not to modify the state. :: pragma solidity ^0.4.0; contract C { - function f(uint a, uint b) constant returns (uint) { + function f(uint a, uint b) view returns (uint) { return a * (b + 42); } } .. note:: - Getter methods are marked constant. + ``constant`` is an alias to ``view``. + +.. note:: + Getter methods are marked ``view``. .. warning:: - The compiler does not enforce yet that a constant method is not modifying state. + The compiler does not enforce yet that a ``view`` method is not modifying state. .. index:: ! fallback function, function;fallback diff --git a/docs/grammar.txt b/docs/grammar.txt index a9838ccb..36ba36f0 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -16,7 +16,7 @@ ContractPart = StateVariableDeclaration | UsingForDeclaration InheritanceSpecifier = UserDefinedTypeName ( '(' Expression ( ',' Expression )* ')' )? -StateVariableDeclaration = TypeName ( 'public' | 'internal' | 'private' )? Identifier ('=' Expression)? ';' +StateVariableDeclaration = TypeName ( 'public' | 'internal' | 'private' | 'constant' )? Identifier ('=' Expression)? ';' UsingForDeclaration = 'using' Identifier 'for' ('*' | TypeName) ';' StructDefinition = 'struct' Identifier '{' ( VariableDeclaration ';' (VariableDeclaration ';')* )? '}' @@ -25,7 +25,7 @@ ModifierDefinition = 'modifier' Identifier ParameterList? Block ModifierInvocation = Identifier ( '(' ExpressionList? ')' )? FunctionDefinition = 'function' Identifier? ParameterList - ( ModifierInvocation | 'constant' | 'payable' | 'external' | 'public' | 'internal' | 'private' )* + ( ModifierInvocation | StateMutability | 'external' | 'public' | 'internal' | 'private' )* ( 'returns' ParameterList )? ( ';' | Block ) EventDefinition = 'event' Identifier IndexedParameterList 'anonymous'? ';' @@ -50,9 +50,10 @@ UserDefinedTypeName = Identifier ( '.' Identifier )* Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')' ArrayTypeName = TypeName '[' Expression? ']' -FunctionTypeName = 'function' TypeNameList ( 'internal' | 'external' | 'constant' | 'payable' )* +FunctionTypeName = 'function' TypeNameList ( 'internal' | 'external' | StateMutability )* ( 'returns' TypeNameList )? StorageLocation = 'memory' | 'storage' +StateMutability = 'constant' | 'view' | 'payable' Block = '{' Statement* '}' Statement = IfStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement | diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 199182d3..7889fff2 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -505,11 +505,12 @@ Function Visibility Specifiers Modifiers ========= +- ``view`` for functions: Disallow modification of state - this is not enforced yet. +- ``payable`` for functions: Allows them to receive Ether together with a call. - ``constant`` for state variables: Disallows assignment (except initialisation), does not occupy storage slot. -- ``constant`` for functions: Disallows modification of state - this is not enforced yet. +- ``constant`` for functions: Same as ``view``. - ``anonymous`` for events: Does not store event signature as topic. - ``indexed`` for event parameters: Stores the parameter as topic. -- ``payable`` for functions: Allows them to receive Ether together with a call. Reserved Keywords ================= diff --git a/docs/types.rst b/docs/types.rst index abf8bb17..9c1c73c6 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -337,7 +337,7 @@ be passed via and returned from external function calls. Function types are notated as follows:: - function (<parameter types>) {internal|external} [constant] [payable] [returns (<return types>)] + function (<parameter types>) {internal|external} [constant|view|payable] [returns (<return types>)] In contrast to the parameter types, the return types cannot be empty - if the function type should not return anything, the whole ``returns (<return types>)`` diff --git a/docs/utils/SolidityLexer.py b/docs/utils/SolidityLexer.py index ef55c6a2..a828146f 100644 --- a/docs/utils/SolidityLexer.py +++ b/docs/utils/SolidityLexer.py @@ -57,7 +57,7 @@ class SolidityLexer(RegexLexer): (r'(anonymous|as|assembly|break|constant|continue|do|delete|else|external|for|hex|if|' r'indexed|internal|import|is|mapping|memory|new|payable|public|pragma|' r'private|return|returns|storage|super|this|throw|using|while)\b', Keyword, 'slashstartsregex'), - (r'(var|function|event|modifier|struct|enum|contract|library)\b', Keyword.Declaration, 'slashstartsregex'), + (r'(var|function|event|modifier|struct|enum|contract|library|interface)\b', Keyword.Declaration, 'slashstartsregex'), (r'(bytes|string|address|uint|int|bool|byte|' + '|'.join( ['uint%d' % (i + 8) for i in range(0, 256, 8)] + @@ -71,7 +71,7 @@ class SolidityLexer(RegexLexer): r'null|of|pure|relocatable|static|switch|try|type|typeof|view)\b', Keyword.Reserved), (r'(true|false)\b', Keyword.Constant), (r'(block|msg|tx|now|suicide|selfdestruct|addmod|mulmod|sha3|keccak256|log[0-4]|' - r'sha256|ecrecover|ripemd160|assert|revert)', Name.Builtin), + r'sha256|ecrecover|ripemd160|assert|revert|require)', Name.Builtin), (r'[$a-zA-Z_][a-zA-Z0-9_]*', Name.Other), (r'[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?', Number.Float), (r'0x[0-9a-fA-F]+', Number.Hex), diff --git a/libdevcore/Common.h b/libdevcore/Common.h index c5b09a80..9d6dd408 100644 --- a/libdevcore/Common.h +++ b/libdevcore/Common.h @@ -44,7 +44,6 @@ #include <unordered_set> #include <functional> #include <string> -#include <chrono> #if defined(__GNUC__) #pragma warning(push) @@ -158,7 +157,7 @@ template <> inline u256 exp10<0>() class ScopeGuard { public: - ScopeGuard(std::function<void(void)> _f): m_f(_f) {} + explicit ScopeGuard(std::function<void(void)> _f): m_f(_f) {} ~ScopeGuard() { m_f(); } private: diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 6f40d7be..0321011e 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -184,7 +184,8 @@ template <class T> inline std::vector<T> operator+(std::vector<T> const& _a, std::vector<T> const& _b) { std::vector<T> ret(_a); - return ret += _b; + ret += _b; + return ret; } template <class T, class V> diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index 5b1c7acf..141e9ffd 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -27,6 +27,7 @@ #include <cstdint> #include <algorithm> #include <boost/functional/hash.hpp> +#include <boost/io/ios_state.hpp> #include "CommonData.h" namespace dev @@ -59,7 +60,7 @@ public: enum ConstructFromHashType { AlignLeft, AlignRight, FailIfDifferent }; /// Construct an empty hash. - FixedHash() { m_data.fill(0); } + explicit FixedHash() { m_data.fill(0); } /// Construct from another hash, filling with zeroes or cropping as necessary. template <unsigned M> explicit FixedHash(FixedHash<M> const& _h, ConstructFromHashType _t = AlignLeft) { m_data.fill(0); unsigned c = std::min(M, N); for (unsigned i = 0; i < c; ++i) m_data[_t == AlignRight ? N - 1 - i : i] = _h[_t == AlignRight ? M - 1 - i : i]; } @@ -224,6 +225,7 @@ template<> inline size_t FixedHash<32>::hash::operator()(FixedHash<32> const& va template <unsigned N> inline std::ostream& operator<<(std::ostream& _out, FixedHash<N> const& _h) { + boost::io::ios_all_saver guard(_out); _out << std::noshowbase << std::hex << std::setfill('0'); for (unsigned i = 0; i < N; ++i) _out << std::setw(2) << (int)_h[i]; diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 42b923df..0a3bf6b8 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -233,7 +233,7 @@ Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes { Json::Value root; - Json::Value collection(Json::arrayValue); + Json::Value& collection = root[".code"] = Json::arrayValue; for (AssemblyItem const& i: m_items) { switch (i.type()) @@ -289,11 +289,9 @@ Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes } } - root[".code"] = collection; - if (!m_data.empty() || !m_subs.empty()) { - Json::Value data; + Json::Value& data = root[".data"] = Json::objectValue; for (auto const& i: m_data) if (u256(i.first) >= m_subs.size()) data[toStringInHex((u256)i.first)] = toHex(i.second); @@ -304,7 +302,6 @@ Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes hexStr << hex << i; data[hexStr.str()] = m_subs[i]->stream(_out, "", _sourceCodes, true); } - root[".data"] = data; } if (m_auxiliaryData.size() > 0) diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index 76104866..419a8c0b 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -219,10 +219,10 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item) _out << "\t" << _item.getJumpTypeAsString(); break; case Push: - _out << " PUSH " << hex << _item.data(); + _out << " PUSH " << hex << _item.data() << dec; break; case PushString: - _out << " PushString" << hex << (unsigned)_item.data(); + _out << " PushString" << hex << (unsigned)_item.data() << dec; break; case PushTag: { @@ -237,19 +237,19 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item) _out << " Tag " << _item.data(); break; case PushData: - _out << " PushData " << hex << (unsigned)_item.data(); + _out << " PushData " << hex << (unsigned)_item.data() << dec; break; case PushSub: - _out << " PushSub " << hex << size_t(_item.data()); + _out << " PushSub " << hex << size_t(_item.data()) << dec; break; case PushSubSize: - _out << " PushSubSize " << hex << size_t(_item.data()); + _out << " PushSubSize " << hex << size_t(_item.data()) << dec; break; case PushProgramSize: _out << " PushProgramSize"; break; case PushLibraryAddress: - _out << " PushLibraryAddress " << hex << h256(_item.data()).abridgedMiddle(); + _out << " PushLibraryAddress " << hex << h256(_item.data()).abridgedMiddle() << dec; break; case UndefinedItem: _out << " ???"; diff --git a/libevmasm/BlockDeduplicator.h b/libevmasm/BlockDeduplicator.h index 797c2476..5640984b 100644 --- a/libevmasm/BlockDeduplicator.h +++ b/libevmasm/BlockDeduplicator.h @@ -45,7 +45,7 @@ using AssemblyItems = std::vector<AssemblyItem>; class BlockDeduplicator { public: - BlockDeduplicator(AssemblyItems& _items): m_items(_items) {} + explicit BlockDeduplicator(AssemblyItems& _items): m_items(_items) {} /// @returns true if something was changed bool deduplicate(); /// @returns the tags that were replaced. diff --git a/libevmasm/CommonSubexpressionEliminator.cpp b/libevmasm/CommonSubexpressionEliminator.cpp index 70324e7f..293cb02c 100644 --- a/libevmasm/CommonSubexpressionEliminator.cpp +++ b/libevmasm/CommonSubexpressionEliminator.cpp @@ -220,6 +220,7 @@ void CSECodeGenerator::addDependencies(Id _c) if (m_neededBy.count(_c)) return; // we already computed the dependencies for _c ExpressionClasses::Expression expr = m_expressionClasses.representative(_c); + assertThrow(expr.item, OptimizerException, ""); if (expr.item->type() == UndefinedItem) BOOST_THROW_EXCEPTION( // If this exception happens, we need to find a different way to generate the diff --git a/libevmasm/CommonSubexpressionEliminator.h b/libevmasm/CommonSubexpressionEliminator.h index 83fc9732..0b957a0e 100644 --- a/libevmasm/CommonSubexpressionEliminator.h +++ b/libevmasm/CommonSubexpressionEliminator.h @@ -61,7 +61,7 @@ public: using Id = ExpressionClasses::Id; using StoreOperation = KnownState::StoreOperation; - CommonSubexpressionEliminator(KnownState const& _state): m_initialState(_state), m_state(_state) {} + explicit CommonSubexpressionEliminator(KnownState const& _state): m_initialState(_state), m_state(_state) {} /// Feeds AssemblyItems into the eliminator and @returns the iterator pointing at the first /// item that must be fed into a new instance of the eliminator. @@ -147,7 +147,7 @@ private: AssemblyItems m_generatedItems; /// Current height of the stack relative to the start. - int m_stackHeight; + int m_stackHeight = 0; /// If (b, a) is in m_requests then b is needed to compute a. std::multimap<Id, Id> m_neededBy; /// Current content of the stack. diff --git a/libevmasm/ConstantOptimiser.cpp b/libevmasm/ConstantOptimiser.cpp index 2ecbfa7f..2efd2dc9 100644 --- a/libevmasm/ConstantOptimiser.cpp +++ b/libevmasm/ConstantOptimiser.cpp @@ -124,7 +124,7 @@ void ConstantOptimisationMethod::replaceConstants( _items = std::move(replaced); } -bigint LiteralMethod::gasNeeded() +bigint LiteralMethod::gasNeeded() const { return combineGas( simpleRunGas({Instruction::PUSH1}), @@ -139,7 +139,7 @@ CodeCopyMethod::CodeCopyMethod(Params const& _params, u256 const& _value): { } -bigint CodeCopyMethod::gasNeeded() +bigint CodeCopyMethod::gasNeeded() const { return combineGas( // Run gas: we ignore memory increase costs @@ -151,7 +151,7 @@ bigint CodeCopyMethod::gasNeeded() ); } -AssemblyItems CodeCopyMethod::execute(Assembly& _assembly) +AssemblyItems CodeCopyMethod::execute(Assembly& _assembly) const { bytes data = toBigEndian(m_value); AssemblyItems actualCopyRoutine = copyRoutine(); @@ -159,7 +159,7 @@ AssemblyItems CodeCopyMethod::execute(Assembly& _assembly) return actualCopyRoutine; } -AssemblyItems const& CodeCopyMethod::copyRoutine() const +AssemblyItems const& CodeCopyMethod::copyRoutine() { AssemblyItems static copyRoutine{ u256(0), @@ -282,7 +282,7 @@ bool ComputeMethod::checkRepresentation(u256 const& _value, AssemblyItems const& return stack.size() == 1 && stack.front() == _value; } -bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine) +bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine) const { size_t numExps = count(_routine.begin(), _routine.end(), Instruction::EXP); return combineGas( diff --git a/libevmasm/ConstantOptimiser.h b/libevmasm/ConstantOptimiser.h index 85bdabac..82982e25 100644 --- a/libevmasm/ConstantOptimiser.h +++ b/libevmasm/ConstantOptimiser.h @@ -63,11 +63,11 @@ public: explicit ConstantOptimisationMethod(Params const& _params, u256 const& _value): m_params(_params), m_value(_value) {} - virtual bigint gasNeeded() = 0; + virtual bigint gasNeeded() const = 0; /// Executes the method, potentially appending to the assembly and returns a vector of /// assembly items the constant should be relpaced with in one sweep. /// If the vector is empty, the constants will not be deleted. - virtual AssemblyItems execute(Assembly& _assembly) = 0; + virtual AssemblyItems execute(Assembly& _assembly) const = 0; protected: size_t dataSize() const { return std::max<size_t>(1, dev::bytesRequired(m_value)); } @@ -84,7 +84,7 @@ protected: bigint const& _runGas, bigint const& _repeatedDataGas, bigint const& _uniqueDataGas - ) + ) const { // _runGas is not multiplied by _multiplicity because the runs are "per opcode" return m_params.runs * _runGas + m_params.multiplicity * _repeatedDataGas + _uniqueDataGas; @@ -106,8 +106,8 @@ class LiteralMethod: public ConstantOptimisationMethod public: explicit LiteralMethod(Params const& _params, u256 const& _value): ConstantOptimisationMethod(_params, _value) {} - virtual bigint gasNeeded() override; - virtual AssemblyItems execute(Assembly&) override { return AssemblyItems{}; } + virtual bigint gasNeeded() const override; + virtual AssemblyItems execute(Assembly&) const override { return AssemblyItems{}; } }; /** @@ -117,11 +117,11 @@ class CodeCopyMethod: public ConstantOptimisationMethod { public: explicit CodeCopyMethod(Params const& _params, u256 const& _value); - virtual bigint gasNeeded() override; - virtual AssemblyItems execute(Assembly& _assembly) override; + virtual bigint gasNeeded() const override; + virtual AssemblyItems execute(Assembly& _assembly) const override; protected: - AssemblyItems const& copyRoutine() const; + static AssemblyItems const& copyRoutine(); }; /** @@ -141,8 +141,8 @@ public: ); } - virtual bigint gasNeeded() override { return gasNeeded(m_routine); } - virtual AssemblyItems execute(Assembly&) override + virtual bigint gasNeeded() const override { return gasNeeded(m_routine); } + virtual AssemblyItems execute(Assembly&) const override { return m_routine; } @@ -151,8 +151,8 @@ protected: /// Tries to recursively find a way to compute @a _value. AssemblyItems findRepresentation(u256 const& _value); /// Recomputes the value from the calculated representation and checks for correctness. - bool checkRepresentation(u256 const& _value, AssemblyItems const& _routine); - bigint gasNeeded(AssemblyItems const& _routine); + static bool checkRepresentation(u256 const& _value, AssemblyItems const& _routine); + bigint gasNeeded(AssemblyItems const& _routine) const; /// Counter for the complexity of optimization, will stop when it reaches zero. size_t m_maxSteps = 10000; diff --git a/libevmasm/PathGasMeter.h b/libevmasm/PathGasMeter.h index 0a0fe5d0..4826eac2 100644 --- a/libevmasm/PathGasMeter.h +++ b/libevmasm/PathGasMeter.h @@ -50,7 +50,7 @@ struct GasPath class PathGasMeter { public: - PathGasMeter(AssemblyItems const& _items); + explicit PathGasMeter(AssemblyItems const& _items); GasMeter::GasConsumption estimateMax(size_t _startIndex, std::shared_ptr<KnownState> const& _state); diff --git a/liblll/CodeFragment.h b/liblll/CodeFragment.h index 6622de3e..95d21563 100644 --- a/liblll/CodeFragment.h +++ b/liblll/CodeFragment.h @@ -50,8 +50,8 @@ public: private: void finalise(CompilerState const& _cs); - template <class T> void error() const { BOOST_THROW_EXCEPTION(T() ); } - template <class T> void error(std::string const& reason) const { + template <class T> static void error() { BOOST_THROW_EXCEPTION(T() ); } + template <class T> static void error(std::string const& reason) { auto err = T(); err << errinfo_comment(reason); BOOST_THROW_EXCEPTION(err); diff --git a/liblll/CompilerState.cpp b/liblll/CompilerState.cpp index 9701e16b..d53dec7e 100644 --- a/liblll/CompilerState.cpp +++ b/liblll/CompilerState.cpp @@ -30,7 +30,7 @@ CompilerState::CompilerState() { } -CodeFragment const& CompilerState::getDef(std::string const& _s) +CodeFragment const& CompilerState::getDef(std::string const& _s) const { if (defs.count(_s)) return defs.at(_s); diff --git a/liblll/CompilerState.h b/liblll/CompilerState.h index c29d3b7d..96a0246d 100644 --- a/liblll/CompilerState.h +++ b/liblll/CompilerState.h @@ -40,7 +40,7 @@ struct CompilerState { CompilerState(); - CodeFragment const& getDef(std::string const& _s); + CodeFragment const& getDef(std::string const& _s) const; void populateStandard(); unsigned stackSize = 128; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 0764bf67..99f3c64c 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -430,7 +430,11 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) m_errorReporter.typeError(_inheritance.location(), "Libraries cannot be inherited from."); auto const& arguments = _inheritance.arguments(); - TypePointers parameterTypes = ContractType(*base).newExpressionType()->parameterTypes(); + TypePointers parameterTypes; + if (base->contractKind() != ContractDefinition::ContractKind::Interface) + // Interfaces do not have constructors, so there are zero parameters. + parameterTypes = ContractType(*base).newExpressionType()->parameterTypes(); + if (!arguments.empty() && parameterTypes.size() != arguments.size()) { m_errorReporter.typeError( @@ -463,7 +467,7 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor) _usingFor.libraryName().annotation().referencedDeclaration ); if (!library || !library->isLibrary()) - m_errorReporter.typeError(_usingFor.libraryName().location(), "Library name expected."); + m_errorReporter.fatalTypeError(_usingFor.libraryName().location(), "Library name expected."); } bool TypeChecker::visit(StructDefinition const& _struct) @@ -546,6 +550,9 @@ bool TypeChecker::visit(FunctionDefinition const& _function) if (_function.isConstructor()) m_errorReporter.typeError(_function.location(), "Constructor cannot be defined in interfaces."); } + else if (m_scope->contractKind() == ContractDefinition::ContractKind::Library) + if (_function.isConstructor()) + m_errorReporter.typeError(_function.location(), "Constructor cannot be defined in libraries."); if (_function.isImplemented()) _function.body().accept(*this); else if (_function.isConstructor()) @@ -1551,6 +1558,8 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) if (!contract) m_errorReporter.fatalTypeError(_newExpression.location(), "Identifier is not a contract."); + if (contract->contractKind() == ContractDefinition::ContractKind::Interface) + m_errorReporter.fatalTypeError(_newExpression.location(), "Cannot instantiate an interface."); if (!contract->annotation().unimplementedFunctions.empty()) m_errorReporter.typeError( _newExpression.location(), diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index e173237e..605daf55 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -188,7 +188,6 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::inter { if (!m_interfaceFunctionList) { - set<string> functionsSeen; set<string> signaturesSeen; m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionTypePointer>>()); for (ContractDefinition const* contract: annotation().linearizedBaseContracts) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 53a34d32..4592a190 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -836,11 +836,10 @@ private: */ class TypeName: public ASTNode { -public: +protected: explicit TypeName(SourceLocation const& _location): ASTNode(_location) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; +public: virtual TypeNameAnnotation& annotation() const override; }; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 3f16db23..f1f6770e 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -15,8 +15,7 @@ along with solidity. If not, see <http://www.gnu.org/licenses/>. */ /** - * @author Lefteris <lefteris@ethdev.com> - * @date 2015 + * @date 2017 * Converts the AST into json format */ @@ -81,28 +80,30 @@ void ASTJsonConverter::setJsonNode( (_nodeType == "InlineAssembly") || (_nodeType == "Throw") ) - { - Json::Value children(Json::arrayValue); - m_currentValue["children"] = children; - } + m_currentValue["children"] = Json::arrayValue; for (auto& e: _attributes) { - if ( - (!e.second.isNull()) && - ( - (e.second.isObject() && e.second.isMember("name")) || - (e.second.isArray() && e.second[0].isObject() && e.second[0].isMember("name")) || - (e.first == "declarations") // (in the case (_,x)= ... there's a nullpointer at [0] - ) - ) + if ((!e.second.isNull()) && ( + (e.second.isObject() && e.second.isMember("name")) || + (e.second.isArray() && e.second[0].isObject() && e.second[0].isMember("name")) || + (e.first == "declarations") // (in the case (_,x)= ... there's a nullpointer at [0] + )) { if (e.second.isObject()) - m_currentValue["children"].append(std::move(e.second)); + { + if (!m_currentValue["children"].isArray()) + m_currentValue["children"] = Json::arrayValue; + appendMove(m_currentValue["children"], std::move(e.second)); + } if (e.second.isArray()) for (auto& child: e.second) if (!child.isNull()) - m_currentValue["children"].append(std::move(child)); + { + if (!m_currentValue["children"].isArray()) + m_currentValue["children"] = Json::arrayValue; + appendMove(m_currentValue["children"], std::move(child)); + } } else { @@ -147,7 +148,7 @@ Json::Value ASTJsonConverter::typePointerToJson(std::shared_ptr<std::vector<Type { Json::Value arguments(Json::arrayValue); for (auto const& tp: *_tps) - arguments.append(typePointerToJson(tp)); + appendMove(arguments, typePointerToJson(tp)); return arguments; } else @@ -186,7 +187,7 @@ void ASTJsonConverter::print(ostream& _stream, ASTNode const& _node) _stream << toJson(_node); } -Json::Value ASTJsonConverter::toJson(ASTNode const& _node) +Json::Value&& ASTJsonConverter::toJson(ASTNode const& _node) { _node.accept(*this); return std::move(m_currentValue); @@ -323,6 +324,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node) { std::vector<pair<string, Json::Value>> attributes = { make_pair("name", _node.name()), + // FIXME: remove with next breaking release make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() == StateMutability::View), make_pair("payable", _node.isPayable()), make_pair("statemutability", stateMutabilityToString(_node.stateMutability())), @@ -378,12 +380,6 @@ bool ASTJsonConverter::visit(ModifierInvocation const& _node) return false; } -bool ASTJsonConverter::visit(TypeName const&) -{ - solAssert(false, "AST node of abstract type used."); - return false; -} - bool ASTJsonConverter::visit(EventDefinition const& _node) { m_inEvent = true; @@ -421,6 +417,7 @@ bool ASTJsonConverter::visit(FunctionTypeName const& _node) make_pair("payable", _node.isPayable()), make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair("statemutability", stateMutabilityToString(_node.stateMutability())), + // FIXME: remove with next breaking release make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() == StateMutability::View), make_pair("parameterTypes", toJson(*_node.parameterTypeList())), make_pair("returnParameterTypes", toJson(*_node.returnParameterTypeList())), @@ -547,7 +544,7 @@ bool ASTJsonConverter::visit(VariableDeclarationStatement const& _node) { Json::Value varDecs(Json::arrayValue); for (auto const& v: _node.annotation().assignments) - varDecs.append(idOrNull(v)); + appendMove(varDecs, idOrNull(v)); setJsonNode(_node, "VariableDeclarationStatement", { make_pair("assignments", std::move(varDecs)), make_pair("declarations", toJson(_node.declarations())), diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index 70e260db..60c660c1 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -49,13 +49,16 @@ public: ); /// Output the json representation of the AST to _stream. void print(std::ostream& _stream, ASTNode const& _node); - Json::Value toJson(ASTNode const& _node); + Json::Value&& toJson(ASTNode const& _node); template <class T> Json::Value toJson(std::vector<ASTPointer<T>> const& _nodes) { Json::Value ret(Json::arrayValue); for (auto const& n: _nodes) - ret.append(n ? toJson(*n) : Json::nullValue); + if (n) + appendMove(ret, toJson(*n)); + else + ret.append(Json::nullValue); return ret; } bool visit(SourceUnit const& _node) override; @@ -73,7 +76,6 @@ public: bool visit(ModifierDefinition const& _node) override; bool visit(ModifierInvocation const& _node) override; bool visit(EventDefinition const& _node) override; - bool visit(TypeName const& _node) override; bool visit(ElementaryTypeName const& _node) override; bool visit(UserDefinedTypeName const& _node) override; bool visit(FunctionTypeName const& _node) override; @@ -119,7 +121,7 @@ private: ); std::string sourceLocationToString(SourceLocation const& _location) const; std::string namePathToString(std::vector<ASTString> const& _namePath) const; - Json::Value idOrNull(ASTNode const* _pt) + static Json::Value idOrNull(ASTNode const* _pt) { return _pt ? Json::Value(nodeId(*_pt)) : Json::nullValue; } @@ -134,12 +136,12 @@ private: std::string literalTokenKind(Token::Value _token); std::string type(Expression const& _expression); std::string type(VariableDeclaration const& _varDecl); - int nodeId(ASTNode const& _node) + static int nodeId(ASTNode const& _node) { return _node.id(); } template<class Container> - Json::Value getContainerIds(Container const& container) + static Json::Value getContainerIds(Container const& container) { Json::Value tmp(Json::arrayValue); for (auto const& element: container) @@ -155,6 +157,12 @@ private: std::vector<std::pair<std::string, Json::Value>> &_attributes, ExpressionAnnotation const& _annotation ); + static void appendMove(Json::Value& _array, Json::Value&& _value) + { + solAssert(_array.isArray(), ""); + _array.append(std::move(_value)); + } + bool m_legacy = false; ///< if true, use legacy format bool m_inEvent = false; ///< whether we are currently inside an event or not Json::Value m_currentValue; diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp index bcb5e3b9..392179ef 100644 --- a/libsolidity/ast/ASTPrinter.cpp +++ b/libsolidity/ast/ASTPrinter.cpp @@ -143,13 +143,6 @@ bool ASTPrinter::visit(EventDefinition const& _node) return goDeeper(); } -bool ASTPrinter::visit(TypeName const& _node) -{ - writeLine("TypeName"); - printSourcePart(_node); - return goDeeper(); -} - bool ASTPrinter::visit(ElementaryTypeName const& _node) { writeLine(string("ElementaryTypeName ") + _node.typeName().toString()); @@ -434,11 +427,6 @@ void ASTPrinter::endVisit(EventDefinition const&) m_indentation--; } -void ASTPrinter::endVisit(TypeName const&) -{ - m_indentation--; -} - void ASTPrinter::endVisit(ElementaryTypeName const&) { m_indentation--; diff --git a/libsolidity/ast/ASTPrinter.h b/libsolidity/ast/ASTPrinter.h index 4a37f17f..d6897dfd 100644 --- a/libsolidity/ast/ASTPrinter.h +++ b/libsolidity/ast/ASTPrinter.h @@ -60,7 +60,6 @@ public: bool visit(ModifierDefinition const& _node) override; bool visit(ModifierInvocation const& _node) override; bool visit(EventDefinition const& _node) override; - bool visit(TypeName const& _node) override; bool visit(ElementaryTypeName const& _node) override; bool visit(UserDefinedTypeName const& _node) override; bool visit(FunctionTypeName const& _node) override; @@ -104,7 +103,6 @@ public: void endVisit(ModifierDefinition const&) override; void endVisit(ModifierInvocation const&) override; void endVisit(EventDefinition const&) override; - void endVisit(TypeName const&) override; void endVisit(ElementaryTypeName const&) override; void endVisit(UserDefinedTypeName const&) override; void endVisit(FunctionTypeName const&) override; @@ -146,7 +144,7 @@ private: std::string m_source; ASTNode const* m_ast; GasEstimator::ASTGasConsumption m_gasCosts; - std::ostream* m_ostream; + std::ostream* m_ostream = nullptr; }; } diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index 20be634b..b726d592 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -58,7 +58,6 @@ public: virtual bool visit(ModifierDefinition& _node) { return visitNode(_node); } virtual bool visit(ModifierInvocation& _node) { return visitNode(_node); } virtual bool visit(EventDefinition& _node) { return visitNode(_node); } - virtual bool visit(TypeName& _node) { return visitNode(_node); } virtual bool visit(ElementaryTypeName& _node) { return visitNode(_node); } virtual bool visit(UserDefinedTypeName& _node) { return visitNode(_node); } virtual bool visit(FunctionTypeName& _node) { return visitNode(_node); } @@ -104,7 +103,6 @@ public: virtual void endVisit(ModifierDefinition& _node) { endVisitNode(_node); } virtual void endVisit(ModifierInvocation& _node) { endVisitNode(_node); } virtual void endVisit(EventDefinition& _node) { endVisitNode(_node); } - virtual void endVisit(TypeName& _node) { endVisitNode(_node); } virtual void endVisit(ElementaryTypeName& _node) { endVisitNode(_node); } virtual void endVisit(UserDefinedTypeName& _node) { endVisitNode(_node); } virtual void endVisit(FunctionTypeName& _node) { endVisitNode(_node); } @@ -162,7 +160,6 @@ public: virtual bool visit(ModifierDefinition const& _node) { return visitNode(_node); } virtual bool visit(ModifierInvocation const& _node) { return visitNode(_node); } virtual bool visit(EventDefinition const& _node) { return visitNode(_node); } - virtual bool visit(TypeName const& _node) { return visitNode(_node); } virtual bool visit(ElementaryTypeName const& _node) { return visitNode(_node); } virtual bool visit(UserDefinedTypeName const& _node) { return visitNode(_node); } virtual bool visit(FunctionTypeName const& _node) { return visitNode(_node); } @@ -208,7 +205,6 @@ public: virtual void endVisit(ModifierDefinition const& _node) { endVisitNode(_node); } virtual void endVisit(ModifierInvocation const& _node) { endVisitNode(_node); } virtual void endVisit(EventDefinition const& _node) { endVisitNode(_node); } - virtual void endVisit(TypeName const& _node) { endVisitNode(_node); } virtual void endVisit(ElementaryTypeName const& _node) { endVisitNode(_node); } virtual void endVisit(UserDefinedTypeName const& _node) { endVisitNode(_node); } virtual void endVisit(FunctionTypeName const& _node) { endVisitNode(_node); } diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index 7c1c64b0..904d9ff6 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -291,18 +291,6 @@ void EventDefinition::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } -void TypeName::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void TypeName::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - void ElementaryTypeName::accept(ASTVisitor& _visitor) { _visitor.visit(*this); diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index e6664895..5e61cdee 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -739,18 +739,18 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const { if (_convertTo.category() == Category::Integer) { - auto targetType = dynamic_cast<IntegerType const*>(&_convertTo); if (m_value == rational(0)) return true; if (isFractional()) return false; - int forSignBit = (targetType->isSigned() ? 1 : 0); + IntegerType const& targetType = dynamic_cast<IntegerType const&>(_convertTo); + int forSignBit = (targetType.isSigned() ? 1 : 0); if (m_value > rational(0)) { - if (m_value.numerator() <= (u256(-1) >> (256 - targetType->numBits() + forSignBit))) + if (m_value.numerator() <= (u256(-1) >> (256 - targetType.numBits() + forSignBit))) return true; } - else if (targetType->isSigned() && -m_value.numerator() <= (u256(1) << (targetType->numBits() - forSignBit))) + else if (targetType.isSigned() && -m_value.numerator() <= (u256(1) << (targetType.numBits() - forSignBit))) return true; return false; } @@ -2140,6 +2140,8 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c strings parameterNames; StateMutability stateMutability = StateMutability::NonPayable; + solAssert(_contract.contractKind() != ContractDefinition::ContractKind::Interface, ""); + if (constructor) { for (ASTPointer<VariableDeclaration> const& var: constructor->parameters()) @@ -2150,6 +2152,7 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c if (constructor->isPayable()) stateMutability = StateMutability::Payable; } + return make_shared<FunctionType>( parameters, TypePointers{make_shared<ContractType>(_contract)}, diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 56546a82..ce2d3bf8 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -993,7 +993,6 @@ public: return *m_declaration; } bool hasDeclaration() const { return !!m_declaration; } - bool isConstant() const { return m_stateMutability == StateMutability::View; } /// @returns true if the the result of this function only depends on its arguments /// and it does not modify the state. /// Currently, this will only return true for internal functions like keccak and ecrecover. diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index 76f4b467..e43e2323 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -162,7 +162,7 @@ private: std::string createFunction(std::string const& _name, std::function<std::string()> const& _creator); /// @returns the size of the static part of the encoding of the given types. - size_t headSize(TypePointers const& _targetTypes); + static size_t headSize(TypePointers const& _targetTypes); /// Map from function name to code for a multi-use function. std::map<std::string, std::string> m_requestedFunctions; diff --git a/libsolidity/codegen/ArrayUtils.h b/libsolidity/codegen/ArrayUtils.h index 806fbea7..f3ddc4ee 100644 --- a/libsolidity/codegen/ArrayUtils.h +++ b/libsolidity/codegen/ArrayUtils.h @@ -40,7 +40,7 @@ using TypePointer = std::shared_ptr<Type const>; class ArrayUtils { public: - ArrayUtils(CompilerContext& _context): m_context(_context) {} + explicit ArrayUtils(CompilerContext& _context): m_context(_context) {} /// Copies an array to an array in storage. The arrays can be of different types only if /// their storage representation is the same. diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index eef078c1..8c63ea9c 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -51,9 +51,9 @@ public: ContractDefinition const& _contract, std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts ); - eth::Assembly const& assembly() { return m_context.assembly(); } - eth::LinkerObject assembledObject() { return m_context.assembledObject(); } - eth::LinkerObject runtimeObject() { return m_context.assembledRuntimeObject(m_runtimeSub); } + eth::Assembly const& assembly() const { return m_context.assembly(); } + eth::LinkerObject assembledObject() const { return m_context.assembledObject(); } + eth::LinkerObject runtimeObject() const { return m_context.assembledRuntimeObject(m_runtimeSub); } /// @arg _sourceCodes is the map of input files to source code strings /// @arg _inJsonFromat shows whether the out should be in Json format Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 583360ea..96cbf6c1 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -208,8 +208,8 @@ public: return m_asm->stream(_stream, "", _sourceCodes, _inJsonFormat); } - eth::LinkerObject const& assembledObject() { return m_asm->assemble(); } - eth::LinkerObject const& assembledRuntimeObject(size_t _subIndex) { return m_asm->sub(_subIndex).assemble(); } + eth::LinkerObject const& assembledObject() const { return m_asm->assemble(); } + eth::LinkerObject const& assembledRuntimeObject(size_t _subIndex) const { return m_asm->sub(_subIndex).assemble(); } /** * Helper class to pop the visited nodes stack when a scope closes diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 09427788..18b70250 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -33,7 +33,7 @@ class Type; // forward class CompilerUtils { public: - CompilerUtils(CompilerContext& _context): m_context(_context) {} + explicit CompilerUtils(CompilerContext& _context): m_context(_context) {} /// Stores the initial value of the free-memory-pointer at its position; void initialiseFreeMemoryPointer(); diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 29a22fae..e53f1b94 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -45,7 +45,7 @@ using namespace dev::solidity; class StackHeightChecker { public: - StackHeightChecker(CompilerContext const& _context): + explicit StackHeightChecker(CompilerContext const& _context): m_context(_context), stackHeight(m_context.stackHeight()) {} void check() { solAssert(m_context.stackHeight() == stackHeight, std::string("I sense a disturbance in the stack: ") + std::to_string(m_context.stackHeight()) + " vs " + std::to_string(stackHeight)); } private: diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 55d35c44..639bfc32 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1811,7 +1811,7 @@ void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression) setLValue<StorageItem>(_expression, *_expression.annotation().type); } -bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token::Value _op) +bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token::Value _op) const { if (Token::isCompareOp(_op) || Token::isShiftOp(_op)) return true; diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h index 3b8cf1c6..5f6c3d64 100644 --- a/libsolidity/codegen/ExpressionCompiler.h +++ b/libsolidity/codegen/ExpressionCompiler.h @@ -119,7 +119,7 @@ private: /// @returns true if the operator applied to the given type requires a cleanup prior to the /// operation. - bool cleanupNeededForOp(Type::Category _type, Token::Value _op); + bool cleanupNeededForOp(Type::Category _type, Token::Value _op) const; /// @returns the CompilerUtils object containing the current context. CompilerUtils utils(); diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp index 74743737..6d0c0255 100644 --- a/libsolidity/inlineasm/AsmCodeGen.cpp +++ b/libsolidity/inlineasm/AsmCodeGen.cpp @@ -52,7 +52,7 @@ using namespace dev::solidity::assembly; class EthAssemblyAdapter: public julia::AbstractAssembly { public: - EthAssemblyAdapter(eth::Assembly& _assembly): + explicit EthAssemblyAdapter(eth::Assembly& _assembly): m_assembly(_assembly) { } @@ -127,7 +127,7 @@ public: } private: - LabelID assemblyTagToIdentifier(eth::AssemblyItem const& _tag) const + static LabelID assemblyTagToIdentifier(eth::AssemblyItem const& _tag) { u256 id = _tag.data(); solAssert(id <= std::numeric_limits<LabelID>::max(), "Tag id too large."); diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 133f70b1..1dcc42b8 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -23,6 +23,9 @@ #include <libsolidity/inlineasm/AsmParser.h> #include <libsolidity/parsing/Scanner.h> #include <libsolidity/interface/ErrorReporter.h> + +#include <boost/algorithm/string.hpp> + #include <ctype.h> #include <algorithm> @@ -297,6 +300,8 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) kind = LiteralKind::String; break; case Token::Number: + if (!isValidNumberLiteral(currentLiteral())) + fatalParserError("Invalid number literal."); kind = LiteralKind::Number; break; case Token::TrueLiteral: @@ -501,3 +506,19 @@ string Parser::expectAsmIdentifier() expectToken(Token::Identifier); return name; } + +bool Parser::isValidNumberLiteral(string const& _literal) +{ + try + { + u256(_literal); + } + catch (...) + { + return false; + } + if (boost::starts_with(_literal, "0x")) + return true; + else + return _literal.find_first_not_of("0123456789") == string::npos; +} diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h index 45708afd..e46d1732 100644 --- a/libsolidity/inlineasm/AsmParser.h +++ b/libsolidity/inlineasm/AsmParser.h @@ -45,7 +45,7 @@ public: protected: /// Creates an inline assembly node with the given source location. - template <class T> T createWithLocation(SourceLocation const& _loc = SourceLocation()) + template <class T> T createWithLocation(SourceLocation const& _loc = SourceLocation()) const { T r; r.location = _loc; @@ -75,6 +75,8 @@ protected: TypedName parseTypedName(); std::string expectAsmIdentifier(); + static bool isValidNumberLiteral(std::string const& _literal); + private: bool m_julia = false; }; diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp index 4f96a3e9..47ede91d 100644 --- a/libsolidity/inlineasm/AsmPrinter.cpp +++ b/libsolidity/inlineasm/AsmPrinter.cpp @@ -209,7 +209,7 @@ string AsmPrinter::operator()(Block const& _block) return "{\n " + body + "\n}"; } -string AsmPrinter::appendTypeName(std::string const& _type) +string AsmPrinter::appendTypeName(std::string const& _type) const { if (m_julia) return ":" + _type; diff --git a/libsolidity/inlineasm/AsmPrinter.h b/libsolidity/inlineasm/AsmPrinter.h index f57dddc8..66520632 100644 --- a/libsolidity/inlineasm/AsmPrinter.h +++ b/libsolidity/inlineasm/AsmPrinter.h @@ -53,7 +53,7 @@ public: std::string operator()(assembly::Block const& _block); private: - std::string appendTypeName(std::string const& _type); + std::string appendTypeName(std::string const& _type) const; bool m_julia = false; }; diff --git a/libsolidity/inlineasm/AsmScope.cpp b/libsolidity/inlineasm/AsmScope.cpp index 315d5953..64d5bd9a 100644 --- a/libsolidity/inlineasm/AsmScope.cpp +++ b/libsolidity/inlineasm/AsmScope.cpp @@ -70,7 +70,7 @@ Scope::Identifier* Scope::lookup(string const& _name) return nullptr; } -bool Scope::exists(string const& _name) +bool Scope::exists(string const& _name) const { if (identifiers.count(_name)) return true; diff --git a/libsolidity/inlineasm/AsmScope.h b/libsolidity/inlineasm/AsmScope.h index cc240565..447d6490 100644 --- a/libsolidity/inlineasm/AsmScope.h +++ b/libsolidity/inlineasm/AsmScope.h @@ -107,7 +107,7 @@ struct Scope } /// @returns true if the name exists in this scope or in super scopes (also searches /// across function and assembly boundaries). - bool exists(std::string const& _name); + bool exists(std::string const& _name) const; /// @returns the number of variables directly registered inside the scope. size_t numberOfVariables() const; diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp index 9fc2f4e8..6e8563f7 100644 --- a/libsolidity/interface/ABI.cpp +++ b/libsolidity/interface/ABI.cpp @@ -19,7 +19,6 @@ */ #include <libsolidity/interface/ABI.h> -#include <boost/range/irange.hpp> #include <libsolidity/ast/AST.h> using namespace std; @@ -36,7 +35,8 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef) Json::Value method; method["type"] = "function"; method["name"] = it.second->declaration().name(); - method["constant"] = it.second->isConstant(); + // TODO: deprecate constant in a future release + method["constant"] = it.second->stateMutability() == StateMutability::View; method["payable"] = it.second->isPayable(); method["statemutability"] = stateMutabilityToString(it.second->stateMutability()); method["inputs"] = formatTypeList( diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 70bebfa5..412d2fd3 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -49,8 +49,6 @@ #include <json/json.h> #include <boost/algorithm/string.hpp> -#include <boost/filesystem.hpp> - using namespace std; using namespace dev; @@ -406,39 +404,42 @@ Json::Value const& CompilerStack::contractABI(Contract const& _contract) const return *_contract.abi; } -Json::Value const& CompilerStack::natspec(string const& _contractName, DocumentationType _type) const +Json::Value const& CompilerStack::natspecUser(string const& _contractName) const { - return natspec(contract(_contractName), _type); + return natspecUser(contract(_contractName)); } -Json::Value const& CompilerStack::natspec(Contract const& _contract, DocumentationType _type) const +Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const { if (m_stackState < AnalysisSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); solAssert(_contract.contract, ""); - std::unique_ptr<Json::Value const>* doc; - // checks wheather we already have the documentation - switch (_type) - { - case DocumentationType::NatspecUser: - doc = &_contract.userDocumentation; - // caches the result - if (!*doc) - doc->reset(new Json::Value(Natspec::userDocumentation(*_contract.contract))); - break; - case DocumentationType::NatspecDev: - doc = &_contract.devDocumentation; - // caches the result - if (!*doc) - doc->reset(new Json::Value(Natspec::devDocumentation(*_contract.contract))); - break; - default: - solAssert(false, "Illegal documentation type."); - } + // caches the result + if (!_contract.userDocumentation) + _contract.userDocumentation.reset(new Json::Value(Natspec::userDocumentation(*_contract.contract))); + + return *_contract.userDocumentation; +} + +Json::Value const& CompilerStack::natspecDev(string const& _contractName) const +{ + return natspecDev(contract(_contractName)); +} + +Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const +{ + if (m_stackState < AnalysisSuccessful) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); + + solAssert(_contract.contract, ""); + + // caches the result + if (!_contract.devDocumentation) + _contract.devDocumentation.reset(new Json::Value(Natspec::devDocumentation(*_contract.contract))); - return *(*doc); + return *_contract.devDocumentation; } Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const @@ -819,8 +820,8 @@ string CompilerStack::createMetadata(Contract const& _contract) const meta["settings"]["libraries"][library.first] = "0x" + toHex(library.second.asBytes()); meta["output"]["abi"] = contractABI(_contract); - meta["output"]["userdoc"] = natspec(_contract, DocumentationType::NatspecUser); - meta["output"]["devdoc"] = natspec(_contract, DocumentationType::NatspecDev); + meta["output"]["userdoc"] = natspecUser(_contract); + meta["output"]["devdoc"] = natspecDev(_contract); return jsonCompactPrint(meta); } diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index d287f224..bb0f4126 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -63,12 +63,6 @@ class Natspec; class Error; class DeclarationContainer; -enum class DocumentationType: uint8_t -{ - NatspecUser = 1, - NatspecDev -}; - /** * Easy to use and self-contained Solidity compiler with as few header dependencies as possible. * It holds state and can be used to either step through the compilation stages (and abort e.g. @@ -94,7 +88,7 @@ public: m_errorReporter(m_errorList) {} /// @returns the list of errors that occured during parsing and type checking. - ErrorList const& errors() { return m_errorReporter.errors(); } + ErrorList const& errors() const { return m_errorReporter.errors(); } /// @returns the current state. State state() const { return m_stackState; } @@ -203,11 +197,13 @@ public: /// Prerequisite: Successful call to parse or compile. Json::Value const& contractABI(std::string const& _contractName = "") const; - /// @returns a JSON representing the contract's documentation. + /// @returns a JSON representing the contract's user documentation. + /// Prerequisite: Successful call to parse or compile. + Json::Value const& natspecUser(std::string const& _contractName) const; + + /// @returns a JSON representing the contract's developer documentation. /// Prerequisite: Successful call to parse or compile. - /// @param type The type of the documentation to get. - /// Can be one of 4 types defined at @c DocumentationType - Json::Value const& natspec(std::string const& _contractName, DocumentationType _type) const; + Json::Value const& natspecDev(std::string const& _contractName) const; /// @returns a JSON representing a map of method identifiers (hashes) to function names. Json::Value methodIdentifiers(std::string const& _contractName) const; @@ -274,7 +270,8 @@ private: std::string createMetadata(Contract const& _contract) const; std::string computeSourceMapping(eth::AssemblyItems const& _items) const; Json::Value const& contractABI(Contract const&) const; - Json::Value const& natspec(Contract const&, DocumentationType _type) const; + Json::Value const& natspecUser(Contract const&) const; + Json::Value const& natspecDev(Contract const&) const; /// @returns the offset of the entry point of the given function into the list of assembly items /// or zero if it is not found or does not exist. diff --git a/libsolidity/interface/ErrorReporter.h b/libsolidity/interface/ErrorReporter.h index 12f4e8df..241d6b43 100644 --- a/libsolidity/interface/ErrorReporter.h +++ b/libsolidity/interface/ErrorReporter.h @@ -36,7 +36,7 @@ class ErrorReporter { public: - ErrorReporter(ErrorList& _errors): + explicit ErrorReporter(ErrorList& _errors): m_errorList(_errors) { } ErrorReporter& operator=(ErrorReporter const& _errorReporter); diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index dd135ce5..7a6f9989 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -394,8 +394,8 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) Json::Value contractData(Json::objectValue); contractData["abi"] = m_compilerStack.contractABI(contractName); contractData["metadata"] = m_compilerStack.metadata(contractName); - contractData["userdoc"] = m_compilerStack.natspec(contractName, DocumentationType::NatspecUser); - contractData["devdoc"] = m_compilerStack.natspec(contractName, DocumentationType::NatspecDev); + contractData["userdoc"] = m_compilerStack.natspecUser(contractName); + contractData["devdoc"] = m_compilerStack.natspecDev(contractName); // EVM Json::Value evmData(Json::objectValue); diff --git a/libsolidity/interface/StandardCompiler.h b/libsolidity/interface/StandardCompiler.h index dfaf88cd..d9787a40 100644 --- a/libsolidity/interface/StandardCompiler.h +++ b/libsolidity/interface/StandardCompiler.h @@ -40,7 +40,7 @@ public: /// Creates a new StandardCompiler. /// @param _readFile callback to used to read files for import statements. Must return /// and must not emit exceptions. - StandardCompiler(ReadFile::Callback const& _readFile = ReadFile::Callback()) + explicit StandardCompiler(ReadFile::Callback const& _readFile = ReadFile::Callback()) : m_compilerStack(_readFile), m_readFile(_readFile) { } diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index d3a6aa45..92a614e0 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -40,7 +40,7 @@ namespace solidity class Parser::ASTNodeFactory { public: - ASTNodeFactory(Parser const& _parser): + explicit ASTNodeFactory(Parser const& _parser): m_parser(_parser), m_location(_parser.position(), -1, _parser.sourceName()) {} ASTNodeFactory(Parser const& _parser, ASTPointer<ASTNode> const& _childNode): m_parser(_parser), m_location(_childNode->location()) {} @@ -69,7 +69,7 @@ private: class Parser::RecursionGuard { public: - RecursionGuard(Parser& _parser): + explicit RecursionGuard(Parser& _parser): m_parser(_parser) { m_parser.increaseRecursionDepth(); @@ -337,7 +337,8 @@ StateMutability Parser::parseStateMutability(Token::Value _token) StateMutability stateMutability(StateMutability::NonPayable); if (_token == Token::Payable) stateMutability = StateMutability::Payable; - else if (_token == Token::Constant) + // FIXME: constant should be removed at the next breaking release + else if (_token == Token::View || _token == Token::Constant) stateMutability = StateMutability::View; else solAssert(false, "Invalid state mutability specifier."); diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 5e6f3ef6..0f74880c 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -35,7 +35,7 @@ class Scanner; class Parser: public ParserBase { public: - Parser(ErrorReporter& _errorReporter): ParserBase(_errorReporter) {} + explicit Parser(ErrorReporter& _errorReporter): ParserBase(_errorReporter) {} ASTPointer<SourceUnit> parse(std::shared_ptr<Scanner> const& _scanner); diff --git a/libsolidity/parsing/ParserBase.h b/libsolidity/parsing/ParserBase.h index 5b03ab5e..48733fc1 100644 --- a/libsolidity/parsing/ParserBase.h +++ b/libsolidity/parsing/ParserBase.h @@ -36,7 +36,7 @@ class Scanner; class ParserBase { public: - ParserBase(ErrorReporter& errorReporter): m_errorReporter(errorReporter) {} + explicit ParserBase(ErrorReporter& errorReporter): m_errorReporter(errorReporter) {} std::shared_ptr<std::string const> const& sourceName() const; diff --git a/libsolidity/parsing/Scanner.h b/libsolidity/parsing/Scanner.h index d6b48c6f..0adaa6fd 100644 --- a/libsolidity/parsing/Scanner.h +++ b/libsolidity/parsing/Scanner.h @@ -75,7 +75,7 @@ public: int position() const { return m_position; } bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_position + _charsForward) >= m_source.size(); } char get(size_t _charsForward = 0) const { return m_source[m_position + _charsForward]; } - char advanceAndGet(size_t _chars=1); + char advanceAndGet(size_t _chars = 1); char rollback(size_t _amount); void reset() { m_position = 0; } @@ -118,11 +118,11 @@ public: ///@name Information about the current token /// @returns the current token - Token::Value currentToken() + Token::Value currentToken() const { return m_currentToken.token; } - ElementaryTypeNameToken currentElementaryTypeNameToken() + ElementaryTypeNameToken currentElementaryTypeNameToken() const { unsigned firstSize; unsigned secondSize; @@ -219,8 +219,8 @@ private: bool scanEscape(); /// Return the current source position. - int sourcePos() { return m_source.position(); } - bool isSourcePastEndOfInput() { return m_source.isPastEndOfInput(); } + int sourcePos() const { return m_source.position(); } + bool isSourcePastEndOfInput() const { return m_source.isPastEndOfInput(); } TokenDesc m_skippedComment; // desc for current skipped comment TokenDesc m_nextSkippedComment; // desc for next skiped comment diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index efbe5e9e..3bc52f1d 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -176,6 +176,7 @@ namespace solidity K(Throw, "throw", 0) \ K(Using, "using", 0) \ K(Var, "var", 0) \ + K(View, "view", 0) \ K(While, "while", 0) \ \ /* Ether subdenominations */ \ @@ -236,7 +237,6 @@ namespace solidity K(Try, "try", 0) \ K(Type, "type", 0) \ K(TypeOf, "typeof", 0) \ - K(View, "view", 0) \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ \ @@ -290,7 +290,7 @@ public: static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; } static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; } static bool isLocationSpecifier(Value op) { return op == Memory || op == Storage; } - static bool isStateMutabilitySpecifier(Value op) { return op == Constant || op == Payable; } + static bool isStateMutabilitySpecifier(Value op) { return op == Constant || op == View || op == Payable; } static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; } static bool isTimeSubdenomination(Value op) { return op == SubSecond || op == SubMinute || op == SubHour || op == SubDay || op == SubWeek || op == SubYear; } static bool isReservedKeyword(Value op) { return (Abstract <= op && op <= TypeOf); } diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 740061a1..bfc53aef 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -316,31 +316,32 @@ void CommandLineInterface::handleABI(string const& _contract) cout << "Contract JSON ABI " << endl << data << endl; } -void CommandLineInterface::handleNatspec(DocumentationType _type, string const& _contract) +void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contract) { std::string argName; std::string suffix; std::string title; - switch(_type) + + if (_natspecDev) { - case DocumentationType::NatspecUser: - argName = g_argNatspecUser; - suffix = ".docuser"; - title = "User Documentation"; - break; - case DocumentationType::NatspecDev: argName = g_argNatspecDev; suffix = ".docdev"; title = "Developer Documentation"; - break; - default: - // should never happen - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation _type")); + } + else + { + argName = g_argNatspecUser; + suffix = ".docuser"; + title = "User Documentation"; } if (m_args.count(argName)) { - std::string output = dev::jsonPrettyPrint(m_compiler->natspec(_contract, _type)); + std::string output = dev::jsonPrettyPrint( + _natspecDev ? + m_compiler->natspecDev(_contract) : + m_compiler->natspecUser(_contract) + ); if (m_args.count(g_argOutputDir)) createFile(m_compiler->filesystemFriendlyName(_contract) + suffix, output); @@ -850,7 +851,7 @@ void CommandLineInterface::handleCombinedJSON() output[g_strContracts] = Json::Value(Json::objectValue); for (string const& contractName: contracts) { - Json::Value contractData(Json::objectValue); + Json::Value& contractData = output[g_strContracts][contractName] = Json::objectValue; if (requests.count(g_strAbi)) contractData[g_strAbi] = dev::jsonCompactPrint(m_compiler->contractABI(contractName)); if (requests.count("metadata")) @@ -881,10 +882,9 @@ void CommandLineInterface::handleCombinedJSON() if (requests.count(g_strSignatureHashes)) contractData[g_strSignatureHashes] = m_compiler->methodIdentifiers(contractName); if (requests.count(g_strNatspecDev)) - contractData[g_strNatspecDev] = dev::jsonCompactPrint(m_compiler->natspec(contractName, DocumentationType::NatspecDev)); + contractData[g_strNatspecDev] = dev::jsonCompactPrint(m_compiler->natspecDev(contractName)); if (requests.count(g_strNatspecUser)) - contractData[g_strNatspecUser] = dev::jsonCompactPrint(m_compiler->natspec(contractName, DocumentationType::NatspecUser)); - output[g_strContracts][contractName] = contractData; + contractData[g_strNatspecUser] = dev::jsonCompactPrint(m_compiler->natspecUser(contractName)); } bool needsSourceList = requests.count(g_strAst) || requests.count(g_strSrcMap) || requests.count(g_strSrcMapRuntime); @@ -1170,8 +1170,8 @@ void CommandLineInterface::outputCompilationResults() handleSignatureHashes(contract); handleMetadata(contract); handleABI(contract); - handleNatspec(DocumentationType::NatspecDev, contract); - handleNatspec(DocumentationType::NatspecUser, contract); + handleNatspec(true, contract); + handleNatspec(false, contract); } // end of contracts iteration if (m_args.count(g_argFormal)) diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 8a476ef5..bf9400e4 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -66,7 +66,7 @@ private: void handleSignatureHashes(std::string const& _contract); void handleMetadata(std::string const& _contract); void handleABI(std::string const& _contract); - void handleNatspec(DocumentationType _type, std::string const& _contract); + void handleNatspec(bool _natspecDev, std::string const& _contract); void handleGasEstimation(std::string const& _contract); void handleFormal(); diff --git a/test/Metadata.cpp b/test/Metadata.cpp index 03f905b1..e4de0a6b 100644 --- a/test/Metadata.cpp +++ b/test/Metadata.cpp @@ -21,7 +21,6 @@ #include <string> #include <iostream> -#include <regex> #include <libdevcore/JSON.h> using namespace std; diff --git a/test/RPCSession.h b/test/RPCSession.h index f7fdd86e..558cb99f 100644 --- a/test/RPCSession.h +++ b/test/RPCSession.h @@ -124,7 +124,7 @@ public: std::string const& accountCreateIfNotExists(size_t _id); private: - RPCSession(std::string const& _path); + explicit RPCSession(std::string const& _path); inline std::string quote(std::string const& _arg) { return "\"" + _arg + "\""; } /// Parse std::string replacing keywords to values diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 5aa81af5..6656f15b 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -29,7 +29,6 @@ #include <boost/test/unit_test.hpp> #include <boost/lexical_cast.hpp> -#include <chrono> #include <string> #include <tuple> #include <memory> diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp index dd6f3d94..e1bf5a3a 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libjulia/Parser.cpp @@ -214,6 +214,14 @@ BOOST_AUTO_TEST_CASE(invalid_types) CHECK_ERROR("{ function f(a:invalid) {} }", TypeError, "\"invalid\" is not a valid type (user defined types are not yet supported)."); } +BOOST_AUTO_TEST_CASE(number_literals) +{ + BOOST_CHECK(successParse("{ let x:u256 := 1:u256 }")); + CHECK_ERROR("{ let x:u256 := .1:u256 }", ParserError, "Invalid number literal."); + CHECK_ERROR("{ let x:u256 := 1e5:u256 }", ParserError, "Invalid number literal."); + CHECK_ERROR("{ let x:u256 := 67.235:u256 }", ParserError, "Invalid number literal."); +} + BOOST_AUTO_TEST_CASE(builtin_types) { BOOST_CHECK(successParse("{ let x:bool := true:bool }")); diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 4bf4eb48..8e1c304a 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -339,6 +339,14 @@ BOOST_AUTO_TEST_CASE(blocks) BOOST_CHECK(successParse("{ let x := 7 { let y := 3 } { let z := 2 } }")); } +BOOST_AUTO_TEST_CASE(number_literals) +{ + BOOST_CHECK(successParse("{ let x := 1 }")); + 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."); +} + BOOST_AUTO_TEST_CASE(function_definitions) { BOOST_CHECK(successParse("{ function f() { } function g(a) -> x { } }")); diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp index 536ba730..0fe7636c 100644 --- a/test/libsolidity/JSONCompiler.cpp +++ b/test/libsolidity/JSONCompiler.cpp @@ -20,8 +20,6 @@ */ #include <string> -#include <iostream> -#include <regex> #include <boost/test/unit_test.hpp> #include <libdevcore/JSON.h> #include <libsolidity/interface/Version.h> diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 80b4b6ad..12fb1f9c 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -250,7 +250,63 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order) checkInterface(sourceCode, interface); } -BOOST_AUTO_TEST_CASE(const_function) +BOOST_AUTO_TEST_CASE(view_function) +{ + char const* sourceCode = R"( + contract test { + function foo(uint a, uint b) returns(uint d) { return a + b; } + function boo(uint32 a) view returns(uint b) { return a * 4; } + } + )"; + + char const* interface = R"([ + { + "name": "foo", + "constant": false, + "payable" : false, + "statemutability": "nonpayable", + "type": "function", + "inputs": [ + { + "name": "a", + "type": "uint256" + }, + { + "name": "b", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "d", + "type": "uint256" + } + ] + }, + { + "name": "boo", + "constant": true, + "payable" : false, + "statemutability": "view", + "type": "function", + "inputs": [{ + "name": "a", + "type": "uint32" + }], + "outputs": [ + { + "name": "b", + "type": "uint256" + } + ] + } + ])"; + + checkInterface(sourceCode, interface); +} + +// constant is an alias to view above +BOOST_AUTO_TEST_CASE(constant_function) { char const* sourceCode = R"( contract test { diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index fb2686fc..380978e8 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -1172,7 +1172,7 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) BOOST_REQUIRE(function && function->hasDeclaration()); auto returnParams = function->returnParameterTypes(); BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(false), "uint256"); - BOOST_CHECK(function->isConstant()); + BOOST_CHECK(function->stateMutability() == StateMutability::View); function = retrieveFunctionBySignature(*contract, "map(uint256)"); BOOST_REQUIRE(function && function->hasDeclaration()); @@ -1180,7 +1180,7 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) BOOST_CHECK_EQUAL(params.at(0)->canonicalName(false), "uint256"); returnParams = function->returnParameterTypes(); BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(false), "bytes4"); - BOOST_CHECK(function->isConstant()); + BOOST_CHECK(function->stateMutability() == StateMutability::View); function = retrieveFunctionBySignature(*contract, "multiple_map(uint256,uint256)"); BOOST_REQUIRE(function && function->hasDeclaration()); @@ -1189,7 +1189,7 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) BOOST_CHECK_EQUAL(params.at(1)->canonicalName(false), "uint256"); returnParams = function->returnParameterTypes(); BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(false), "bytes4"); - BOOST_CHECK(function->isConstant()); + BOOST_CHECK(function->stateMutability() == StateMutability::View); } BOOST_AUTO_TEST_CASE(function_clash_with_state_variable_accessor) @@ -3055,6 +3055,16 @@ BOOST_AUTO_TEST_CASE(library_having_variables) CHECK_ERROR(text, TypeError, "Library cannot have non-constant state variables"); } +BOOST_AUTO_TEST_CASE(library_constructor) +{ + char const* text = R"( + library Lib { + function Lib(); + } + )"; + CHECK_ERROR_ALLOW_MULTI(text, TypeError, "Constructor cannot be defined in libraries."); +} + BOOST_AUTO_TEST_CASE(valid_library) { char const* text = R"( @@ -6647,6 +6657,21 @@ BOOST_AUTO_TEST_CASE(library_function_without_implementation) CHECK_ERROR(text, TypeError, "Internal library function must be implemented if declared."); } +BOOST_AUTO_TEST_CASE(using_for_with_non_library) +{ + // This tests a crash that was resolved by making the first error fatal. + char const* text = R"( + library L { + struct S { uint d; } + using S for S; + function f(S _s) internal { + _s.d = 1; + } + } + )"; + CHECK_ERROR(text, TypeError, "Library name expected."); +} + BOOST_AUTO_TEST_CASE(experimental_pragma) { char const* text = R"( @@ -6684,6 +6709,41 @@ BOOST_AUTO_TEST_CASE(experimental_pragma) // CHECK_ERROR_ALLOW_MULTI(text, SyntaxError, "Duplicate experimental feature name."); } +BOOST_AUTO_TEST_CASE(reject_interface_creation) +{ + char const* text = R"( + interface I {} + contract C { + function f() { + new I(); + } + } + )"; + CHECK_ERROR(text, TypeError, "Cannot instantiate an interface."); +} + +BOOST_AUTO_TEST_CASE(accept_library_creation) +{ + char const* text = R"( + library L {} + contract C { + function f() { + new L(); + } + } + )"; + CHECK_SUCCESS(text); +} + +BOOST_AUTO_TEST_CASE(reject_interface_constructors) +{ + char const* text = R"( + interface I {} + contract C is I(2) {} + )"; + CHECK_ERROR(text, TypeError, "Wrong argument count for constructor call: 1 arguments given but expected 0."); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index be20a9f2..149221d5 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -51,9 +51,9 @@ public: Json::Value generatedDocumentation; if (_userDocumentation) - generatedDocumentation = m_compilerStack.natspec("", DocumentationType::NatspecUser); + generatedDocumentation = m_compilerStack.natspecUser(""); else - generatedDocumentation = m_compilerStack.natspec("", DocumentationType::NatspecDev); + generatedDocumentation = m_compilerStack.natspecDev(""); Json::Value expectedDocumentation; m_reader.parse(_expectedDocumentationString, expectedDocumentation); BOOST_CHECK_MESSAGE( diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 30dc80d9..8e84ead1 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -920,6 +920,11 @@ BOOST_AUTO_TEST_CASE(multiple_statemutability_specifiers) CHECK_PARSE_ERROR(text, "State mutability already specified as \"view\"."); text = R"( contract c { + function f() constant view {} + })"; + CHECK_PARSE_ERROR(text, "State mutability already specified as \"view\"."); + text = R"( + contract c { function f() payable constant {} })"; CHECK_PARSE_ERROR(text, "State mutability already specified as \"payable\"."); diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index be13d46b..79848c36 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -20,8 +20,6 @@ */ #include <string> -#include <iostream> -#include <regex> #include <boost/test/unit_test.hpp> #include <libsolidity/interface/StandardCompiler.h> #include <libdevcore/JSON.h> |