Merge pull request #2710 from ethereum/develop

Merge develop into release for 0.4.15
This commit is contained in:
chriseth 2017-08-08 17:15:30 +02:00 committed by GitHub
commit bbb8e64fbe
22 changed files with 421 additions and 116 deletions

View File

@ -8,7 +8,7 @@ include(EthPolicy)
eth_policy() eth_policy()
# project name and version should be set after cmake_policy CMP0048 # 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}) project(solidity VERSION ${PROJECT_VERSION})
# Let's find our dependencies # Let's find our dependencies

View File

@ -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) ### 0.4.14 (2017-07-31)
Features: Features:

View File

@ -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", "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.", "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", "fixed": "0.4.14",
"severity": "medium" "severity": "medium"
}, },
{ {
"name": "SkipEmptyStringLiteral", "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.", "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", "fixed": "0.4.12",
"severity": "low" "severity": "low"

View File

@ -182,6 +182,7 @@
}, },
"0.3.0": { "0.3.0": {
"bugs": [ "bugs": [
"DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction", "ConstantOptimizerSubtraction",
@ -198,6 +199,7 @@
}, },
"0.3.1": { "0.3.1": {
"bugs": [ "bugs": [
"DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction", "ConstantOptimizerSubtraction",
@ -213,6 +215,7 @@
}, },
"0.3.2": { "0.3.2": {
"bugs": [ "bugs": [
"DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction", "ConstantOptimizerSubtraction",
@ -228,6 +231,7 @@
}, },
"0.3.3": { "0.3.3": {
"bugs": [ "bugs": [
"DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction", "ConstantOptimizerSubtraction",
@ -242,6 +246,7 @@
}, },
"0.3.4": { "0.3.4": {
"bugs": [ "bugs": [
"DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction", "ConstantOptimizerSubtraction",
@ -256,6 +261,7 @@
}, },
"0.3.5": { "0.3.5": {
"bugs": [ "bugs": [
"DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction", "ConstantOptimizerSubtraction",
@ -270,6 +276,7 @@
}, },
"0.3.6": { "0.3.6": {
"bugs": [ "bugs": [
"DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction", "ConstantOptimizerSubtraction",
@ -282,6 +289,7 @@
}, },
"0.4.0": { "0.4.0": {
"bugs": [ "bugs": [
"DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction", "ConstantOptimizerSubtraction",
@ -294,6 +302,7 @@
}, },
"0.4.1": { "0.4.1": {
"bugs": [ "bugs": [
"DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction", "ConstantOptimizerSubtraction",
@ -306,6 +315,7 @@
}, },
"0.4.10": { "0.4.10": {
"bugs": [ "bugs": [
"DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction" "ConstantOptimizerSubtraction"
@ -314,6 +324,7 @@
}, },
"0.4.11": { "0.4.11": {
"bugs": [ "bugs": [
"DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral" "SkipEmptyStringLiteral"
], ],
@ -321,22 +332,31 @@
}, },
"0.4.12": { "0.4.12": {
"bugs": [ "bugs": [
"DelegateCallReturnValue",
"ECRecoverMalformedInput" "ECRecoverMalformedInput"
], ],
"released": "2017-07-03" "released": "2017-07-03"
}, },
"0.4.13": { "0.4.13": {
"bugs": [ "bugs": [
"DelegateCallReturnValue",
"ECRecoverMalformedInput" "ECRecoverMalformedInput"
], ],
"released": "2017-07-06" "released": "2017-07-06"
}, },
"0.4.14": { "0.4.14": {
"bugs": [], "bugs": [
"DelegateCallReturnValue"
],
"released": "2017-07-31" "released": "2017-07-31"
}, },
"0.4.15": {
"bugs": [],
"released": "2017-08-08"
},
"0.4.2": { "0.4.2": {
"bugs": [ "bugs": [
"DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction", "ConstantOptimizerSubtraction",
@ -348,6 +368,7 @@
}, },
"0.4.3": { "0.4.3": {
"bugs": [ "bugs": [
"DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction", "ConstantOptimizerSubtraction",
@ -358,6 +379,7 @@
}, },
"0.4.4": { "0.4.4": {
"bugs": [ "bugs": [
"DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction", "ConstantOptimizerSubtraction",
@ -367,6 +389,7 @@
}, },
"0.4.5": { "0.4.5": {
"bugs": [ "bugs": [
"DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction", "ConstantOptimizerSubtraction",
@ -377,6 +400,7 @@
}, },
"0.4.6": { "0.4.6": {
"bugs": [ "bugs": [
"DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction", "ConstantOptimizerSubtraction",
@ -386,6 +410,7 @@
}, },
"0.4.7": { "0.4.7": {
"bugs": [ "bugs": [
"DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction" "ConstantOptimizerSubtraction"
@ -394,6 +419,7 @@
}, },
"0.4.8": { "0.4.8": {
"bugs": [ "bugs": [
"DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction" "ConstantOptimizerSubtraction"
@ -402,6 +428,7 @@
}, },
"0.4.9": { "0.4.9": {
"bugs": [ "bugs": [
"DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction" "ConstantOptimizerSubtraction"

View File

@ -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 and the low-level functions ``call``, ``delegatecall`` and ``callcode`` -- those return ``false`` in case
of an exception instead of "bubbling up". 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. Catching exceptions is not yet possible.
In the following example, you can see how ``require`` can be used to easily check conditions on inputs In the following example, you can see how ``require`` can be used to easily check conditions on inputs

View File

@ -56,7 +56,7 @@ repository contains potentially unstable changes in the develop branch.
docker run ethereum/solc:stable solc --version 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 so you have to do some additional work to link in the source and
output directories. output directories.
@ -83,7 +83,15 @@ If you want to use the cutting edge developer version:
sudo apt-get update sudo apt-get update
sudo apt-get install solc 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 sudo snap install solc --edge

View File

@ -277,9 +277,9 @@ activate themselves.
if (highestBidder != 0) { if (highestBidder != 0) {
// Sending back the money by simply using // Sending back the money by simply using
// highestBidder.send(highestBid) is a security risk // highestBidder.send(highestBid) is a security risk
// because it can be prevented by the caller by e.g. // because it could execute an untrusted contract.
// raising the call stack to 1023. It is always safer // It is always safer to let the recipients
// to let the recipients withdraw their money themselves. // withdraw their money themselves.
pendingReturns[highestBidder] += highestBid; pendingReturns[highestBidder] += highestBid;
} }
highestBidder = msg.sender; highestBidder = msg.sender;

View File

@ -53,6 +53,7 @@ public:
bool registerDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr, bool _invisible = false, bool _update = false); 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; std::vector<Declaration const*> resolveName(ASTString const& _name, bool _recursive = false) const;
ASTNode const* enclosingNode() const { return m_enclosingNode; } 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; } 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. /// @returns whether declaration is valid, and if not also returns previous declaration.
Declaration const* conflictingDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr) const; Declaration const* conflictingDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr) const;

View File

@ -452,13 +452,9 @@ bool DeclarationRegistrationHelper::registerDeclaration(
_errorLocation = &_declaration.location(); _errorLocation = &_declaration.location();
Declaration const* shadowedDeclaration = nullptr; Declaration const* shadowedDeclaration = nullptr;
if (_warnOnShadow && !_declaration.name().empty()) if (_warnOnShadow && !_declaration.name().empty() && _container.enclosingContainer())
for (auto const* decl: _container.resolveName(_declaration.name(), true)) for (auto const* decl: _container.enclosingContainer()->resolveName(_declaration.name(), true))
if (decl != &_declaration) shadowedDeclaration = decl;
{
shadowedDeclaration = decl;
break;
}
if (!_container.registerDeclaration(_declaration, _name, !_declaration.isVisibleInContract())) if (!_container.registerDeclaration(_declaration, _name, !_declaration.isVisibleInContract()))
{ {

View File

@ -169,6 +169,8 @@ private:
void closeCurrentScope(); void closeCurrentScope();
void registerDeclaration(Declaration& _declaration, bool _opensScope); void registerDeclaration(Declaration& _declaration, bool _opensScope);
static bool isOverloadedFunction(Declaration const& _declaration1, Declaration const& _declaration2);
/// @returns the canonical name of the current scope. /// @returns the canonical name of the current scope.
std::string currentCanonicalName() const; std::string currentCanonicalName() const;

View File

@ -112,8 +112,6 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
m_errorReporter.typeError(fallbackFunction->returnParameterList()->location(), "Fallback function cannot return values."); m_errorReporter.typeError(fallbackFunction->returnParameterList()->location(), "Fallback function cannot return values.");
} }
} }
if (!function->isImplemented())
_contract.annotation().isFullyImplemented = false;
} }
for (auto const& n: _contract.subNodes()) for (auto const& n: _contract.subNodes())
@ -188,20 +186,13 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont
using FunTypeAndFlag = std::pair<FunctionTypePointer, bool>; using FunTypeAndFlag = std::pair<FunctionTypePointer, bool>;
map<string, vector<FunTypeAndFlag>> functions; map<string, vector<FunTypeAndFlag>> functions;
bool allBaseConstructorsImplemented = true;
// Search from base to derived // Search from base to derived
for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts)) for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts))
for (FunctionDefinition const* function: contract->definedFunctions()) for (FunctionDefinition const* function: contract->definedFunctions())
{ {
// Take constructors out of overload hierarchy // Take constructors out of overload hierarchy
if (function->isConstructor()) if (function->isConstructor())
{
if (!function->isImplemented())
// Base contract's constructor is not fully implemented, no way to get
// out of this.
allBaseConstructorsImplemented = false;
continue; continue;
}
auto& overloads = functions[function->name()]; auto& overloads = functions[function->name()];
FunctionTypePointer funType = make_shared<FunctionType>(*function); FunctionTypePointer funType = make_shared<FunctionType>(*function);
auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag) auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag)
@ -219,16 +210,15 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont
it->second = true; it->second = true;
} }
if (!allBaseConstructorsImplemented)
_contract.annotation().isFullyImplemented = false;
// Set to not fully implemented if at least one flag is false. // Set to not fully implemented if at least one flag is false.
for (auto const& it: functions) for (auto const& it: functions)
for (auto const& funAndFlag: it.second) for (auto const& funAndFlag: it.second)
if (!funAndFlag.second) if (!funAndFlag.second)
{ {
_contract.annotation().isFullyImplemented = false; FunctionDefinition const* function = dynamic_cast<FunctionDefinition const*>(&funAndFlag.first->declaration());
return; solAssert(function, "");
_contract.annotation().unimplementedFunctions.push_back(function);
break;
} }
} }
@ -266,7 +256,8 @@ void TypeChecker::checkContractAbstractConstructors(ContractDefinition const& _c
} }
} }
if (!argumentsNeeded.empty()) 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) void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contract)
@ -525,6 +516,10 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
} }
if (_function.isImplemented()) if (_function.isImplemented())
_function.body().accept(*this); _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; return false;
} }
@ -1050,7 +1045,7 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement)
{ {
auto kind = callType->kind(); auto kind = callType->kind();
if ( if (
kind == FunctionType::Kind::Bare || kind == FunctionType::Kind::BareCall ||
kind == FunctionType::Kind::BareCallCode || kind == FunctionType::Kind::BareCallCode ||
kind == FunctionType::Kind::BareDelegateCall kind == FunctionType::Kind::BareDelegateCall
) )
@ -1523,8 +1518,15 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
if (!contract) if (!contract)
m_errorReporter.fatalTypeError(_newExpression.location(), "Identifier is not a contract."); m_errorReporter.fatalTypeError(_newExpression.location(), "Identifier is not a contract.");
if (!contract->annotation().isFullyImplemented) if (!contract->annotation().unimplementedFunctions.empty())
m_errorReporter.typeError(_newExpression.location(), "Trying to create an instance of an abstract contract."); 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()) if (!contract->constructorIsPublic())
m_errorReporter.typeError(_newExpression.location(), "Contract with internal constructor cannot be created directly."); m_errorReporter.typeError(_newExpression.location(), "Contract with internal constructor cannot be created directly.");

View File

@ -79,8 +79,8 @@ struct TypeDeclarationAnnotation: ASTAnnotation
struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, DocumentedAnnotation struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, DocumentedAnnotation
{ {
/// Whether all functions are implemented. /// List of functions without a body. Can also contain functions from base classes.
bool isFullyImplemented = true; std::vector<FunctionDefinition const*> unimplementedFunctions;
/// List of all (direct and indirect) base contracts in order from derived to /// List of all (direct and indirect) base contracts in order from derived to
/// base, including the contract itself. /// base, including the contract itself.
std::vector<ContractDefinition const*> linearizedBaseContracts; std::vector<ContractDefinition const*> linearizedBaseContracts;

View File

@ -253,7 +253,7 @@ bool ASTJsonConverter::visit(ContractDefinition const& _node)
make_pair("name", _node.name()), make_pair("name", _node.name()),
make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue), make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue),
make_pair("contractKind", contractKind(_node.contractKind())), 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("linearizedBaseContracts", getContainerIds(_node.annotation().linearizedBaseContracts)),
make_pair("baseContracts", toJson(_node.baseContracts())), make_pair("baseContracts", toJson(_node.baseContracts())),
make_pair("contractDependencies", getContainerIds(_node.annotation().contractDependencies)), make_pair("contractDependencies", getContainerIds(_node.annotation().contractDependencies)),

View File

@ -477,7 +477,7 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons
if (isAddress()) if (isAddress())
return { return {
{"balance", make_shared<IntegerType >(256)}, {"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)}, {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareCallCode, true, false, true)},
{"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareDelegateCall, true)}, {"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareDelegateCall, true)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)}, {"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::External: id += "external"; break;
case Kind::CallCode: id += "callcode"; break; case Kind::CallCode: id += "callcode"; break;
case Kind::DelegateCall: id += "delegatecall"; 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::BareCallCode: id += "barecallcode"; break;
case Kind::BareDelegateCall: id += "baredelegatecall"; break; case Kind::BareDelegateCall: id += "baredelegatecall"; break;
case Kind::Creation: id += "creation"; break; case Kind::Creation: id += "creation"; break;
@ -2346,7 +2346,7 @@ unsigned FunctionType::sizeOnStack() const
unsigned size = 0; unsigned size = 0;
if (kind == Kind::External || kind == Kind::CallCode || kind == Kind::DelegateCall) if (kind == Kind::External || kind == Kind::CallCode || kind == Kind::DelegateCall)
size = 2; 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; size = 1;
else if (kind == Kind::Internal) else if (kind == Kind::Internal)
size = 1; size = 1;
@ -2402,10 +2402,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
{ {
case Kind::External: case Kind::External:
case Kind::Creation: case Kind::Creation:
case Kind::ECRecover: case Kind::BareCall:
case Kind::SHA256:
case Kind::RIPEMD160:
case Kind::Bare:
case Kind::BareCallCode: case Kind::BareCallCode:
case Kind::BareDelegateCall: case Kind::BareDelegateCall:
{ {
@ -2509,7 +2506,7 @@ bool FunctionType::isBareCall() const
{ {
switch (m_kind) switch (m_kind)
{ {
case Kind::Bare: case Kind::BareCall:
case Kind::BareCallCode: case Kind::BareCallCode:
case Kind::BareDelegateCall: case Kind::BareDelegateCall:
case Kind::ECRecover: case Kind::ECRecover:

View File

@ -838,7 +838,7 @@ public:
External, ///< external call using CALL External, ///< external call using CALL
CallCode, ///< external call using CALLCODE, i.e. not exchanging the storage CallCode, ///< external call using CALLCODE, i.e. not exchanging the storage
DelegateCall, ///< external call using DELEGATECALL, 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 BareCallCode, ///< CALLCODE without function hash
BareDelegateCall, ///< DELEGATECALL without function hash BareDelegateCall, ///< DELEGATECALL without function hash
Creation, ///< external call using CREATE Creation, ///< external call using CREATE

View File

@ -546,7 +546,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
case FunctionType::Kind::External: case FunctionType::Kind::External:
case FunctionType::Kind::CallCode: case FunctionType::Kind::CallCode:
case FunctionType::Kind::DelegateCall: case FunctionType::Kind::DelegateCall:
case FunctionType::Kind::Bare: case FunctionType::Kind::BareCall:
case FunctionType::Kind::BareCallCode: case FunctionType::Kind::BareCallCode:
case FunctionType::Kind::BareDelegateCall: case FunctionType::Kind::BareDelegateCall:
_functionCall.expression().accept(*this); _functionCall.expression().accept(*this);
@ -642,7 +642,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
TypePointers{}, TypePointers{},
strings(), strings(),
strings(), strings(),
FunctionType::Kind::Bare, FunctionType::Kind::BareCall,
false, false,
nullptr, nullptr,
false, false,
@ -973,7 +973,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
case FunctionType::Kind::DelegateCall: case FunctionType::Kind::DelegateCall:
case FunctionType::Kind::CallCode: case FunctionType::Kind::CallCode:
case FunctionType::Kind::Send: case FunctionType::Kind::Send:
case FunctionType::Kind::Bare: case FunctionType::Kind::BareCall:
case FunctionType::Kind::BareCallCode: case FunctionType::Kind::BareCallCode:
case FunctionType::Kind::BareDelegateCall: case FunctionType::Kind::BareDelegateCall:
case FunctionType::Kind::Transfer: case FunctionType::Kind::Transfer:
@ -1560,7 +1560,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack()); utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack());
auto funKind = _functionType.kind(); 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 isCallCode = funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::CallCode;
bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall; bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
@ -1579,7 +1579,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
TypePointers parameterTypes = _functionType.parameterTypes(); TypePointers parameterTypes = _functionType.parameterTypes();
bool manualFunctionId = false; bool manualFunctionId = false;
if ( 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() !_arguments.empty()
) )
{ {
@ -1712,7 +1712,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
u256 gasNeededByCaller = eth::GasCosts::callGas + 10; u256 gasNeededByCaller = eth::GasCosts::callGas + 10;
if (_functionType.valueSet()) if (_functionType.valueSet())
gasNeededByCaller += eth::GasCosts::callValueTransferGas; gasNeededByCaller += eth::GasCosts::callValueTransferGas;
if (!isCallCode && !isDelegateCall && !existenceChecked) if (!existenceChecked)
gasNeededByCaller += eth::GasCosts::callNewAccountGas; // we never know gasNeededByCaller += eth::GasCosts::callNewAccountGas; // we never know
m_context << gasNeededByCaller << Instruction::GAS << Instruction::SUB; m_context << gasNeededByCaller << Instruction::GAS << Instruction::SUB;
} }

View File

@ -639,7 +639,7 @@ void CompilerStack::compileContract(
{ {
if ( if (
_compiledContracts.count(&_contract) || _compiledContracts.count(&_contract) ||
!_contract.annotation().isFullyImplemented || !_contract.annotation().unimplementedFunctions.empty() ||
!_contract.constructorIsPublic() !_contract.constructorIsPublic()
) )
return; return;

View File

@ -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) void ErrorReporter::typeError(SourceLocation const& _location, string const& _description)
{ {
error( error(

View File

@ -73,6 +73,12 @@ public:
void syntaxError(SourceLocation const& _location, std::string const& _description); 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 typeError(SourceLocation const& _location, std::string const& _description);
void fatalTypeError(SourceLocation const& _location, std::string const& _description); void fatalTypeError(SourceLocation const& _location, std::string const& _description);

View File

@ -101,6 +101,8 @@ void SourceReferenceFormatter::printExceptionInformation(
_stream << _name; _stream << _name;
if (string const* description = boost::get_error_info<errinfo_comment>(_exception)) if (string const* description = boost::get_error_info<errinfo_comment>(_exception))
_stream << ": " << *description << endl; _stream << ": " << *description << endl;
else
_stream << endl;
printSourceLocation(_stream, location, _scannerFromSourceName); printSourceLocation(_stream, location, _scannerFromSourceName);
@ -108,9 +110,8 @@ void SourceReferenceFormatter::printExceptionInformation(
{ {
for (auto info: secondarylocation->infos) for (auto info: secondarylocation->infos)
{ {
_stream << info.first << " ";
printSourceName(_stream, &info.second, _scannerFromSourceName); printSourceName(_stream, &info.second, _scannerFromSourceName);
_stream << endl; _stream << info.first << endl;
printSourceLocation(_stream, &info.second, _scannerFromSourceName); printSourceLocation(_stream, &info.second, _scannerFromSourceName);
} }
_stream << endl; _stream << endl;

View File

@ -1950,6 +1950,87 @@ BOOST_AUTO_TEST_CASE(ripemd)
testContractAgainstCpp("a(bytes32)", f, u256(-1)); 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) BOOST_AUTO_TEST_CASE(ecrecover)
{ {
char const* sourceCode = R"( 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_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) BOOST_AUTO_TEST_CASE(value_complex)
{ {
char const* sourceCode = R"( 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_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() BOOST_AUTO_TEST_SUITE_END()
} }

View File

@ -624,7 +624,7 @@ BOOST_AUTO_TEST_CASE(function_no_implementation)
std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes(); std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes();
ContractDefinition* contract = dynamic_cast<ContractDefinition*>(nodes[1].get()); ContractDefinition* contract = dynamic_cast<ContractDefinition*>(nodes[1].get());
BOOST_REQUIRE(contract); BOOST_REQUIRE(contract);
BOOST_CHECK(!contract->annotation().isFullyImplemented); BOOST_CHECK(!contract->annotation().unimplementedFunctions.empty());
BOOST_CHECK(!contract->definedFunctions()[0]->isImplemented()); 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* base = dynamic_cast<ContractDefinition*>(nodes[1].get());
ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get()); ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get());
BOOST_REQUIRE(base); BOOST_REQUIRE(base);
BOOST_CHECK(!base->annotation().isFullyImplemented); BOOST_CHECK(!base->annotation().unimplementedFunctions.empty());
BOOST_CHECK(!base->definedFunctions()[0]->isImplemented()); BOOST_CHECK(!base->definedFunctions()[0]->isImplemented());
BOOST_REQUIRE(derived); BOOST_REQUIRE(derived);
BOOST_CHECK(derived->annotation().isFullyImplemented); BOOST_CHECK(derived->annotation().unimplementedFunctions.empty());
BOOST_CHECK(derived->definedFunctions()[0]->isImplemented()); 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* base = dynamic_cast<ContractDefinition*>(nodes[1].get());
ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get()); ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get());
BOOST_REQUIRE(base); BOOST_REQUIRE(base);
BOOST_CHECK(!base->annotation().isFullyImplemented); BOOST_CHECK(!base->annotation().unimplementedFunctions.empty());
BOOST_REQUIRE(derived); BOOST_REQUIRE(derived);
BOOST_CHECK(!derived->annotation().isFullyImplemented); BOOST_CHECK(!derived->annotation().unimplementedFunctions.empty());
} }
BOOST_AUTO_TEST_CASE(create_abstract_contract) BOOST_AUTO_TEST_CASE(create_abstract_contract)
@ -677,44 +677,6 @@ BOOST_AUTO_TEST_CASE(create_abstract_contract)
CHECK_ERROR(text, TypeError, ""); 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) BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract)
{ {
ASTPointer<SourceUnit> sourceUnit; ASTPointer<SourceUnit> sourceUnit;
@ -738,7 +700,7 @@ BOOST_AUTO_TEST_CASE(implement_abstract_via_constructor)
BOOST_CHECK_EQUAL(nodes.size(), 3); BOOST_CHECK_EQUAL(nodes.size(), 3);
ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get()); ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get());
BOOST_REQUIRE(derived); BOOST_REQUIRE(derived);
BOOST_CHECK(!derived->annotation().isFullyImplemented); BOOST_CHECK(!derived->annotation().unimplementedFunctions.empty());
} }
BOOST_AUTO_TEST_CASE(function_canonical_signature) BOOST_AUTO_TEST_CASE(function_canonical_signature)
@ -5714,7 +5676,7 @@ BOOST_AUTO_TEST_CASE(interface_constructor)
function I(); 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) 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"); 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) BOOST_AUTO_TEST_CASE(shadowing_builtins_with_parameters)
{ {
char const* text = R"( char const* text = R"(
@ -6190,6 +6171,28 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_ignores_constructor)
CHECK_SUCCESS_NO_WARNINGS(text); 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) BOOST_AUTO_TEST_CASE(callable_crash)
{ {
char const* text = R"( char const* text = R"(
@ -6437,7 +6440,7 @@ BOOST_AUTO_TEST_CASE(using_this_in_constructor)
CHECK_WARNING(text, "\"this\" used 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. // This checks for a bug that caused a crash because of continued analysis.
char const* text = R"( 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"); 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() BOOST_AUTO_TEST_SUITE_END()
} }