Merge pull request #1701 from ethereum/fixFatalErrors

Fix early exits for fatal errors.
This commit is contained in:
chriseth 2017-02-16 17:50:28 +01:00 committed by GitHub
commit 0ad8e53404
7 changed files with 103 additions and 73 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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;
} }

View File

@ -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:

View File

@ -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
{ {

View File

@ -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()
} }