mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #1701 from ethereum/fixFatalErrors
Fix early exits for fatal errors.
This commit is contained in:
commit
0ad8e53404
@ -9,6 +9,7 @@ Features:
|
|||||||
Bugfixes:
|
Bugfixes:
|
||||||
* Commandline interface: Always escape filenames (replace ``/``, ``:`` and ``.`` with ``_``).
|
* Commandline interface: Always escape filenames (replace ``/``, ``:`` and ``.`` with ``_``).
|
||||||
* Commandline interface: Do not try creating paths ``.`` and ``..``.
|
* Commandline interface: Do not try creating paths ``.`` and ``..``.
|
||||||
|
* Type system: Fix a crash caused by continuing on fatal errors in the code.
|
||||||
* Type system: Disallow arrays with negative length.
|
* Type system: Disallow arrays with negative length.
|
||||||
|
|
||||||
### 0.4.9 (2017-01-31)
|
### 0.4.9 (2017-01-31)
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libsolidity/analysis/NameAndTypeResolver.h>
|
#include <libsolidity/analysis/NameAndTypeResolver.h>
|
||||||
|
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <libsolidity/analysis/TypeChecker.h>
|
#include <libsolidity/analysis/TypeChecker.h>
|
||||||
#include <libsolidity/interface/Exceptions.h>
|
#include <libsolidity/interface/Exceptions.h>
|
||||||
@ -130,62 +131,9 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
|
|||||||
|
|
||||||
bool NameAndTypeResolver::resolveNamesAndTypes(ASTNode& _node, bool _resolveInsideCode)
|
bool NameAndTypeResolver::resolveNamesAndTypes(ASTNode& _node, bool _resolveInsideCode)
|
||||||
{
|
{
|
||||||
bool success = true;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(&_node))
|
return resolveNamesAndTypesInternal(_node, _resolveInsideCode);
|
||||||
{
|
|
||||||
m_currentScope = m_scopes[contract->scope()].get();
|
|
||||||
solAssert(!!m_currentScope, "");
|
|
||||||
|
|
||||||
for (ASTPointer<InheritanceSpecifier> const& baseContract: contract->baseContracts())
|
|
||||||
if (!resolveNamesAndTypes(*baseContract, true))
|
|
||||||
success = false;
|
|
||||||
|
|
||||||
m_currentScope = m_scopes[contract].get();
|
|
||||||
|
|
||||||
if (success)
|
|
||||||
{
|
|
||||||
linearizeBaseContracts(*contract);
|
|
||||||
vector<ContractDefinition const*> properBases(
|
|
||||||
++contract->annotation().linearizedBaseContracts.begin(),
|
|
||||||
contract->annotation().linearizedBaseContracts.end()
|
|
||||||
);
|
|
||||||
|
|
||||||
for (ContractDefinition const* base: properBases)
|
|
||||||
importInheritedScope(*base);
|
|
||||||
}
|
|
||||||
|
|
||||||
// these can contain code, only resolve parameters for now
|
|
||||||
for (ASTPointer<ASTNode> const& node: contract->subNodes())
|
|
||||||
{
|
|
||||||
m_currentScope = m_scopes[contract].get();
|
|
||||||
if (!resolveNamesAndTypes(*node, false))
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!success)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!_resolveInsideCode)
|
|
||||||
return success;
|
|
||||||
|
|
||||||
m_currentScope = m_scopes[contract].get();
|
|
||||||
|
|
||||||
// now resolve references inside the code
|
|
||||||
for (ASTPointer<ASTNode> const& node: contract->subNodes())
|
|
||||||
{
|
|
||||||
m_currentScope = m_scopes[contract].get();
|
|
||||||
if (!resolveNamesAndTypes(*node, true))
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (m_scopes.count(&_node))
|
|
||||||
m_currentScope = m_scopes[&_node].get();
|
|
||||||
return ReferencesResolver(m_errors, *this, _resolveInsideCode).resolve(_node);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (FatalError const&)
|
catch (FatalError const&)
|
||||||
{
|
{
|
||||||
@ -193,7 +141,6 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ASTNode& _node, bool _resolveInsi
|
|||||||
throw; // Something is weird here, rather throw again.
|
throw; // Something is weird here, rather throw again.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
|
bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
|
||||||
@ -249,21 +196,25 @@ vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
|
|||||||
solAssert(_declarations.size() > 1, "");
|
solAssert(_declarations.size() > 1, "");
|
||||||
vector<Declaration const*> uniqueFunctions;
|
vector<Declaration const*> uniqueFunctions;
|
||||||
|
|
||||||
for (auto it = _declarations.begin(); it != _declarations.end(); ++it)
|
for (Declaration const* declaration: _declarations)
|
||||||
{
|
{
|
||||||
solAssert(*it, "");
|
solAssert(declaration, "");
|
||||||
// the declaration is functionDefinition, eventDefinition or a VariableDeclaration while declarations > 1
|
// the declaration is functionDefinition, eventDefinition or a VariableDeclaration while declarations > 1
|
||||||
solAssert(dynamic_cast<FunctionDefinition const*>(*it) || dynamic_cast<EventDefinition const*>(*it) || dynamic_cast<VariableDeclaration const*>(*it),
|
solAssert(
|
||||||
"Found overloading involving something not a function or a variable");
|
dynamic_cast<FunctionDefinition const*>(declaration) ||
|
||||||
|
dynamic_cast<EventDefinition const*>(declaration) ||
|
||||||
|
dynamic_cast<VariableDeclaration const*>(declaration),
|
||||||
|
"Found overloading involving something not a function or a variable."
|
||||||
|
);
|
||||||
|
|
||||||
shared_ptr<FunctionType const> functionType { (*it)->functionType(false) };
|
FunctionTypePointer functionType { declaration->functionType(false) };
|
||||||
if (!functionType)
|
if (!functionType)
|
||||||
functionType = (*it)->functionType(true);
|
functionType = declaration->functionType(true);
|
||||||
solAssert(functionType, "failed to determine the function type of the overloaded");
|
solAssert(functionType, "Failed to determine the function type of the overloaded.");
|
||||||
|
|
||||||
for (auto parameter: functionType->parameterTypes() + functionType->returnParameterTypes())
|
for (auto parameter: functionType->parameterTypes() + functionType->returnParameterTypes())
|
||||||
if (!parameter)
|
if (!parameter)
|
||||||
reportFatalDeclarationError(_identifier.location(), "Function type can not be used in this context");
|
reportFatalDeclarationError(_identifier.location(), "Function type can not be used in this context.");
|
||||||
|
|
||||||
if (uniqueFunctions.end() == find_if(
|
if (uniqueFunctions.end() == find_if(
|
||||||
uniqueFunctions.begin(),
|
uniqueFunctions.begin(),
|
||||||
@ -276,11 +227,73 @@ vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
|
|||||||
return newFunctionType && functionType->hasEqualArgumentTypes(*newFunctionType);
|
return newFunctionType && functionType->hasEqualArgumentTypes(*newFunctionType);
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
uniqueFunctions.push_back(*it);
|
uniqueFunctions.push_back(declaration);
|
||||||
}
|
}
|
||||||
return uniqueFunctions;
|
return uniqueFunctions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode)
|
||||||
|
{
|
||||||
|
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(&_node))
|
||||||
|
{
|
||||||
|
bool success = true;
|
||||||
|
m_currentScope = m_scopes[contract->scope()].get();
|
||||||
|
solAssert(!!m_currentScope, "");
|
||||||
|
|
||||||
|
for (ASTPointer<InheritanceSpecifier> const& baseContract: contract->baseContracts())
|
||||||
|
if (!resolveNamesAndTypes(*baseContract, true))
|
||||||
|
success = false;
|
||||||
|
|
||||||
|
m_currentScope = m_scopes[contract].get();
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
linearizeBaseContracts(*contract);
|
||||||
|
vector<ContractDefinition const*> properBases(
|
||||||
|
++contract->annotation().linearizedBaseContracts.begin(),
|
||||||
|
contract->annotation().linearizedBaseContracts.end()
|
||||||
|
);
|
||||||
|
|
||||||
|
for (ContractDefinition const* base: properBases)
|
||||||
|
importInheritedScope(*base);
|
||||||
|
}
|
||||||
|
|
||||||
|
// these can contain code, only resolve parameters for now
|
||||||
|
for (ASTPointer<ASTNode> const& node: contract->subNodes())
|
||||||
|
{
|
||||||
|
m_currentScope = m_scopes[contract].get();
|
||||||
|
if (!resolveNamesAndTypes(*node, false))
|
||||||
|
{
|
||||||
|
success = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!_resolveInsideCode)
|
||||||
|
return success;
|
||||||
|
|
||||||
|
m_currentScope = m_scopes[contract].get();
|
||||||
|
|
||||||
|
// now resolve references inside the code
|
||||||
|
for (ASTPointer<ASTNode> const& node: contract->subNodes())
|
||||||
|
{
|
||||||
|
m_currentScope = m_scopes[contract].get();
|
||||||
|
if (!resolveNamesAndTypes(*node, true))
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (m_scopes.count(&_node))
|
||||||
|
m_currentScope = m_scopes[&_node].get();
|
||||||
|
return ReferencesResolver(m_errors, *this, _resolveInsideCode).resolve(_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base)
|
void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base)
|
||||||
{
|
{
|
||||||
auto iterator = m_scopes.find(&_base);
|
auto iterator = m_scopes.find(&_base);
|
||||||
|
@ -89,6 +89,9 @@ public:
|
|||||||
);
|
);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors.
|
||||||
|
bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true);
|
||||||
|
|
||||||
/// Imports all members declared directly in the given contract (i.e. does not import inherited members)
|
/// Imports all members declared directly in the given contract (i.e. does not import inherited members)
|
||||||
/// into the current scope if they are not present already.
|
/// into the current scope if they are not present already.
|
||||||
void importInheritedScope(ContractDefinition const& _base);
|
void importInheritedScope(ContractDefinition const& _base);
|
||||||
|
@ -35,14 +35,7 @@ using namespace dev::solidity;
|
|||||||
|
|
||||||
bool ReferencesResolver::resolve(ASTNode const& _root)
|
bool ReferencesResolver::resolve(ASTNode const& _root)
|
||||||
{
|
{
|
||||||
try
|
_root.accept(*this);
|
||||||
{
|
|
||||||
_root.accept(*this);
|
|
||||||
}
|
|
||||||
catch (FatalError const&)
|
|
||||||
{
|
|
||||||
solAssert(m_errorOccurred, "");
|
|
||||||
}
|
|
||||||
return !m_errorOccurred;
|
return !m_errorOccurred;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ public:
|
|||||||
m_resolveInsideCode(_resolveInsideCode)
|
m_resolveInsideCode(_resolveInsideCode)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/// @returns true if no errors during resolving
|
/// @returns true if no errors during resolving and throws exceptions on fatal errors.
|
||||||
bool resolve(ASTNode const& _root);
|
bool resolve(ASTNode const& _root);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -1652,6 +1652,7 @@ MemberList::MemberMap StructType::nativeMembers(ContractDefinition const*) const
|
|||||||
for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
|
for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
|
||||||
{
|
{
|
||||||
TypePointer type = variable->annotation().type;
|
TypePointer type = variable->annotation().type;
|
||||||
|
solAssert(type, "");
|
||||||
// Skip all mapping members if we are not in storage.
|
// Skip all mapping members if we are not in storage.
|
||||||
if (location() != DataLocation::Storage && !type->canLiveOutsideStorage())
|
if (location() != DataLocation::Storage && !type->canLiveOutsideStorage())
|
||||||
continue;
|
continue;
|
||||||
@ -1964,6 +1965,8 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
|
|||||||
if (auto structType = dynamic_cast<StructType const*>(returnType.get()))
|
if (auto structType = dynamic_cast<StructType const*>(returnType.get()))
|
||||||
{
|
{
|
||||||
for (auto const& member: structType->members(nullptr))
|
for (auto const& member: structType->members(nullptr))
|
||||||
|
{
|
||||||
|
solAssert(member.type, "");
|
||||||
if (member.type->category() != Category::Mapping)
|
if (member.type->category() != Category::Mapping)
|
||||||
{
|
{
|
||||||
if (auto arrayType = dynamic_cast<ArrayType const*>(member.type.get()))
|
if (auto arrayType = dynamic_cast<ArrayType const*>(member.type.get()))
|
||||||
@ -1972,6 +1975,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
|
|||||||
retParams.push_back(member.type);
|
retParams.push_back(member.type);
|
||||||
retParamNames.push_back(member.name);
|
retParamNames.push_back(member.name);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -5079,6 +5079,22 @@ BOOST_AUTO_TEST_CASE(invalid_address_length)
|
|||||||
CHECK_WARNING(text, "checksum");
|
CHECK_WARNING(text, "checksum");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(early_exit_on_fatal_errors)
|
||||||
|
{
|
||||||
|
// This tests a crash that occured because we did not stop for fatal errors.
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
ftring a;
|
||||||
|
}
|
||||||
|
S public s;
|
||||||
|
function s() s {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
CHECK_ERROR(text, DeclarationError, "Identifier not found or not unique");
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user