mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #2710 from ethereum/develop
Merge develop into release for 0.4.15
This commit is contained in:
commit
bbb8e64fbe
@ -8,7 +8,7 @@ include(EthPolicy)
|
||||
eth_policy()
|
||||
|
||||
# project name and version should be set after cmake_policy CMP0048
|
||||
set(PROJECT_VERSION "0.4.14")
|
||||
set(PROJECT_VERSION "0.4.15")
|
||||
project(solidity VERSION ${PROJECT_VERSION})
|
||||
|
||||
# Let's find our dependencies
|
||||
|
13
Changelog.md
13
Changelog.md
@ -1,3 +1,16 @@
|
||||
### 0.4.15 (2017-08-08)
|
||||
|
||||
Features:
|
||||
* Type Checker: Show unimplemented function if trying to instantiate an abstract class.
|
||||
|
||||
Bugfixes:
|
||||
* Code Generator: ``.delegatecall()`` should always return execution outcome.
|
||||
* Code Generator: Provide "new account gas" for low-level ``callcode`` and ``delegatecall``.
|
||||
* Type Checker: Constructors must be implemented if declared.
|
||||
* Type Checker: Disallow the ``.gas()`` modifier on ``ecrecover``, ``sha256`` and ``ripemd160``.
|
||||
* Type Checker: Do not mark overloaded functions as shadowing other functions.
|
||||
* Type Checker: Internal library functions must be implemented if declared.
|
||||
|
||||
### 0.4.14 (2017-07-31)
|
||||
|
||||
Features:
|
||||
|
@ -1,14 +1,22 @@
|
||||
[
|
||||
{
|
||||
"name": "DelegateCallReturnValue",
|
||||
"summary": "The low-level .delegatecall() does not return the execution outcome, but converts the value returned by the functioned called to a boolean instead.",
|
||||
"description": "The return value of the low-level .delegatecall() function is taken from a position in memory, where the call data or the return data resides. This value is interpreted as a boolean and put onto the stack. This means if the called function returns at least 32 zero bytes, .delegatecall() returns false even if the call was successuful.",
|
||||
"introduced": "0.3.0",
|
||||
"fixed": "0.4.15",
|
||||
"severity": "low"
|
||||
},
|
||||
{
|
||||
"name": "ECRecoverMalformedInput",
|
||||
"summary": "The ecrecover() builtin can return garbage for malformed input.",
|
||||
"summary": "The ecrecover() builtin can return garbage for malformed input.",
|
||||
"description": "The ecrecover precompile does not properly signal failure for malformed input (especially in the 'v' argument) and thus the Solidity function can return data that was previously present in the return area in memory.",
|
||||
"fixed": "0.4.14",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"name": "SkipEmptyStringLiteral",
|
||||
"summary": "If \"\" is used in a function call, the following function arguments will not be correctly passed to the function.",
|
||||
"summary": "If \"\" is used in a function call, the following function arguments will not be correctly passed to the function.",
|
||||
"description": "If the empty string literal \"\" is used as an argument in a function call, it is skipped by the encoder. This has the effect that the encoding of all arguments following this is shifted left by 32 bytes and thus the function call data is corrupted.",
|
||||
"fixed": "0.4.12",
|
||||
"severity": "low"
|
||||
|
@ -182,6 +182,7 @@
|
||||
},
|
||||
"0.3.0": {
|
||||
"bugs": [
|
||||
"DelegateCallReturnValue",
|
||||
"ECRecoverMalformedInput",
|
||||
"SkipEmptyStringLiteral",
|
||||
"ConstantOptimizerSubtraction",
|
||||
@ -198,6 +199,7 @@
|
||||
},
|
||||
"0.3.1": {
|
||||
"bugs": [
|
||||
"DelegateCallReturnValue",
|
||||
"ECRecoverMalformedInput",
|
||||
"SkipEmptyStringLiteral",
|
||||
"ConstantOptimizerSubtraction",
|
||||
@ -213,6 +215,7 @@
|
||||
},
|
||||
"0.3.2": {
|
||||
"bugs": [
|
||||
"DelegateCallReturnValue",
|
||||
"ECRecoverMalformedInput",
|
||||
"SkipEmptyStringLiteral",
|
||||
"ConstantOptimizerSubtraction",
|
||||
@ -228,6 +231,7 @@
|
||||
},
|
||||
"0.3.3": {
|
||||
"bugs": [
|
||||
"DelegateCallReturnValue",
|
||||
"ECRecoverMalformedInput",
|
||||
"SkipEmptyStringLiteral",
|
||||
"ConstantOptimizerSubtraction",
|
||||
@ -242,6 +246,7 @@
|
||||
},
|
||||
"0.3.4": {
|
||||
"bugs": [
|
||||
"DelegateCallReturnValue",
|
||||
"ECRecoverMalformedInput",
|
||||
"SkipEmptyStringLiteral",
|
||||
"ConstantOptimizerSubtraction",
|
||||
@ -256,6 +261,7 @@
|
||||
},
|
||||
"0.3.5": {
|
||||
"bugs": [
|
||||
"DelegateCallReturnValue",
|
||||
"ECRecoverMalformedInput",
|
||||
"SkipEmptyStringLiteral",
|
||||
"ConstantOptimizerSubtraction",
|
||||
@ -270,6 +276,7 @@
|
||||
},
|
||||
"0.3.6": {
|
||||
"bugs": [
|
||||
"DelegateCallReturnValue",
|
||||
"ECRecoverMalformedInput",
|
||||
"SkipEmptyStringLiteral",
|
||||
"ConstantOptimizerSubtraction",
|
||||
@ -282,6 +289,7 @@
|
||||
},
|
||||
"0.4.0": {
|
||||
"bugs": [
|
||||
"DelegateCallReturnValue",
|
||||
"ECRecoverMalformedInput",
|
||||
"SkipEmptyStringLiteral",
|
||||
"ConstantOptimizerSubtraction",
|
||||
@ -294,6 +302,7 @@
|
||||
},
|
||||
"0.4.1": {
|
||||
"bugs": [
|
||||
"DelegateCallReturnValue",
|
||||
"ECRecoverMalformedInput",
|
||||
"SkipEmptyStringLiteral",
|
||||
"ConstantOptimizerSubtraction",
|
||||
@ -306,6 +315,7 @@
|
||||
},
|
||||
"0.4.10": {
|
||||
"bugs": [
|
||||
"DelegateCallReturnValue",
|
||||
"ECRecoverMalformedInput",
|
||||
"SkipEmptyStringLiteral",
|
||||
"ConstantOptimizerSubtraction"
|
||||
@ -314,6 +324,7 @@
|
||||
},
|
||||
"0.4.11": {
|
||||
"bugs": [
|
||||
"DelegateCallReturnValue",
|
||||
"ECRecoverMalformedInput",
|
||||
"SkipEmptyStringLiteral"
|
||||
],
|
||||
@ -321,22 +332,31 @@
|
||||
},
|
||||
"0.4.12": {
|
||||
"bugs": [
|
||||
"DelegateCallReturnValue",
|
||||
"ECRecoverMalformedInput"
|
||||
],
|
||||
"released": "2017-07-03"
|
||||
},
|
||||
"0.4.13": {
|
||||
"bugs": [
|
||||
"DelegateCallReturnValue",
|
||||
"ECRecoverMalformedInput"
|
||||
],
|
||||
"released": "2017-07-06"
|
||||
},
|
||||
"0.4.14": {
|
||||
"bugs": [],
|
||||
"bugs": [
|
||||
"DelegateCallReturnValue"
|
||||
],
|
||||
"released": "2017-07-31"
|
||||
},
|
||||
"0.4.15": {
|
||||
"bugs": [],
|
||||
"released": "2017-08-08"
|
||||
},
|
||||
"0.4.2": {
|
||||
"bugs": [
|
||||
"DelegateCallReturnValue",
|
||||
"ECRecoverMalformedInput",
|
||||
"SkipEmptyStringLiteral",
|
||||
"ConstantOptimizerSubtraction",
|
||||
@ -348,6 +368,7 @@
|
||||
},
|
||||
"0.4.3": {
|
||||
"bugs": [
|
||||
"DelegateCallReturnValue",
|
||||
"ECRecoverMalformedInput",
|
||||
"SkipEmptyStringLiteral",
|
||||
"ConstantOptimizerSubtraction",
|
||||
@ -358,6 +379,7 @@
|
||||
},
|
||||
"0.4.4": {
|
||||
"bugs": [
|
||||
"DelegateCallReturnValue",
|
||||
"ECRecoverMalformedInput",
|
||||
"SkipEmptyStringLiteral",
|
||||
"ConstantOptimizerSubtraction",
|
||||
@ -367,6 +389,7 @@
|
||||
},
|
||||
"0.4.5": {
|
||||
"bugs": [
|
||||
"DelegateCallReturnValue",
|
||||
"ECRecoverMalformedInput",
|
||||
"SkipEmptyStringLiteral",
|
||||
"ConstantOptimizerSubtraction",
|
||||
@ -377,6 +400,7 @@
|
||||
},
|
||||
"0.4.6": {
|
||||
"bugs": [
|
||||
"DelegateCallReturnValue",
|
||||
"ECRecoverMalformedInput",
|
||||
"SkipEmptyStringLiteral",
|
||||
"ConstantOptimizerSubtraction",
|
||||
@ -386,6 +410,7 @@
|
||||
},
|
||||
"0.4.7": {
|
||||
"bugs": [
|
||||
"DelegateCallReturnValue",
|
||||
"ECRecoverMalformedInput",
|
||||
"SkipEmptyStringLiteral",
|
||||
"ConstantOptimizerSubtraction"
|
||||
@ -394,6 +419,7 @@
|
||||
},
|
||||
"0.4.8": {
|
||||
"bugs": [
|
||||
"DelegateCallReturnValue",
|
||||
"ECRecoverMalformedInput",
|
||||
"SkipEmptyStringLiteral",
|
||||
"ConstantOptimizerSubtraction"
|
||||
@ -402,6 +428,7 @@
|
||||
},
|
||||
"0.4.9": {
|
||||
"bugs": [
|
||||
"DelegateCallReturnValue",
|
||||
"ECRecoverMalformedInput",
|
||||
"SkipEmptyStringLiteral",
|
||||
"ConstantOptimizerSubtraction"
|
||||
|
@ -393,6 +393,9 @@ When exceptions happen in a sub-call, they "bubble up" (i.e. exceptions are reth
|
||||
and the low-level functions ``call``, ``delegatecall`` and ``callcode`` -- those return ``false`` in case
|
||||
of an exception instead of "bubbling up".
|
||||
|
||||
.. warning::
|
||||
The low-level ``call``, ``delegatecall`` and ``callcode`` will return success if the calling account is non-existent, as part of the design of EVM. Existence must be checked prior to calling if desired.
|
||||
|
||||
Catching exceptions is not yet possible.
|
||||
|
||||
In the following example, you can see how ``require`` can be used to easily check conditions on inputs
|
||||
|
@ -56,7 +56,7 @@ repository contains potentially unstable changes in the develop branch.
|
||||
|
||||
docker run ethereum/solc:stable solc --version
|
||||
|
||||
Currenty, the docker image only contains the compiler executable,
|
||||
Currently, the docker image only contains the compiler executable,
|
||||
so you have to do some additional work to link in the source and
|
||||
output directories.
|
||||
|
||||
@ -83,7 +83,15 @@ If you want to use the cutting edge developer version:
|
||||
sudo apt-get update
|
||||
sudo apt-get install solc
|
||||
|
||||
We are also releasing a `snap package <https://snapcraft.io/>`_, which is installable in all the `supported Linux distros <https://snapcraft.io/docs/core/install>`_. To help testing the unstable solc with the most recent changes from the development branch:
|
||||
We are also releasing a `snap package <https://snapcraft.io/>`_, which is installable in all the `supported Linux distros <https://snapcraft.io/docs/core/install>`_. To install the latest stable version of solc:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
sudo snap install solc
|
||||
|
||||
Or if you want to help testing the unstable solc with the most recent changes from the development branch:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
sudo snap install solc --edge
|
||||
|
||||
|
@ -277,9 +277,9 @@ activate themselves.
|
||||
if (highestBidder != 0) {
|
||||
// Sending back the money by simply using
|
||||
// highestBidder.send(highestBid) is a security risk
|
||||
// because it can be prevented by the caller by e.g.
|
||||
// raising the call stack to 1023. It is always safer
|
||||
// to let the recipients withdraw their money themselves.
|
||||
// because it could execute an untrusted contract.
|
||||
// It is always safer to let the recipients
|
||||
// withdraw their money themselves.
|
||||
pendingReturns[highestBidder] += highestBid;
|
||||
}
|
||||
highestBidder = msg.sender;
|
||||
|
@ -53,6 +53,7 @@ public:
|
||||
bool registerDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr, bool _invisible = false, bool _update = false);
|
||||
std::vector<Declaration const*> resolveName(ASTString const& _name, bool _recursive = false) const;
|
||||
ASTNode const* enclosingNode() const { return m_enclosingNode; }
|
||||
DeclarationContainer const* enclosingContainer() const { return m_enclosingContainer; }
|
||||
std::map<ASTString, std::vector<Declaration const*>> const& declarations() const { return m_declarations; }
|
||||
/// @returns whether declaration is valid, and if not also returns previous declaration.
|
||||
Declaration const* conflictingDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr) const;
|
||||
|
@ -452,13 +452,9 @@ bool DeclarationRegistrationHelper::registerDeclaration(
|
||||
_errorLocation = &_declaration.location();
|
||||
|
||||
Declaration const* shadowedDeclaration = nullptr;
|
||||
if (_warnOnShadow && !_declaration.name().empty())
|
||||
for (auto const* decl: _container.resolveName(_declaration.name(), true))
|
||||
if (decl != &_declaration)
|
||||
{
|
||||
shadowedDeclaration = decl;
|
||||
break;
|
||||
}
|
||||
if (_warnOnShadow && !_declaration.name().empty() && _container.enclosingContainer())
|
||||
for (auto const* decl: _container.enclosingContainer()->resolveName(_declaration.name(), true))
|
||||
shadowedDeclaration = decl;
|
||||
|
||||
if (!_container.registerDeclaration(_declaration, _name, !_declaration.isVisibleInContract()))
|
||||
{
|
||||
|
@ -169,6 +169,8 @@ private:
|
||||
void closeCurrentScope();
|
||||
void registerDeclaration(Declaration& _declaration, bool _opensScope);
|
||||
|
||||
static bool isOverloadedFunction(Declaration const& _declaration1, Declaration const& _declaration2);
|
||||
|
||||
/// @returns the canonical name of the current scope.
|
||||
std::string currentCanonicalName() const;
|
||||
|
||||
|
@ -112,8 +112,6 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
|
||||
m_errorReporter.typeError(fallbackFunction->returnParameterList()->location(), "Fallback function cannot return values.");
|
||||
}
|
||||
}
|
||||
if (!function->isImplemented())
|
||||
_contract.annotation().isFullyImplemented = false;
|
||||
}
|
||||
|
||||
for (auto const& n: _contract.subNodes())
|
||||
@ -188,20 +186,13 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont
|
||||
using FunTypeAndFlag = std::pair<FunctionTypePointer, bool>;
|
||||
map<string, vector<FunTypeAndFlag>> functions;
|
||||
|
||||
bool allBaseConstructorsImplemented = true;
|
||||
// Search from base to derived
|
||||
for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts))
|
||||
for (FunctionDefinition const* function: contract->definedFunctions())
|
||||
{
|
||||
// Take constructors out of overload hierarchy
|
||||
if (function->isConstructor())
|
||||
{
|
||||
if (!function->isImplemented())
|
||||
// Base contract's constructor is not fully implemented, no way to get
|
||||
// out of this.
|
||||
allBaseConstructorsImplemented = false;
|
||||
continue;
|
||||
}
|
||||
auto& overloads = functions[function->name()];
|
||||
FunctionTypePointer funType = make_shared<FunctionType>(*function);
|
||||
auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag)
|
||||
@ -219,16 +210,15 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont
|
||||
it->second = true;
|
||||
}
|
||||
|
||||
if (!allBaseConstructorsImplemented)
|
||||
_contract.annotation().isFullyImplemented = false;
|
||||
|
||||
// Set to not fully implemented if at least one flag is false.
|
||||
for (auto const& it: functions)
|
||||
for (auto const& funAndFlag: it.second)
|
||||
if (!funAndFlag.second)
|
||||
{
|
||||
_contract.annotation().isFullyImplemented = false;
|
||||
return;
|
||||
FunctionDefinition const* function = dynamic_cast<FunctionDefinition const*>(&funAndFlag.first->declaration());
|
||||
solAssert(function, "");
|
||||
_contract.annotation().unimplementedFunctions.push_back(function);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -266,7 +256,8 @@ void TypeChecker::checkContractAbstractConstructors(ContractDefinition const& _c
|
||||
}
|
||||
}
|
||||
if (!argumentsNeeded.empty())
|
||||
_contract.annotation().isFullyImplemented = false;
|
||||
for (ContractDefinition const* contract: argumentsNeeded)
|
||||
_contract.annotation().unimplementedFunctions.push_back(contract->constructor());
|
||||
}
|
||||
|
||||
void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contract)
|
||||
@ -525,6 +516,10 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
||||
}
|
||||
if (_function.isImplemented())
|
||||
_function.body().accept(*this);
|
||||
else if (_function.isConstructor())
|
||||
m_errorReporter.typeError(_function.location(), "Constructor must be implemented if declared.");
|
||||
else if (isLibraryFunction && _function.visibility() <= FunctionDefinition::Visibility::Internal)
|
||||
m_errorReporter.typeError(_function.location(), "Internal library function must be implemented if declared.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1050,7 +1045,7 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement)
|
||||
{
|
||||
auto kind = callType->kind();
|
||||
if (
|
||||
kind == FunctionType::Kind::Bare ||
|
||||
kind == FunctionType::Kind::BareCall ||
|
||||
kind == FunctionType::Kind::BareCallCode ||
|
||||
kind == FunctionType::Kind::BareDelegateCall
|
||||
)
|
||||
@ -1523,8 +1518,15 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
|
||||
|
||||
if (!contract)
|
||||
m_errorReporter.fatalTypeError(_newExpression.location(), "Identifier is not a contract.");
|
||||
if (!contract->annotation().isFullyImplemented)
|
||||
m_errorReporter.typeError(_newExpression.location(), "Trying to create an instance of an abstract contract.");
|
||||
if (!contract->annotation().unimplementedFunctions.empty())
|
||||
m_errorReporter.typeError(
|
||||
_newExpression.location(),
|
||||
SecondarySourceLocation().append(
|
||||
"Missing implementation:",
|
||||
contract->annotation().unimplementedFunctions.front()->location()
|
||||
),
|
||||
"Trying to create an instance of an abstract contract."
|
||||
);
|
||||
if (!contract->constructorIsPublic())
|
||||
m_errorReporter.typeError(_newExpression.location(), "Contract with internal constructor cannot be created directly.");
|
||||
|
||||
|
@ -79,8 +79,8 @@ struct TypeDeclarationAnnotation: ASTAnnotation
|
||||
|
||||
struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, DocumentedAnnotation
|
||||
{
|
||||
/// Whether all functions are implemented.
|
||||
bool isFullyImplemented = true;
|
||||
/// List of functions without a body. Can also contain functions from base classes.
|
||||
std::vector<FunctionDefinition const*> unimplementedFunctions;
|
||||
/// List of all (direct and indirect) base contracts in order from derived to
|
||||
/// base, including the contract itself.
|
||||
std::vector<ContractDefinition const*> linearizedBaseContracts;
|
||||
|
@ -253,7 +253,7 @@ bool ASTJsonConverter::visit(ContractDefinition const& _node)
|
||||
make_pair("name", _node.name()),
|
||||
make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue),
|
||||
make_pair("contractKind", contractKind(_node.contractKind())),
|
||||
make_pair("fullyImplemented", _node.annotation().isFullyImplemented),
|
||||
make_pair("fullyImplemented", _node.annotation().unimplementedFunctions.empty()),
|
||||
make_pair("linearizedBaseContracts", getContainerIds(_node.annotation().linearizedBaseContracts)),
|
||||
make_pair("baseContracts", toJson(_node.baseContracts())),
|
||||
make_pair("contractDependencies", getContainerIds(_node.annotation().contractDependencies)),
|
||||
|
@ -477,7 +477,7 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons
|
||||
if (isAddress())
|
||||
return {
|
||||
{"balance", make_shared<IntegerType >(256)},
|
||||
{"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::Bare, true, false, true)},
|
||||
{"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareCall, true, false, true)},
|
||||
{"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareCallCode, true, false, true)},
|
||||
{"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareDelegateCall, true)},
|
||||
{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)},
|
||||
@ -2178,7 +2178,7 @@ string FunctionType::identifier() const
|
||||
case Kind::External: id += "external"; break;
|
||||
case Kind::CallCode: id += "callcode"; break;
|
||||
case Kind::DelegateCall: id += "delegatecall"; break;
|
||||
case Kind::Bare: id += "bare"; break;
|
||||
case Kind::BareCall: id += "barecall"; break;
|
||||
case Kind::BareCallCode: id += "barecallcode"; break;
|
||||
case Kind::BareDelegateCall: id += "baredelegatecall"; break;
|
||||
case Kind::Creation: id += "creation"; break;
|
||||
@ -2346,7 +2346,7 @@ unsigned FunctionType::sizeOnStack() const
|
||||
unsigned size = 0;
|
||||
if (kind == Kind::External || kind == Kind::CallCode || kind == Kind::DelegateCall)
|
||||
size = 2;
|
||||
else if (kind == Kind::Bare || kind == Kind::BareCallCode || kind == Kind::BareDelegateCall)
|
||||
else if (kind == Kind::BareCall || kind == Kind::BareCallCode || kind == Kind::BareDelegateCall)
|
||||
size = 1;
|
||||
else if (kind == Kind::Internal)
|
||||
size = 1;
|
||||
@ -2402,10 +2402,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
|
||||
{
|
||||
case Kind::External:
|
||||
case Kind::Creation:
|
||||
case Kind::ECRecover:
|
||||
case Kind::SHA256:
|
||||
case Kind::RIPEMD160:
|
||||
case Kind::Bare:
|
||||
case Kind::BareCall:
|
||||
case Kind::BareCallCode:
|
||||
case Kind::BareDelegateCall:
|
||||
{
|
||||
@ -2509,7 +2506,7 @@ bool FunctionType::isBareCall() const
|
||||
{
|
||||
switch (m_kind)
|
||||
{
|
||||
case Kind::Bare:
|
||||
case Kind::BareCall:
|
||||
case Kind::BareCallCode:
|
||||
case Kind::BareDelegateCall:
|
||||
case Kind::ECRecover:
|
||||
|
@ -838,7 +838,7 @@ public:
|
||||
External, ///< external call using CALL
|
||||
CallCode, ///< external call using CALLCODE, i.e. not exchanging the storage
|
||||
DelegateCall, ///< external call using DELEGATECALL, i.e. not exchanging the storage
|
||||
Bare, ///< CALL without function hash
|
||||
BareCall, ///< CALL without function hash
|
||||
BareCallCode, ///< CALLCODE without function hash
|
||||
BareDelegateCall, ///< DELEGATECALL without function hash
|
||||
Creation, ///< external call using CREATE
|
||||
|
@ -546,7 +546,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
case FunctionType::Kind::External:
|
||||
case FunctionType::Kind::CallCode:
|
||||
case FunctionType::Kind::DelegateCall:
|
||||
case FunctionType::Kind::Bare:
|
||||
case FunctionType::Kind::BareCall:
|
||||
case FunctionType::Kind::BareCallCode:
|
||||
case FunctionType::Kind::BareDelegateCall:
|
||||
_functionCall.expression().accept(*this);
|
||||
@ -642,7 +642,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
TypePointers{},
|
||||
strings(),
|
||||
strings(),
|
||||
FunctionType::Kind::Bare,
|
||||
FunctionType::Kind::BareCall,
|
||||
false,
|
||||
nullptr,
|
||||
false,
|
||||
@ -973,7 +973,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
||||
case FunctionType::Kind::DelegateCall:
|
||||
case FunctionType::Kind::CallCode:
|
||||
case FunctionType::Kind::Send:
|
||||
case FunctionType::Kind::Bare:
|
||||
case FunctionType::Kind::BareCall:
|
||||
case FunctionType::Kind::BareCallCode:
|
||||
case FunctionType::Kind::BareDelegateCall:
|
||||
case FunctionType::Kind::Transfer:
|
||||
@ -1560,7 +1560,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
||||
utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack());
|
||||
|
||||
auto funKind = _functionType.kind();
|
||||
bool returnSuccessCondition = funKind == FunctionType::Kind::Bare || funKind == FunctionType::Kind::BareCallCode;
|
||||
bool returnSuccessCondition = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall;
|
||||
bool isCallCode = funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::CallCode;
|
||||
bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
|
||||
|
||||
@ -1579,7 +1579,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
||||
TypePointers parameterTypes = _functionType.parameterTypes();
|
||||
bool manualFunctionId = false;
|
||||
if (
|
||||
(funKind == FunctionType::Kind::Bare || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall) &&
|
||||
(funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall) &&
|
||||
!_arguments.empty()
|
||||
)
|
||||
{
|
||||
@ -1712,7 +1712,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
||||
u256 gasNeededByCaller = eth::GasCosts::callGas + 10;
|
||||
if (_functionType.valueSet())
|
||||
gasNeededByCaller += eth::GasCosts::callValueTransferGas;
|
||||
if (!isCallCode && !isDelegateCall && !existenceChecked)
|
||||
if (!existenceChecked)
|
||||
gasNeededByCaller += eth::GasCosts::callNewAccountGas; // we never know
|
||||
m_context << gasNeededByCaller << Instruction::GAS << Instruction::SUB;
|
||||
}
|
||||
|
@ -639,7 +639,7 @@ void CompilerStack::compileContract(
|
||||
{
|
||||
if (
|
||||
_compiledContracts.count(&_contract) ||
|
||||
!_contract.annotation().isFullyImplemented ||
|
||||
!_contract.annotation().unimplementedFunctions.empty() ||
|
||||
!_contract.constructorIsPublic()
|
||||
)
|
||||
return;
|
||||
|
@ -151,6 +151,16 @@ void ErrorReporter::syntaxError(SourceLocation const& _location, string const& _
|
||||
);
|
||||
}
|
||||
|
||||
void ErrorReporter::typeError(SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
|
||||
{
|
||||
error(
|
||||
Error::Type::TypeError,
|
||||
_location,
|
||||
_secondaryLocation,
|
||||
_description
|
||||
);
|
||||
}
|
||||
|
||||
void ErrorReporter::typeError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
error(
|
||||
|
@ -73,6 +73,12 @@ public:
|
||||
|
||||
void syntaxError(SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
void typeError(
|
||||
SourceLocation const& _location,
|
||||
SecondarySourceLocation const& _secondaryLocation,
|
||||
std::string const& _description
|
||||
);
|
||||
|
||||
void typeError(SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
void fatalTypeError(SourceLocation const& _location, std::string const& _description);
|
||||
|
@ -101,6 +101,8 @@ void SourceReferenceFormatter::printExceptionInformation(
|
||||
_stream << _name;
|
||||
if (string const* description = boost::get_error_info<errinfo_comment>(_exception))
|
||||
_stream << ": " << *description << endl;
|
||||
else
|
||||
_stream << endl;
|
||||
|
||||
printSourceLocation(_stream, location, _scannerFromSourceName);
|
||||
|
||||
@ -108,9 +110,8 @@ void SourceReferenceFormatter::printExceptionInformation(
|
||||
{
|
||||
for (auto info: secondarylocation->infos)
|
||||
{
|
||||
_stream << info.first << " ";
|
||||
printSourceName(_stream, &info.second, _scannerFromSourceName);
|
||||
_stream << endl;
|
||||
_stream << info.first << endl;
|
||||
printSourceLocation(_stream, &info.second, _scannerFromSourceName);
|
||||
}
|
||||
_stream << endl;
|
||||
|
@ -1950,6 +1950,87 @@ BOOST_AUTO_TEST_CASE(ripemd)
|
||||
testContractAgainstCpp("a(bytes32)", f, u256(-1));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(packed_keccak256)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function a(bytes32 input) returns (bytes32 hash) {
|
||||
var b = 65536;
|
||||
uint c = 256;
|
||||
return keccak256(8, input, b, input, c);
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode);
|
||||
auto f = [&](u256 const& _x) -> u256
|
||||
{
|
||||
return dev::keccak256(
|
||||
toCompactBigEndian(unsigned(8)) +
|
||||
toBigEndian(_x) +
|
||||
toCompactBigEndian(unsigned(65536)) +
|
||||
toBigEndian(_x) +
|
||||
toBigEndian(u256(256))
|
||||
);
|
||||
};
|
||||
testContractAgainstCpp("a(bytes32)", f, u256(4));
|
||||
testContractAgainstCpp("a(bytes32)", f, u256(5));
|
||||
testContractAgainstCpp("a(bytes32)", f, u256(-1));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(packed_sha256)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function a(bytes32 input) returns (bytes32 hash) {
|
||||
var b = 65536;
|
||||
uint c = 256;
|
||||
return sha256(8, input, b, input, c);
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode);
|
||||
auto f = [&](u256 const& _x) -> bytes
|
||||
{
|
||||
if (_x == u256(4))
|
||||
return fromHex("804e0d7003cfd70fc925dc103174d9f898ebb142ecc2a286da1abd22ac2ce3ac");
|
||||
if (_x == u256(5))
|
||||
return fromHex("e94921945f9068726c529a290a954f412bcac53184bb41224208a31edbf63cf0");
|
||||
if (_x == u256(-1))
|
||||
return fromHex("f14def4d07cd185ddd8b10a81b2238326196a38867e6e6adbcc956dc913488c7");
|
||||
return fromHex("");
|
||||
};
|
||||
testContractAgainstCpp("a(bytes32)", f, u256(4));
|
||||
testContractAgainstCpp("a(bytes32)", f, u256(5));
|
||||
testContractAgainstCpp("a(bytes32)", f, u256(-1));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(packed_ripemd160)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function a(bytes32 input) returns (bytes32 hash) {
|
||||
var b = 65536;
|
||||
uint c = 256;
|
||||
return ripemd160(8, input, b, input, c);
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode);
|
||||
auto f = [&](u256 const& _x) -> bytes
|
||||
{
|
||||
if (_x == u256(4))
|
||||
return fromHex("f93175303eba2a7b372174fc9330237f5ad202fc000000000000000000000000");
|
||||
if (_x == u256(5))
|
||||
return fromHex("04f4fc112e2bfbe0d38f896a46629e08e2fcfad5000000000000000000000000");
|
||||
if (_x == u256(-1))
|
||||
return fromHex("c0a2e4b1f3ff766a9a0089e7a410391730872495000000000000000000000000");
|
||||
return fromHex("");
|
||||
};
|
||||
testContractAgainstCpp("a(bytes32)", f, u256(4));
|
||||
testContractAgainstCpp("a(bytes32)", f, u256(5));
|
||||
testContractAgainstCpp("a(bytes32)", f, u256(-1));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ecrecover)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
@ -2318,21 +2399,6 @@ BOOST_AUTO_TEST_CASE(gas_and_value_basic)
|
||||
BOOST_REQUIRE(callContractFunction("checkState()") == encodeArgs(false, 20 - 5));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(gas_for_builtin)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract Contract {
|
||||
function test(uint g) returns (bytes32 data, bool flag) {
|
||||
data = ripemd160.gas(g)("abc");
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode);
|
||||
BOOST_CHECK(callContractFunction("test(uint256)", 500) == bytes());
|
||||
BOOST_CHECK(callContractFunction("test(uint256)", 800) == encodeArgs(u256("0x8eb208f7e05d987a9b044a8e98c6b087f15a0bfc000000000000000000000000"), true));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(value_complex)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
@ -9816,6 +9882,64 @@ BOOST_AUTO_TEST_CASE(inlineasm_empty_let)
|
||||
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0), u256(0)));
|
||||
}
|
||||
|
||||
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) {
|
||||
return address(0x4242).call();
|
||||
}
|
||||
function g() external constant returns (bool) {
|
||||
return address(0x4242).callcode();
|
||||
}
|
||||
function h() external constant returns (bool) {
|
||||
return address(0x4242).delegatecall();
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode, 0, "C");
|
||||
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(1)));
|
||||
BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(1)));
|
||||
BOOST_CHECK(callContractFunction("h()") == encodeArgs(u256(1)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(delegatecall_return_value)
|
||||
{
|
||||
char const* sourceCode = R"DELIMITER(
|
||||
contract C {
|
||||
uint value;
|
||||
function set(uint _value) external {
|
||||
value = _value;
|
||||
}
|
||||
function get() external constant returns (uint) {
|
||||
return value;
|
||||
}
|
||||
function get_delegated() external constant returns (bool) {
|
||||
return this.delegatecall(bytes4(sha3("get()")));
|
||||
}
|
||||
function assert0() external constant {
|
||||
assert(value == 0);
|
||||
}
|
||||
function assert0_delegated() external constant returns (bool) {
|
||||
return this.delegatecall(bytes4(sha3("assert0()")));
|
||||
}
|
||||
}
|
||||
)DELIMITER";
|
||||
compileAndRun(sourceCode, 0, "C");
|
||||
BOOST_CHECK(callContractFunction("get()") == encodeArgs(u256(0)));
|
||||
BOOST_CHECK(callContractFunction("assert0_delegated()") == encodeArgs(u256(1)));
|
||||
BOOST_CHECK(callContractFunction("get_delegated()") == encodeArgs(u256(1)));
|
||||
BOOST_CHECK(callContractFunction("set(uint256)", u256(1)) == encodeArgs());
|
||||
BOOST_CHECK(callContractFunction("get()") == encodeArgs(u256(1)));
|
||||
BOOST_CHECK(callContractFunction("assert0_delegated()") == encodeArgs(u256(0)));
|
||||
BOOST_CHECK(callContractFunction("get_delegated()") == encodeArgs(u256(1)));
|
||||
BOOST_CHECK(callContractFunction("set(uint256)", u256(42)) == encodeArgs());
|
||||
BOOST_CHECK(callContractFunction("get()") == encodeArgs(u256(42)));
|
||||
BOOST_CHECK(callContractFunction("assert0_delegated()") == encodeArgs(u256(0)));
|
||||
BOOST_CHECK(callContractFunction("get_delegated()") == encodeArgs(u256(1)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
}
|
||||
|
@ -624,7 +624,7 @@ BOOST_AUTO_TEST_CASE(function_no_implementation)
|
||||
std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes();
|
||||
ContractDefinition* contract = dynamic_cast<ContractDefinition*>(nodes[1].get());
|
||||
BOOST_REQUIRE(contract);
|
||||
BOOST_CHECK(!contract->annotation().isFullyImplemented);
|
||||
BOOST_CHECK(!contract->annotation().unimplementedFunctions.empty());
|
||||
BOOST_CHECK(!contract->definedFunctions()[0]->isImplemented());
|
||||
}
|
||||
|
||||
@ -640,10 +640,10 @@ BOOST_AUTO_TEST_CASE(abstract_contract)
|
||||
ContractDefinition* base = dynamic_cast<ContractDefinition*>(nodes[1].get());
|
||||
ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get());
|
||||
BOOST_REQUIRE(base);
|
||||
BOOST_CHECK(!base->annotation().isFullyImplemented);
|
||||
BOOST_CHECK(!base->annotation().unimplementedFunctions.empty());
|
||||
BOOST_CHECK(!base->definedFunctions()[0]->isImplemented());
|
||||
BOOST_REQUIRE(derived);
|
||||
BOOST_CHECK(derived->annotation().isFullyImplemented);
|
||||
BOOST_CHECK(derived->annotation().unimplementedFunctions.empty());
|
||||
BOOST_CHECK(derived->definedFunctions()[0]->isImplemented());
|
||||
}
|
||||
|
||||
@ -659,9 +659,9 @@ BOOST_AUTO_TEST_CASE(abstract_contract_with_overload)
|
||||
ContractDefinition* base = dynamic_cast<ContractDefinition*>(nodes[1].get());
|
||||
ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get());
|
||||
BOOST_REQUIRE(base);
|
||||
BOOST_CHECK(!base->annotation().isFullyImplemented);
|
||||
BOOST_CHECK(!base->annotation().unimplementedFunctions.empty());
|
||||
BOOST_REQUIRE(derived);
|
||||
BOOST_CHECK(!derived->annotation().isFullyImplemented);
|
||||
BOOST_CHECK(!derived->annotation().unimplementedFunctions.empty());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(create_abstract_contract)
|
||||
@ -677,44 +677,6 @@ BOOST_AUTO_TEST_CASE(create_abstract_contract)
|
||||
CHECK_ERROR(text, TypeError, "");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(abstract_contract_constructor_args_optional)
|
||||
{
|
||||
ASTPointer<SourceUnit> sourceUnit;
|
||||
char const* text = R"(
|
||||
contract BaseBase { function BaseBase(uint j); }
|
||||
contract base is BaseBase { function foo(); }
|
||||
contract derived is base {
|
||||
function derived(uint i) BaseBase(i){}
|
||||
function foo() {}
|
||||
}
|
||||
)";
|
||||
ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name resolving failed");
|
||||
std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes();
|
||||
BOOST_CHECK_EQUAL(nodes.size(), 4);
|
||||
ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[3].get());
|
||||
BOOST_REQUIRE(derived);
|
||||
BOOST_CHECK(!derived->annotation().isFullyImplemented);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(abstract_contract_constructor_args_not_provided)
|
||||
{
|
||||
ASTPointer<SourceUnit> sourceUnit;
|
||||
char const* text = R"(
|
||||
contract BaseBase { function BaseBase(uint); }
|
||||
contract base is BaseBase { function foo(); }
|
||||
contract derived is base {
|
||||
function derived(uint) {}
|
||||
function foo() {}
|
||||
}
|
||||
)";
|
||||
ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name resolving failed");
|
||||
std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes();
|
||||
BOOST_CHECK_EQUAL(nodes.size(), 4);
|
||||
ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[3].get());
|
||||
BOOST_REQUIRE(derived);
|
||||
BOOST_CHECK(!derived->annotation().isFullyImplemented);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract)
|
||||
{
|
||||
ASTPointer<SourceUnit> sourceUnit;
|
||||
@ -738,7 +700,7 @@ BOOST_AUTO_TEST_CASE(implement_abstract_via_constructor)
|
||||
BOOST_CHECK_EQUAL(nodes.size(), 3);
|
||||
ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get());
|
||||
BOOST_REQUIRE(derived);
|
||||
BOOST_CHECK(!derived->annotation().isFullyImplemented);
|
||||
BOOST_CHECK(!derived->annotation().unimplementedFunctions.empty());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(function_canonical_signature)
|
||||
@ -5714,7 +5676,7 @@ BOOST_AUTO_TEST_CASE(interface_constructor)
|
||||
function I();
|
||||
}
|
||||
)";
|
||||
CHECK_ERROR(text, TypeError, "Constructor cannot be defined in interfaces");
|
||||
CHECK_ERROR_ALLOW_MULTI(text, TypeError, "Constructor cannot be defined in interfaces");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(interface_functions)
|
||||
@ -6134,6 +6096,25 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_variables)
|
||||
CHECK_WARNING(text, "shadows a builtin symbol");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(shadowing_builtins_with_storage_variables)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract C {
|
||||
uint msg;
|
||||
}
|
||||
)";
|
||||
CHECK_WARNING(text, "shadows a builtin symbol");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(shadowing_builtin_at_global_scope)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract msg {
|
||||
}
|
||||
)";
|
||||
CHECK_WARNING(text, "shadows a builtin symbol");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(shadowing_builtins_with_parameters)
|
||||
{
|
||||
char const* text = R"(
|
||||
@ -6190,6 +6171,28 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_ignores_constructor)
|
||||
CHECK_SUCCESS_NO_WARNINGS(text);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(function_overload_is_not_shadowing)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract C {
|
||||
function f() {}
|
||||
function f(uint) {}
|
||||
}
|
||||
)";
|
||||
CHECK_SUCCESS_NO_WARNINGS(text);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(function_override_is_not_shadowing)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract D { function f() {} }
|
||||
contract C is D {
|
||||
function f(uint) {}
|
||||
}
|
||||
)";
|
||||
CHECK_SUCCESS_NO_WARNINGS(text);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(callable_crash)
|
||||
{
|
||||
char const* text = R"(
|
||||
@ -6437,7 +6440,7 @@ BOOST_AUTO_TEST_CASE(using_this_in_constructor)
|
||||
CHECK_WARNING(text, "\"this\" used in constructor");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(do_not_crash_on_not_lalue)
|
||||
BOOST_AUTO_TEST_CASE(do_not_crash_on_not_lvalue)
|
||||
{
|
||||
// This checks for a bug that caused a crash because of continued analysis.
|
||||
char const* text = R"(
|
||||
@ -6451,6 +6454,110 @@ BOOST_AUTO_TEST_CASE(do_not_crash_on_not_lalue)
|
||||
CHECK_ERROR_ALLOW_MULTI(text, TypeError, "is not callable");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(builtin_reject_gas)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract C {
|
||||
function f() {
|
||||
keccak256.gas();
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup");
|
||||
text = R"(
|
||||
contract C {
|
||||
function f() {
|
||||
sha256.gas();
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup");
|
||||
text = R"(
|
||||
contract C {
|
||||
function f() {
|
||||
ripemd160.gas();
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup");
|
||||
text = R"(
|
||||
contract C {
|
||||
function f() {
|
||||
ecrecover.gas();
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(builtin_reject_value)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract C {
|
||||
function f() {
|
||||
keccak256.value();
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup");
|
||||
text = R"(
|
||||
contract C {
|
||||
function f() {
|
||||
sha256.value();
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup");
|
||||
text = R"(
|
||||
contract C {
|
||||
function f() {
|
||||
ripemd160.value();
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup");
|
||||
text = R"(
|
||||
contract C {
|
||||
function f() {
|
||||
ecrecover.value();
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(constructor_without_implementation)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract C {
|
||||
function C();
|
||||
}
|
||||
)";
|
||||
CHECK_ERROR(text, TypeError, "Constructor must be implemented if declared.");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(library_function_without_implementation)
|
||||
{
|
||||
char const* text = R"(
|
||||
library L {
|
||||
function f();
|
||||
}
|
||||
)";
|
||||
CHECK_SUCCESS_NO_WARNINGS(text);
|
||||
text = R"(
|
||||
library L {
|
||||
function f() internal;
|
||||
}
|
||||
)";
|
||||
CHECK_ERROR(text, TypeError, "Internal library function must be implemented if declared.");
|
||||
text = R"(
|
||||
library L {
|
||||
function f() private;
|
||||
}
|
||||
)";
|
||||
CHECK_ERROR(text, TypeError, "Internal library function must be implemented if declared.");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user