mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
errors instead of exceptions
Conflicts: libsolidity/CompilerStack.cpp libsolidity/NameAndTypeResolver.cpp libsolidity/NameAndTypeResolver.h libsolidity/TypeChecker.cpp test/libsolidity/SolidityNameAndTypeResolution.cpp
This commit is contained in:
@@ -137,7 +137,7 @@ ModifierDefinition const& CompilerContext::functionModifier(string const& _name)
|
||||
if (modifier->name() == _name)
|
||||
return *modifier.get();
|
||||
BOOST_THROW_EXCEPTION(InternalCompilerError()
|
||||
<< errinfo_comment("Function modifier " + _name + " not found."));
|
||||
<< errinfo_comment("Function modifier " + _name + " not found."));
|
||||
}
|
||||
|
||||
unsigned CompilerContext::baseStackOffsetOfVariable(Declaration const& _declaration) const
|
||||
|
||||
@@ -97,30 +97,41 @@ void CompilerStack::setSource(string const& _sourceCode)
|
||||
|
||||
bool CompilerStack::parse()
|
||||
{
|
||||
// todo not sure about clear. can contain warnings
|
||||
m_errors.clear();
|
||||
|
||||
for (auto& sourcePair: m_sources)
|
||||
{
|
||||
sourcePair.second.scanner->reset();
|
||||
sourcePair.second.ast = Parser().parse(sourcePair.second.scanner);
|
||||
sourcePair.second.ast = Parser(m_errors).parse(sourcePair.second.scanner); // todo check for errors
|
||||
}
|
||||
if (!Error::containsOnlyWarnings(m_errors))
|
||||
// errors while parsing. sould stop before type checking
|
||||
return false;
|
||||
|
||||
resolveImports();
|
||||
|
||||
m_globalContext = make_shared<GlobalContext>();
|
||||
bool success = true;
|
||||
NameAndTypeResolver resolver(m_globalContext->declarations(), m_errors);
|
||||
for (Source const* source: m_sourceOrder)
|
||||
resolver.registerDeclarations(*source->ast);
|
||||
success = success && resolver.registerDeclarations(*source->ast);
|
||||
for (Source const* source: m_sourceOrder)
|
||||
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
|
||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||
{
|
||||
m_globalContext->setCurrentContract(*contract);
|
||||
resolver.updateDeclaration(*m_globalContext->currentThis());
|
||||
resolver.updateDeclaration(*m_globalContext->currentSuper());
|
||||
resolver.resolveNamesAndTypes(*contract);
|
||||
success = success && resolver.updateDeclaration(*m_globalContext->currentThis());
|
||||
success = success && resolver.updateDeclaration(*m_globalContext->currentSuper());
|
||||
success = success && resolver.resolveNamesAndTypes(*contract);
|
||||
m_contracts[contract->name()].contract = contract;
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
m_parseSuccessful = false;
|
||||
return m_parseSuccessful;
|
||||
}
|
||||
InterfaceHandler interfaceHandler;
|
||||
bool typesFine = true;
|
||||
for (Source const* source: m_sourceOrder)
|
||||
@@ -137,6 +148,7 @@ bool CompilerStack::parse()
|
||||
}
|
||||
else
|
||||
typesFine = false;
|
||||
|
||||
m_contracts[contract->name()].contract = contract;
|
||||
m_errors += typeChecker.errors();
|
||||
}
|
||||
@@ -253,9 +265,8 @@ string const& CompilerStack::metadata(string const& _contractName, Documentation
|
||||
if (!m_parseSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
||||
|
||||
Contract const& currentContract = contract(_contractName);
|
||||
|
||||
std::unique_ptr<string const>* doc;
|
||||
Contract const& currentContract = contract(_contractName);
|
||||
|
||||
// checks wheather we already have the documentation
|
||||
switch (_type)
|
||||
@@ -346,9 +357,10 @@ void CompilerStack::resolveImports()
|
||||
if (!m_sources.count(id))
|
||||
BOOST_THROW_EXCEPTION(
|
||||
Error(Error::Type::ParserError)
|
||||
<< errinfo_sourceLocation(import->location())
|
||||
<< errinfo_comment("Source not found.")
|
||||
<< errinfo_sourceLocation(import->location())
|
||||
<< errinfo_comment("Source not found.")
|
||||
);
|
||||
|
||||
toposort(&m_sources[id]);
|
||||
}
|
||||
sourceOrder.push_back(_source);
|
||||
@@ -414,10 +426,12 @@ CompilerStack::Source const& CompilerStack::source(string const& _sourceName) co
|
||||
auto it = m_sources.find(_sourceName);
|
||||
if (it == m_sources.end())
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Given source file not found."));
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
CompilerStack::Contract::Contract(): interfaceHandler(make_shared<InterfaceHandler>()) {}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,6 +167,7 @@ public:
|
||||
/// @returns the list of errors that occured during parsing and type checking.
|
||||
ErrorList const& errors() const { return m_errors; }
|
||||
|
||||
|
||||
private:
|
||||
/**
|
||||
* Information pertaining to one source unit, filled gradually during parsing and compilation.
|
||||
|
||||
@@ -26,11 +26,18 @@
|
||||
#include <utility>
|
||||
#include <libdevcore/Exceptions.h>
|
||||
#include <libevmasm/SourceLocation.h>
|
||||
#include <libsolidity/Utils.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
class Error;
|
||||
using ErrorList = std::vector<std::shared_ptr<Error const>>;
|
||||
|
||||
struct CompilerError: virtual Exception {};
|
||||
struct InternalCompilerError: virtual Exception {};
|
||||
struct fatalError: virtual Exception {}; //todo rename to FatalError
|
||||
|
||||
class Error: virtual public Exception
|
||||
{
|
||||
@@ -41,7 +48,6 @@ public:
|
||||
DocstringParsingError,
|
||||
ParserError,
|
||||
TypeError,
|
||||
|
||||
Warning
|
||||
};
|
||||
|
||||
@@ -65,22 +71,39 @@ public:
|
||||
m_typeName = "Warning";
|
||||
break;
|
||||
default:
|
||||
m_typeName = "Error";
|
||||
solAssert(false, "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Type const type() { return m_type; } const
|
||||
Type type() const { return m_type; }
|
||||
std::string const& typeName() const { return m_typeName; }
|
||||
|
||||
|
||||
/// helper functions
|
||||
static Error const* containsErrorOfType(ErrorList const& _list, Error::Type _type)
|
||||
{
|
||||
for (auto e: _list)
|
||||
{
|
||||
if(e->type() == _type)
|
||||
return e.get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static bool containsOnlyWarnings(ErrorList const& _list)
|
||||
{
|
||||
for (auto e: _list)
|
||||
{
|
||||
if(e->type() != Type::Warning)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
Type m_type;
|
||||
std::string m_typeName;
|
||||
};
|
||||
|
||||
struct CompilerError: virtual Exception {};
|
||||
struct InternalCompilerError: virtual Exception {};
|
||||
struct FatalError: virtual Exception {};
|
||||
|
||||
using errorSourceLocationInfo = std::pair<std::string, SourceLocation>;
|
||||
|
||||
@@ -96,7 +119,6 @@ public:
|
||||
};
|
||||
|
||||
|
||||
using ErrorList = std::vector<std::shared_ptr<Error const>>;
|
||||
using errinfo_sourceLocation = boost::error_info<struct tag_sourceLocation, SourceLocation>;
|
||||
using errinfo_secondarySourceLocation = boost::error_info<struct tag_secondarySourceLocation, SecondarySourceLocation>;
|
||||
|
||||
|
||||
@@ -42,81 +42,105 @@ NameAndTypeResolver::NameAndTypeResolver(
|
||||
m_scopes[nullptr].registerDeclaration(*declaration);
|
||||
}
|
||||
|
||||
void NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit)
|
||||
bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit)
|
||||
{
|
||||
// The helper registers all declarations in m_scopes as a side-effect of its construction.
|
||||
DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit);
|
||||
try
|
||||
{
|
||||
DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errors);
|
||||
}
|
||||
catch (fatalError)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
|
||||
bool NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
|
||||
{
|
||||
m_currentScope = &m_scopes[nullptr];
|
||||
|
||||
for (ASTPointer<InheritanceSpecifier> const& baseContract: _contract.baseContracts())
|
||||
ReferencesResolver resolver(*baseContract, *this, &_contract, nullptr);
|
||||
|
||||
m_currentScope = &m_scopes[&_contract];
|
||||
|
||||
linearizeBaseContracts(_contract);
|
||||
std::vector<ContractDefinition const*> properBases(
|
||||
++_contract.annotation().linearizedBaseContracts.begin(),
|
||||
_contract.annotation().linearizedBaseContracts.end()
|
||||
);
|
||||
|
||||
for (ContractDefinition const* base: properBases)
|
||||
importInheritedScope(*base);
|
||||
|
||||
for (ASTPointer<StructDefinition> const& structDef: _contract.definedStructs())
|
||||
ReferencesResolver resolver(*structDef, *this, &_contract, nullptr);
|
||||
for (ASTPointer<EnumDefinition> const& enumDef: _contract.definedEnums())
|
||||
ReferencesResolver resolver(*enumDef, *this, &_contract, nullptr);
|
||||
for (ASTPointer<VariableDeclaration> const& variable: _contract.stateVariables())
|
||||
ReferencesResolver resolver(*variable, *this, &_contract, nullptr);
|
||||
for (ASTPointer<EventDefinition> const& event: _contract.events())
|
||||
ReferencesResolver resolver(*event, *this, &_contract, nullptr);
|
||||
|
||||
// these can contain code, only resolve parameters for now
|
||||
for (ASTPointer<ModifierDefinition> const& modifier: _contract.functionModifiers())
|
||||
try
|
||||
{
|
||||
m_currentScope = &m_scopes[modifier.get()];
|
||||
ReferencesResolver resolver(*modifier, *this, &_contract, nullptr);
|
||||
}
|
||||
for (ASTPointer<FunctionDefinition> const& function: _contract.definedFunctions())
|
||||
{
|
||||
m_currentScope = &m_scopes[function.get()];
|
||||
ReferencesResolver referencesResolver(
|
||||
*function,
|
||||
*this,
|
||||
&_contract,
|
||||
function->returnParameterList().get()
|
||||
m_currentScope = &m_scopes[nullptr];
|
||||
|
||||
for (ASTPointer<InheritanceSpecifier> const& baseContract: _contract.baseContracts())
|
||||
ReferencesResolver resolver(*baseContract, *this, &_contract, nullptr);
|
||||
|
||||
m_currentScope = &m_scopes[&_contract];
|
||||
|
||||
linearizeBaseContracts(_contract);
|
||||
std::vector<ContractDefinition const*> properBases(
|
||||
++_contract.annotation().linearizedBaseContracts.begin(),
|
||||
_contract.annotation().linearizedBaseContracts.end()
|
||||
);
|
||||
}
|
||||
|
||||
m_currentScope = &m_scopes[&_contract];
|
||||
for (ContractDefinition const* base: properBases)
|
||||
importInheritedScope(*base);
|
||||
|
||||
// now resolve references inside the code
|
||||
for (ASTPointer<ModifierDefinition> const& modifier: _contract.functionModifiers())
|
||||
{
|
||||
m_currentScope = &m_scopes[modifier.get()];
|
||||
ReferencesResolver resolver(*modifier, *this, &_contract, nullptr, true);
|
||||
for (ASTPointer<StructDefinition> const& structDef: _contract.definedStructs())
|
||||
ReferencesResolver resolver(*structDef, *this, &_contract, nullptr);
|
||||
for (ASTPointer<EnumDefinition> const& enumDef: _contract.definedEnums())
|
||||
ReferencesResolver resolver(*enumDef, *this, &_contract, nullptr);
|
||||
for (ASTPointer<VariableDeclaration> const& variable: _contract.stateVariables())
|
||||
ReferencesResolver resolver(*variable, *this, &_contract, nullptr);
|
||||
for (ASTPointer<EventDefinition> const& event: _contract.events())
|
||||
ReferencesResolver resolver(*event, *this, &_contract, nullptr);
|
||||
|
||||
// these can contain code, only resolve parameters for now
|
||||
for (ASTPointer<ModifierDefinition> const& modifier: _contract.functionModifiers())
|
||||
{
|
||||
m_currentScope = &m_scopes[modifier.get()];
|
||||
ReferencesResolver resolver(*modifier, *this, &_contract, nullptr);
|
||||
}
|
||||
for (ASTPointer<FunctionDefinition> const& function: _contract.definedFunctions())
|
||||
{
|
||||
m_currentScope = &m_scopes[function.get()];
|
||||
ReferencesResolver referencesResolver(
|
||||
*function,
|
||||
*this,
|
||||
&_contract,
|
||||
function->returnParameterList().get()
|
||||
);
|
||||
}
|
||||
|
||||
m_currentScope = &m_scopes[&_contract];
|
||||
|
||||
// now resolve references inside the code
|
||||
for (ASTPointer<ModifierDefinition> const& modifier: _contract.functionModifiers())
|
||||
{
|
||||
m_currentScope = &m_scopes[modifier.get()];
|
||||
ReferencesResolver resolver(*modifier, *this, &_contract, nullptr, true);
|
||||
}
|
||||
for (ASTPointer<FunctionDefinition> const& function: _contract.definedFunctions())
|
||||
{
|
||||
m_currentScope = &m_scopes[function.get()];
|
||||
ReferencesResolver referencesResolver(
|
||||
*function,
|
||||
*this,
|
||||
&_contract,
|
||||
function->returnParameterList().get(),
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
for (ASTPointer<FunctionDefinition> const& function: _contract.definedFunctions())
|
||||
catch (fatalError const& _e)
|
||||
{
|
||||
m_currentScope = &m_scopes[function.get()];
|
||||
ReferencesResolver referencesResolver(
|
||||
*function,
|
||||
*this,
|
||||
&_contract,
|
||||
function->returnParameterList().get(),
|
||||
true
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
|
||||
bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
|
||||
{
|
||||
m_scopes[nullptr].registerDeclaration(_declaration, false, true);
|
||||
solAssert(_declaration.scope() == nullptr, "Updated declaration outside global scope.");
|
||||
try
|
||||
{
|
||||
m_scopes[nullptr].registerDeclaration(_declaration, false, true);
|
||||
solAssert(_declaration.scope() == nullptr, "Updated declaration outside global scope.");
|
||||
}
|
||||
catch(fatalError _error)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
vector<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const
|
||||
@@ -164,11 +188,8 @@ vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
|
||||
FunctionType functionType(functionDefinition);
|
||||
for (auto parameter: functionType.parameterTypes() + functionType.returnParameterTypes())
|
||||
if (!parameter)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
Error(Error::Type::DeclarationError) <<
|
||||
errinfo_sourceLocation(_identifier.location()) <<
|
||||
errinfo_comment("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(
|
||||
uniqueFunctions.begin(),
|
||||
uniqueFunctions.end(),
|
||||
@@ -194,7 +215,7 @@ void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base)
|
||||
m_currentScope->registerDeclaration(*declaration);
|
||||
}
|
||||
|
||||
void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract) const
|
||||
void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract)
|
||||
{
|
||||
// order in the lists is from derived to base
|
||||
// list of lists to linearize, the last element is the list of direct bases
|
||||
@@ -204,19 +225,19 @@ void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract)
|
||||
Identifier const& baseName = baseSpecifier->name();
|
||||
auto base = dynamic_cast<ContractDefinition const*>(baseName.annotation().referencedDeclaration);
|
||||
if (!base)
|
||||
BOOST_THROW_EXCEPTION(baseName.createTypeError("Contract expected."));
|
||||
reportFatalTypeError(baseName.createTypeError("Contract expected."));
|
||||
// "push_front" has the effect that bases mentioned later can overwrite members of bases
|
||||
// mentioned earlier
|
||||
input.back().push_front(base);
|
||||
vector<ContractDefinition const*> const& basesBases = base->annotation().linearizedBaseContracts;
|
||||
if (basesBases.empty())
|
||||
BOOST_THROW_EXCEPTION(baseName.createTypeError("Definition of base has to precede definition of derived contract"));
|
||||
reportFatalTypeError(baseName.createTypeError("Definition of base has to precede definition of derived contract"));
|
||||
input.push_front(list<ContractDefinition const*>(basesBases.begin(), basesBases.end()));
|
||||
}
|
||||
input.back().push_front(&_contract);
|
||||
vector<ContractDefinition const*> result = cThreeMerge(input);
|
||||
if (result.empty())
|
||||
BOOST_THROW_EXCEPTION(_contract.createTypeError("Linearization of inheritance graph impossible"));
|
||||
reportFatalTypeError(_contract.createTypeError("Linearization of inheritance graph impossible"));
|
||||
_contract.annotation().linearizedBaseContracts = result;
|
||||
_contract.annotation().contractDependencies.insert(result.begin() + 1, result.end());
|
||||
}
|
||||
@@ -272,9 +293,53 @@ vector<_T const*> NameAndTypeResolver::cThreeMerge(list<list<_T const*>>& _toMer
|
||||
return result;
|
||||
}
|
||||
|
||||
DeclarationRegistrationHelper::DeclarationRegistrationHelper(map<ASTNode const*, DeclarationContainer>& _scopes,
|
||||
ASTNode& _astRoot):
|
||||
m_scopes(_scopes), m_currentScope(nullptr)
|
||||
void NameAndTypeResolver::reportDeclarationError(
|
||||
SourceLocation _sourceLoction,
|
||||
string const& _description,
|
||||
SourceLocation _secondarySourceLocation = SourceLocation(),
|
||||
string const& _secondaryDescription = ""
|
||||
)
|
||||
{
|
||||
auto err = make_shared<Error>(Error::Type::DeclarationError); // todo remove Error?
|
||||
*err <<
|
||||
errinfo_sourceLocation(_sourceLoction) <<
|
||||
errinfo_comment(_description) <<
|
||||
errinfo_secondarySourceLocation(
|
||||
SecondarySourceLocation().append(_secondaryDescription, _secondarySourceLocation)
|
||||
);
|
||||
|
||||
m_errors.push_back(err);
|
||||
}
|
||||
|
||||
void NameAndTypeResolver::reportFatalDeclarationError(
|
||||
SourceLocation _sourceLoction,
|
||||
string _description
|
||||
)
|
||||
{
|
||||
reportDeclarationError(_sourceLoction, _description);
|
||||
BOOST_THROW_EXCEPTION(fatalError());
|
||||
}
|
||||
|
||||
void NameAndTypeResolver::reportTypeError(Error _e)
|
||||
{
|
||||
m_errors.push_back(make_shared<Error>(_e));
|
||||
}
|
||||
|
||||
void NameAndTypeResolver::reportFatalTypeError(Error _e)
|
||||
{
|
||||
reportTypeError(_e);
|
||||
BOOST_THROW_EXCEPTION(fatalError());
|
||||
}
|
||||
|
||||
|
||||
DeclarationRegistrationHelper::DeclarationRegistrationHelper(
|
||||
map<ASTNode const*, DeclarationContainer>& _scopes,
|
||||
ASTNode& _astRoot,
|
||||
ErrorList& _errors
|
||||
):
|
||||
m_scopes(_scopes),
|
||||
m_currentScope(nullptr),
|
||||
m_errors(_errors)
|
||||
{
|
||||
_astRoot.accept(*this);
|
||||
}
|
||||
@@ -409,13 +474,11 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio
|
||||
secondDeclarationLocation = _declaration.location();
|
||||
}
|
||||
|
||||
BOOST_THROW_EXCEPTION(
|
||||
Error(Error::Type::DeclarationError) <<
|
||||
errinfo_sourceLocation(secondDeclarationLocation) <<
|
||||
errinfo_comment("Identifier already declared.") <<
|
||||
errinfo_secondarySourceLocation(
|
||||
SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation)
|
||||
)
|
||||
declarationError(
|
||||
secondDeclarationLocation,
|
||||
"Identifier already declared.",
|
||||
firstDeclarationLocation,
|
||||
"The previous declaration is here:"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -440,5 +503,32 @@ string DeclarationRegistrationHelper::currentCanonicalName() const
|
||||
return ret;
|
||||
}
|
||||
|
||||
void DeclarationRegistrationHelper::declarationError(
|
||||
SourceLocation _sourceLoction,
|
||||
string const& _description,
|
||||
SourceLocation _secondarySourceLocation = SourceLocation(),
|
||||
string const& _secondaryDescription = ""
|
||||
)
|
||||
{
|
||||
auto err = make_shared<Error>(Error::Type::DeclarationError);
|
||||
*err <<
|
||||
errinfo_sourceLocation(_sourceLoction) <<
|
||||
errinfo_comment(_description) <<
|
||||
errinfo_secondarySourceLocation(
|
||||
SecondarySourceLocation().append(_secondaryDescription, _secondarySourceLocation)
|
||||
);
|
||||
|
||||
m_errors.push_back(err);
|
||||
}
|
||||
|
||||
void DeclarationRegistrationHelper::fatalDeclarationError(
|
||||
SourceLocation _sourceLoction,
|
||||
string const& _description
|
||||
)
|
||||
{
|
||||
declarationError(_sourceLoction, _description);
|
||||
BOOST_THROW_EXCEPTION(fatalError());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,12 +44,15 @@ class NameAndTypeResolver: private boost::noncopyable
|
||||
public:
|
||||
NameAndTypeResolver(std::vector<Declaration const*> const& _globals, ErrorList& _errors);
|
||||
/// Registers all declarations found in the source unit.
|
||||
void registerDeclarations(SourceUnit& _sourceUnit);
|
||||
/// @returns false in case of type error.
|
||||
bool registerDeclarations(SourceUnit& _sourceUnit);
|
||||
/// Resolves all names and types referenced from the given contract.
|
||||
void resolveNamesAndTypes(ContractDefinition& _contract);
|
||||
/// @returns false in case of type error.
|
||||
bool resolveNamesAndTypes(ContractDefinition& _contract);
|
||||
/// Updates the given global declaration (used for "this"). Not to be used with declarations
|
||||
/// that create their own scope.
|
||||
void updateDeclaration(Declaration const& _declaration);
|
||||
/// @returns false in case of type error.
|
||||
bool updateDeclaration(Declaration const& _declaration);
|
||||
|
||||
/// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted,
|
||||
/// the global scope is used (i.e. the one containing only the contract).
|
||||
@@ -66,7 +69,7 @@ public:
|
||||
Declaration const* pathFromCurrentScope(std::vector<ASTString> const& _path, bool _recursive = true) const;
|
||||
|
||||
/// returns the vector of declarations without repetitions
|
||||
static std::vector<Declaration const*> cleanedDeclarations(
|
||||
std::vector<Declaration const*> cleanedDeclarations(
|
||||
Identifier const& _identifier,
|
||||
std::vector<Declaration const*> const& _declarations
|
||||
);
|
||||
@@ -78,8 +81,8 @@ private:
|
||||
/// into the current scope if they are not present already.
|
||||
void importInheritedScope(ContractDefinition const& _base);
|
||||
|
||||
/// Computes "C3-Linearization" of base contracts and stores it inside the contract.
|
||||
void linearizeBaseContracts(ContractDefinition& _contract) const;
|
||||
/// Computes "C3-Linearization" of base contracts and stores it inside the contract. Reports errors if any
|
||||
void linearizeBaseContracts(ContractDefinition& _contract);
|
||||
/// Computes the C3-merge of the given list of lists of bases.
|
||||
/// @returns the linearized vector or an empty vector if linearization is not possible.
|
||||
template <class _T>
|
||||
@@ -90,6 +93,21 @@ private:
|
||||
/// not contain code.
|
||||
std::map<ASTNode const*, DeclarationContainer> m_scopes;
|
||||
|
||||
// creates the Declaration error and adds it in the errors list
|
||||
void reportDeclarationError(
|
||||
SourceLocation _sourceLoction,
|
||||
std::string const& _description,
|
||||
SourceLocation _secondarySourceLocation,
|
||||
std::string const& _secondaryDescription
|
||||
);
|
||||
// creates the Declaration error and adds it in the errors list and throws FatalError
|
||||
void reportFatalDeclarationError(SourceLocation _sourceLoction, std::string _description);
|
||||
|
||||
// creates the Declaration error and adds it in the errors list
|
||||
void reportTypeError(Error _e);
|
||||
// creates the Declaration error and adds it in the errors list and throws FatalError
|
||||
void reportFatalTypeError(Error _e);
|
||||
|
||||
DeclarationContainer* m_currentScope = nullptr;
|
||||
ErrorList& m_errors;
|
||||
};
|
||||
@@ -101,7 +119,7 @@ private:
|
||||
class DeclarationRegistrationHelper: private ASTVisitor
|
||||
{
|
||||
public:
|
||||
DeclarationRegistrationHelper(std::map<ASTNode const*, DeclarationContainer>& _scopes, ASTNode& _astRoot);
|
||||
DeclarationRegistrationHelper(std::map<ASTNode const*, DeclarationContainer>& _scopes, ASTNode& _astRoot, ErrorList& _errors);
|
||||
|
||||
private:
|
||||
bool visit(ContractDefinition& _contract) override;
|
||||
@@ -126,10 +144,20 @@ private:
|
||||
|
||||
/// @returns the canonical name of the current scope.
|
||||
std::string currentCanonicalName() const;
|
||||
// creates the Declaration error and adds it in the errors list
|
||||
void declarationError(
|
||||
SourceLocation _sourceLoction,
|
||||
std::string const& _description,
|
||||
SourceLocation _secondarySourceLocation,
|
||||
std::string const& _secondaryDescription
|
||||
);
|
||||
// creates the Declaration error and adds it in the errors list and throws FatalError
|
||||
void fatalDeclarationError(SourceLocation _sourceLoction, std::string const& _description);
|
||||
|
||||
std::map<ASTNode const*, DeclarationContainer>& m_scopes;
|
||||
Declaration const* m_currentScope;
|
||||
VariableScope* m_currentFunction;
|
||||
ErrorList& m_errors;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+52
-34
@@ -66,25 +66,35 @@ private:
|
||||
|
||||
ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
|
||||
{
|
||||
m_scanner = _scanner;
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
vector<ASTPointer<ASTNode>> nodes;
|
||||
while (m_scanner->currentToken() != Token::EOS)
|
||||
{
|
||||
switch (auto token = m_scanner->currentToken())
|
||||
try{
|
||||
m_scanner = _scanner;
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
vector<ASTPointer<ASTNode>> nodes;
|
||||
while (m_scanner->currentToken() != Token::EOS)
|
||||
{
|
||||
case Token::Import:
|
||||
nodes.push_back(parseImportDirective());
|
||||
break;
|
||||
case Token::Contract:
|
||||
case Token::Library:
|
||||
nodes.push_back(parseContractDefinition(token == Token::Library));
|
||||
break;
|
||||
default:
|
||||
BOOST_THROW_EXCEPTION(createParserError(std::string("Expected import directive or contract definition.")));
|
||||
switch (auto token = m_scanner->currentToken())
|
||||
{
|
||||
case Token::Import:
|
||||
nodes.push_back(parseImportDirective());
|
||||
break;
|
||||
case Token::Contract:
|
||||
case Token::Library:
|
||||
nodes.push_back(parseContractDefinition(token == Token::Library));
|
||||
break;
|
||||
default:
|
||||
fatalParserError(std::string("Expected import directive or contract definition."));
|
||||
}
|
||||
}
|
||||
return nodeFactory.createNode<SourceUnit>(nodes);
|
||||
}
|
||||
catch(fatalError const& _error)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
catch(Exception const& _e)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return nodeFactory.createNode<SourceUnit>(nodes);
|
||||
}
|
||||
|
||||
std::shared_ptr<const string> const& Parser::sourceName() const
|
||||
@@ -107,7 +117,7 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
expectToken(Token::Import);
|
||||
if (m_scanner->currentToken() != Token::StringLiteral)
|
||||
BOOST_THROW_EXCEPTION(createParserError("Expected string literal (URL)."));
|
||||
fatalParserError(std::string("Expected string literal (URL)."));
|
||||
ASTPointer<ASTString> url = getLiteralAndAdvance();
|
||||
nodeFactory.markEndPosition();
|
||||
expectToken(Token::Semicolon);
|
||||
@@ -165,7 +175,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary)
|
||||
else if (currentTokenValue == Token::Event)
|
||||
events.push_back(parseEventDefinition());
|
||||
else
|
||||
BOOST_THROW_EXCEPTION(createParserError("Function, variable, struct or modifier declaration expected."));
|
||||
fatalParserError(std::string("Function, variable, struct or modifier declaration expected."));
|
||||
}
|
||||
nodeFactory.markEndPosition();
|
||||
expectToken(Token::RBrace);
|
||||
@@ -249,7 +259,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
|
||||
else if (Token::isVisibilitySpecifier(token))
|
||||
{
|
||||
if (visibility != Declaration::Visibility::Default)
|
||||
BOOST_THROW_EXCEPTION(createParserError("Multiple visibility specifiers."));
|
||||
fatalParserError(std::string("Multiple visibility specifiers."));
|
||||
visibility = parseVisibilitySpecifier(token);
|
||||
}
|
||||
else
|
||||
@@ -326,7 +336,7 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
|
||||
break;
|
||||
expectToken(Token::Comma);
|
||||
if (m_scanner->currentToken() != Token::Identifier)
|
||||
BOOST_THROW_EXCEPTION(createParserError("Expected Identifier after ','"));
|
||||
fatalParserError(std::string("Expected Identifier after ','"));
|
||||
}
|
||||
|
||||
nodeFactory.markEndPosition();
|
||||
@@ -362,7 +372,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
||||
if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token))
|
||||
{
|
||||
if (visibility != Declaration::Visibility::Default)
|
||||
BOOST_THROW_EXCEPTION(createParserError("Visibility already specified."));
|
||||
fatalParserError(std::string("Visibility already specified."));
|
||||
visibility = parseVisibilitySpecifier(token);
|
||||
}
|
||||
else
|
||||
@@ -374,9 +384,9 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
||||
else if (_options.allowLocationSpecifier && Token::isLocationSpecifier(token))
|
||||
{
|
||||
if (location != VariableDeclaration::Location::Default)
|
||||
BOOST_THROW_EXCEPTION(createParserError("Location already specified."));
|
||||
fatalParserError(std::string("Location already specified."));
|
||||
if (!type)
|
||||
BOOST_THROW_EXCEPTION(createParserError("Location specifier needs explicit type name."));
|
||||
fatalParserError(std::string("Location specifier needs explicit type name."));
|
||||
location = (
|
||||
token == Token::Memory ?
|
||||
VariableDeclaration::Location::Memory :
|
||||
@@ -513,7 +523,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
|
||||
else if (token == Token::Var)
|
||||
{
|
||||
if (!_allowVar)
|
||||
BOOST_THROW_EXCEPTION(createParserError("Expected explicit type name."));
|
||||
fatalParserError(std::string("Expected explicit type name."));
|
||||
m_scanner->next();
|
||||
}
|
||||
else if (token == Token::Mapping)
|
||||
@@ -532,7 +542,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
|
||||
type = nodeFactory.createNode<UserDefinedTypeName>(identifierPath);
|
||||
}
|
||||
else
|
||||
BOOST_THROW_EXCEPTION(createParserError("Expected type name"));
|
||||
fatalParserError(std::string("Expected type name"));
|
||||
|
||||
if (type)
|
||||
// Parse "[...]" postfixes for arrays.
|
||||
@@ -555,7 +565,7 @@ ASTPointer<Mapping> Parser::parseMapping()
|
||||
expectToken(Token::Mapping);
|
||||
expectToken(Token::LParen);
|
||||
if (!Token::isElementaryTypeName(m_scanner->currentToken()))
|
||||
BOOST_THROW_EXCEPTION(createParserError("Expected elementary type name for mapping key type"));
|
||||
fatalParserError(std::string("Expected elementary type name for mapping key type"));
|
||||
ASTPointer<ElementaryTypeName> keyType;
|
||||
keyType = ASTNodeFactory(*this).createNode<ElementaryTypeName>(m_scanner->currentToken());
|
||||
m_scanner->next();
|
||||
@@ -1011,7 +1021,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
|
||||
m_scanner->next();
|
||||
}
|
||||
else
|
||||
BOOST_THROW_EXCEPTION(createParserError("Expected primary expression."));
|
||||
fatalParserError(std::string("Expected primary expression."));
|
||||
break;
|
||||
}
|
||||
return expression;
|
||||
@@ -1122,7 +1132,7 @@ ASTPointer<Expression> Parser::expressionFromIndexAccessStructure(
|
||||
void Parser::expectToken(Token::Value _value)
|
||||
{
|
||||
if (m_scanner->currentToken() != _value)
|
||||
BOOST_THROW_EXCEPTION(createParserError(string("Expected token ") + string(Token::name(_value))));
|
||||
fatalParserError(std::string(string("Expected token ") + string(Token::name(_value))));
|
||||
m_scanner->next();
|
||||
}
|
||||
|
||||
@@ -1130,7 +1140,7 @@ Token::Value Parser::expectAssignmentOperator()
|
||||
{
|
||||
Token::Value op = m_scanner->currentToken();
|
||||
if (!Token::isAssignmentOp(op))
|
||||
BOOST_THROW_EXCEPTION(createParserError("Expected assignment operator"));
|
||||
fatalParserError(std::string("Expected assignment operator"));
|
||||
m_scanner->next();
|
||||
return op;
|
||||
}
|
||||
@@ -1138,7 +1148,7 @@ Token::Value Parser::expectAssignmentOperator()
|
||||
ASTPointer<ASTString> Parser::expectIdentifierToken()
|
||||
{
|
||||
if (m_scanner->currentToken() != Token::Identifier)
|
||||
BOOST_THROW_EXCEPTION(createParserError("Expected identifier"));
|
||||
fatalParserError(std::string("Expected identifier"));
|
||||
return getLiteralAndAdvance();
|
||||
}
|
||||
|
||||
@@ -1156,13 +1166,21 @@ ASTPointer<ParameterList> Parser::createEmptyParameterList()
|
||||
return nodeFactory.createNode<ParameterList>(vector<ASTPointer<VariableDeclaration>>());
|
||||
}
|
||||
|
||||
Error Parser::createParserError(string const& _description) const
|
||||
void Parser::parserError(string const& _description)
|
||||
{
|
||||
return Error(Error::Type::ParserError) <<
|
||||
errinfo_sourceLocation(SourceLocation(position(), position(), sourceName())) <<
|
||||
errinfo_comment(_description);
|
||||
auto err = make_shared<Error>(Error::Type::ParserError);
|
||||
*err <<
|
||||
errinfo_sourceLocation(SourceLocation(position(), position(), sourceName())) <<
|
||||
errinfo_comment(_description);
|
||||
|
||||
m_errors.push_back(err);
|
||||
}
|
||||
|
||||
void Parser::fatalParserError(string const& _description)
|
||||
{
|
||||
parserError(_description);
|
||||
BOOST_THROW_EXCEPTION(fatalError());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+10
-3
@@ -34,7 +34,8 @@ class Scanner;
|
||||
class Parser
|
||||
{
|
||||
public:
|
||||
Parser() {}
|
||||
Parser(ErrorList& errors):
|
||||
m_errors(errors){};
|
||||
|
||||
ASTPointer<SourceUnit> parse(std::shared_ptr<Scanner> const& _scanner);
|
||||
std::shared_ptr<std::string const> const& sourceName() const;
|
||||
@@ -145,13 +146,19 @@ private:
|
||||
/// Creates an empty ParameterList at the current location (used if parameters can be omitted).
|
||||
ASTPointer<ParameterList> createEmptyParameterList();
|
||||
|
||||
/// Creates a @ref ParserError exception and annotates it with the current position and the
|
||||
/// Creates a @ref ParserError and annotates it with the current position and the
|
||||
/// given @a _description.
|
||||
Error createParserError(std::string const& _description) const;
|
||||
void parserError(std::string const& _description);
|
||||
|
||||
/// Creates a @ref ParserError and annotates it with the current position and the
|
||||
/// given @a _description. Throws the FatalError.
|
||||
void fatalParserError(std::string const& _description);
|
||||
|
||||
std::shared_ptr<Scanner> m_scanner;
|
||||
/// Flag that signifies whether '_' is parsed as a PlaceholderStatement or a regular identifier.
|
||||
bool m_insideModifier = false;
|
||||
/// The reference to the list of errors and warning to add errors/warnings during parsing
|
||||
ErrorList& m_errors;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+16
-14
@@ -36,24 +36,26 @@ bool TypeChecker::checkTypeRequirements(const ContractDefinition& _contract)
|
||||
{
|
||||
visit(_contract);
|
||||
}
|
||||
catch (FatalError const&)
|
||||
catch (fatalError const&)
|
||||
{
|
||||
// We got a fatal error which required to stop further type checking, but we can
|
||||
// continue normally from here.
|
||||
if (m_errors.empty())
|
||||
throw; // Something is weird here, rather throw again.
|
||||
}
|
||||
bool success = true;
|
||||
for (auto const& it: m_errors)
|
||||
{
|
||||
Error const& e = dynamic_cast<Error const&>(it.get());
|
||||
if (e.type() != Error::Type::Warning)
|
||||
{
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
|
||||
return Error::containsOnlyWarnings(m_errors);
|
||||
// bool success = true;
|
||||
// for (auto const& it: m_errors)
|
||||
// {
|
||||
// auto e = dynamic_cast<Error const*>(it.get());
|
||||
// if (e->type() != Error::Type::Warning)
|
||||
// {
|
||||
// success = false;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// return success;
|
||||
}
|
||||
|
||||
TypePointer const& TypeChecker::type(Expression const& _expression) const
|
||||
@@ -1255,7 +1257,7 @@ void TypeChecker::requireLValue(Expression const& _expression)
|
||||
|
||||
void TypeChecker::typeError(ASTNode const& _node, string const& _description)
|
||||
{
|
||||
auto err = make_shared<Error>(Error(Error::Type::TypeError));;
|
||||
auto err = make_shared<Error>(Error::Type::TypeError);
|
||||
*err <<
|
||||
errinfo_sourceLocation(_node.location()) <<
|
||||
errinfo_comment(_description);
|
||||
@@ -1266,5 +1268,5 @@ void TypeChecker::typeError(ASTNode const& _node, string const& _description)
|
||||
void TypeChecker::fatalTypeError(ASTNode const& _node, string const& _description)
|
||||
{
|
||||
typeError(_node, _description);
|
||||
BOOST_THROW_EXCEPTION(FatalError());
|
||||
BOOST_THROW_EXCEPTION(fatalError());
|
||||
}
|
||||
|
||||
@@ -42,26 +42,26 @@ namespace solidity
|
||||
class TypeChecker: private ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
/// @_errors the reference to the list of errors and warnings to add them found during type checking.
|
||||
TypeChecker(ErrorList& _errors): m_errors(_errors) {}
|
||||
|
||||
/// Performs type checking on the given contract and all of its sub-nodes.
|
||||
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
|
||||
bool checkTypeRequirements(ContractDefinition const& _contract);
|
||||
|
||||
/// @returns the list of errors and warnings found during type checking.
|
||||
ErrorList const& errors() const { return m_errors; }
|
||||
|
||||
/// @returns the type of an expression and asserts that it is present.
|
||||
TypePointer const& type(Expression const& _expression) const;
|
||||
/// @returns the type of the given variable and throws if the type is not present
|
||||
/// (this can happen for variables with non-explicit types before their types are resolved)
|
||||
TypePointer const& type(VariableDeclaration const& _variable) const;
|
||||
|
||||
private:
|
||||
/// Adds a new error to the list of errors.
|
||||
void typeError(ASTNode const& _node, std::string const& _description);
|
||||
|
||||
/// Adds a new error to the list of errors and throws to abort type checking.
|
||||
void fatalTypeError(ASTNode const& _node, std::string const& _description);
|
||||
|
||||
private:
|
||||
virtual bool visit(ContractDefinition const& _contract) override;
|
||||
/// Checks that two functions defined in this contract with the same name have different
|
||||
/// arguments and that there is at most one constructor.
|
||||
@@ -114,7 +114,7 @@ private:
|
||||
/// Runs type checks on @a _expression to infer its type and then checks that it is an LValue.
|
||||
void requireLValue(Expression const& _expression);
|
||||
|
||||
ErrorList m_errors;
|
||||
ErrorList& m_errors;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user