Merge pull request #246 from chriseth/refactor

Refactoring - more flexible contracts.
This commit is contained in:
chriseth 2015-11-26 17:40:40 +01:00
commit c806b9bcdb
21 changed files with 189 additions and 247 deletions

View File

@ -64,7 +64,7 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
{ {
m_currentScope = &m_scopes[nullptr]; m_currentScope = &m_scopes[nullptr];
ReferencesResolver resolver(m_errors, *this, &_contract, nullptr); ReferencesResolver resolver(m_errors, *this, nullptr);
bool success = true; bool success = true;
for (ASTPointer<InheritanceSpecifier> const& baseContract: _contract.baseContracts()) for (ASTPointer<InheritanceSpecifier> const& baseContract: _contract.baseContracts())
if (!resolver.resolve(*baseContract)) if (!resolver.resolve(*baseContract))
@ -84,36 +84,11 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
importInheritedScope(*base); importInheritedScope(*base);
} }
for (ASTPointer<StructDefinition> const& structDef: _contract.definedStructs())
if (!resolver.resolve(*structDef))
success = false;
for (ASTPointer<EnumDefinition> const& enumDef: _contract.definedEnums())
if (!resolver.resolve(*enumDef))
success = false;
for (ASTPointer<VariableDeclaration> const& variable: _contract.stateVariables())
if (!resolver.resolve(*variable))
success = false;
for (ASTPointer<EventDefinition> const& event: _contract.events())
if (!resolver.resolve(*event))
success = false;
// these can contain code, only resolve parameters for now // these can contain code, only resolve parameters for now
for (ASTPointer<ModifierDefinition> const& modifier: _contract.functionModifiers()) for (ASTPointer<ASTNode> const& node: _contract.subNodes())
{ {
m_currentScope = &m_scopes[modifier.get()]; m_currentScope = &m_scopes[m_scopes.count(node.get()) ? node.get() : &_contract];
ReferencesResolver resolver(m_errors, *this, &_contract, nullptr); if (!resolver.resolve(*node))
if (!resolver.resolve(*modifier))
success = false;
}
for (ASTPointer<FunctionDefinition> const& function: _contract.definedFunctions())
{
m_currentScope = &m_scopes[function.get()];
if (!ReferencesResolver(
m_errors,
*this,
&_contract,
function->returnParameterList().get()
).resolve(*function))
success = false; success = false;
} }
@ -123,21 +98,20 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
m_currentScope = &m_scopes[&_contract]; m_currentScope = &m_scopes[&_contract];
// now resolve references inside the code // now resolve references inside the code
for (ASTPointer<ModifierDefinition> const& modifier: _contract.functionModifiers()) for (ModifierDefinition const* modifier: _contract.functionModifiers())
{ {
m_currentScope = &m_scopes[modifier.get()]; m_currentScope = &m_scopes[modifier];
ReferencesResolver resolver(m_errors, *this, &_contract, nullptr, true); ReferencesResolver resolver(m_errors, *this, nullptr, true);
if (!resolver.resolve(*modifier)) if (!resolver.resolve(*modifier))
success = false; success = false;
} }
for (ASTPointer<FunctionDefinition> const& function: _contract.definedFunctions()) for (FunctionDefinition const* function: _contract.definedFunctions())
{ {
m_currentScope = &m_scopes[function.get()]; m_currentScope = &m_scopes[function];
if (!ReferencesResolver( if (!ReferencesResolver(
m_errors, m_errors,
*this, *this,
&_contract,
function->returnParameterList().get(), function->returnParameterList().get(),
true true
).resolve(*function)) ).resolve(*function))

View File

@ -50,11 +50,10 @@ bool ReferencesResolver::visit(UserDefinedTypeName const& _typeName)
_typeName.annotation().referencedDeclaration = declaration; _typeName.annotation().referencedDeclaration = declaration;
_typeName.annotation().contractScope = m_currentContract;
return true; return true;
} }
bool ReferencesResolver::resolve(ASTNode& _root) bool ReferencesResolver::resolve(ASTNode const& _root)
{ {
try try
{ {
@ -73,10 +72,7 @@ bool ReferencesResolver::visit(Identifier const& _identifier)
if (declarations.empty()) if (declarations.empty())
fatalDeclarationError(_identifier.location(), "Undeclared identifier."); fatalDeclarationError(_identifier.location(), "Undeclared identifier.");
else if (declarations.size() == 1) else if (declarations.size() == 1)
{
_identifier.annotation().referencedDeclaration = declarations.front(); _identifier.annotation().referencedDeclaration = declarations.front();
_identifier.annotation().contractScope = m_currentContract;
}
else else
_identifier.annotation().overloadedDeclarations = _identifier.annotation().overloadedDeclarations =
m_resolver.cleanedDeclarations(_identifier, declarations); m_resolver.cleanedDeclarations(_identifier, declarations);

View File

@ -45,19 +45,17 @@ public:
ReferencesResolver( ReferencesResolver(
ErrorList& _errors, ErrorList& _errors,
NameAndTypeResolver& _resolver, NameAndTypeResolver& _resolver,
ContractDefinition const* _currentContract,
ParameterList const* _returnParameters, ParameterList const* _returnParameters,
bool _resolveInsideCode = false bool _resolveInsideCode = false
): ):
m_errors(_errors), m_errors(_errors),
m_resolver(_resolver), m_resolver(_resolver),
m_currentContract(_currentContract),
m_returnParameters(_returnParameters), m_returnParameters(_returnParameters),
m_resolveInsideCode(_resolveInsideCode) m_resolveInsideCode(_resolveInsideCode)
{} {}
/// @returns true if no errors during resolving /// @returns true if no errors during resolving
bool resolve(ASTNode& _root); bool resolve(ASTNode const& _root);
private: private:
virtual bool visit(Block const&) override { return m_resolveInsideCode; } virtual bool visit(Block const&) override { return m_resolveInsideCode; }
@ -83,7 +81,6 @@ private:
ErrorList& m_errors; ErrorList& m_errors;
NameAndTypeResolver& m_resolver; NameAndTypeResolver& m_resolver;
ContractDefinition const* m_currentContract;
ParameterList const* m_returnParameters; ParameterList const* m_returnParameters;
bool const m_resolveInsideCode; bool const m_resolveInsideCode;
bool m_errorOccurred = false; bool m_errorOccurred = false;

View File

@ -60,7 +60,10 @@ TypePointer const& TypeChecker::type(VariableDeclaration const& _variable) const
bool TypeChecker::visit(ContractDefinition const& _contract) bool TypeChecker::visit(ContractDefinition const& _contract)
{ {
m_scope = &_contract;
// We force our own visiting order here. // We force our own visiting order here.
//@TODO structs will be visited again below, but it is probably fine.
ASTNode::listAccept(_contract.definedStructs(), *this); ASTNode::listAccept(_contract.definedStructs(), *this);
ASTNode::listAccept(_contract.baseContracts(), *this); ASTNode::listAccept(_contract.baseContracts(), *this);
@ -74,7 +77,7 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
typeError(function->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor."); typeError(function->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor.");
FunctionDefinition const* fallbackFunction = nullptr; FunctionDefinition const* fallbackFunction = nullptr;
for (ASTPointer<FunctionDefinition> const& function: _contract.definedFunctions()) for (FunctionDefinition const* function: _contract.definedFunctions())
{ {
if (function->name().empty()) if (function->name().empty())
{ {
@ -86,7 +89,7 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
} }
else else
{ {
fallbackFunction = function.get(); fallbackFunction = function;
if (!fallbackFunction->parameters().empty()) if (!fallbackFunction->parameters().empty())
typeError(fallbackFunction->parameterList().location(), "Fallback function cannot take parameters."); typeError(fallbackFunction->parameterList().location(), "Fallback function cannot take parameters.");
} }
@ -95,10 +98,7 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
_contract.annotation().isFullyImplemented = false; _contract.annotation().isFullyImplemented = false;
} }
ASTNode::listAccept(_contract.stateVariables(), *this); ASTNode::listAccept(_contract.subNodes(), *this);
ASTNode::listAccept(_contract.events(), *this);
ASTNode::listAccept(_contract.functionModifiers(), *this);
ASTNode::listAccept(_contract.definedFunctions(), *this);
checkContractExternalTypeClashes(_contract); checkContractExternalTypeClashes(_contract);
// check for hash collisions in function signatures // check for hash collisions in function signatures
@ -125,8 +125,8 @@ void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _con
/// Checks that two functions with the same name defined in this contract have different /// Checks that two functions with the same name defined in this contract have different
/// argument types and that there is at most one constructor. /// argument types and that there is at most one constructor.
map<string, vector<FunctionDefinition const*>> functions; map<string, vector<FunctionDefinition const*>> functions;
for (ASTPointer<FunctionDefinition> const& function: _contract.definedFunctions()) for (FunctionDefinition const* function: _contract.definedFunctions())
functions[function->name()].push_back(function.get()); functions[function->name()].push_back(function);
// Constructor // Constructor
if (functions[_contract.name()].size() > 1) if (functions[_contract.name()].size() > 1)
@ -170,7 +170,7 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont
// 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 (ASTPointer<FunctionDefinition> const& function: contract->definedFunctions()) for (FunctionDefinition const* function: contract->definedFunctions())
{ {
auto& overloads = functions[function->name()]; auto& overloads = functions[function->name()];
FunctionTypePointer funType = make_shared<FunctionType>(*function); FunctionTypePointer funType = make_shared<FunctionType>(*function);
@ -246,7 +246,7 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr
// We search from derived to base, so the stored item causes the error. // We search from derived to base, so the stored item causes the error.
for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts) for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
{ {
for (ASTPointer<FunctionDefinition> const& function: contract->definedFunctions()) for (FunctionDefinition const* function: contract->definedFunctions())
{ {
if (function->isConstructor()) if (function->isConstructor())
continue; // constructors can neither be overridden nor override anything continue; // constructors can neither be overridden nor override anything
@ -267,14 +267,14 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr
) )
typeError(overriding->location(), "Override changes extended function signature."); typeError(overriding->location(), "Override changes extended function signature.");
} }
functions[name].push_back(function.get()); functions[name].push_back(function);
} }
for (ASTPointer<ModifierDefinition> const& modifier: contract->functionModifiers()) for (ModifierDefinition const* modifier: contract->functionModifiers())
{ {
string const& name = modifier->name(); string const& name = modifier->name();
ModifierDefinition const*& override = modifiers[name]; ModifierDefinition const*& override = modifiers[name];
if (!override) if (!override)
override = modifier.get(); override = modifier;
else if (ModifierType(*override) != ModifierType(*modifier)) else if (ModifierType(*override) != ModifierType(*modifier))
typeError(override->location(), "Override changes modifier signature."); typeError(override->location(), "Override changes modifier signature.");
if (!functions[name].empty()) if (!functions[name].empty())
@ -288,20 +288,20 @@ void TypeChecker::checkContractExternalTypeClashes(ContractDefinition const& _co
map<string, vector<pair<Declaration const*, FunctionTypePointer>>> externalDeclarations; map<string, vector<pair<Declaration const*, FunctionTypePointer>>> externalDeclarations;
for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts) for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
{ {
for (ASTPointer<FunctionDefinition> const& f: contract->definedFunctions()) for (FunctionDefinition const* f: contract->definedFunctions())
if (f->isPartOfExternalInterface()) if (f->isPartOfExternalInterface())
{ {
auto functionType = make_shared<FunctionType>(*f); auto functionType = make_shared<FunctionType>(*f);
externalDeclarations[functionType->externalSignature()].push_back( externalDeclarations[functionType->externalSignature()].push_back(
make_pair(f.get(), functionType) make_pair(f, functionType)
); );
} }
for (ASTPointer<VariableDeclaration> const& v: contract->stateVariables()) for (VariableDeclaration const* v: contract->stateVariables())
if (v->isPartOfExternalInterface()) if (v->isPartOfExternalInterface())
{ {
auto functionType = make_shared<FunctionType>(*v); auto functionType = make_shared<FunctionType>(*v);
externalDeclarations[functionType->externalSignature()].push_back( externalDeclarations[functionType->externalSignature()].push_back(
make_pair(v.get(), functionType) make_pair(v, functionType)
); );
} }
} }
@ -1057,13 +1057,13 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
if (!contract->annotation().isFullyImplemented) if (!contract->annotation().isFullyImplemented)
typeError(_newExpression.location(), "Trying to create an instance of an abstract contract."); typeError(_newExpression.location(), "Trying to create an instance of an abstract contract.");
auto scopeContract = contractName->annotation().contractScope; solAssert(!!m_scope, "");
scopeContract->annotation().contractDependencies.insert(contract); m_scope->annotation().contractDependencies.insert(contract);
solAssert( solAssert(
!contract->annotation().linearizedBaseContracts.empty(), !contract->annotation().linearizedBaseContracts.empty(),
"Linearized base contracts not yet available." "Linearized base contracts not yet available."
); );
if (contractDependenciesAreCyclic(*scopeContract)) if (contractDependenciesAreCyclic(*m_scope))
typeError( typeError(
_newExpression.location(), _newExpression.location(),
"Circular reference for contract creation (cannot create instance of derived or same contract)." "Circular reference for contract creation (cannot create instance of derived or same contract)."
@ -1112,7 +1112,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
// Retrieve the types of the arguments if this is used to call a function. // Retrieve the types of the arguments if this is used to call a function.
auto const& argumentTypes = _memberAccess.annotation().argumentTypes; auto const& argumentTypes = _memberAccess.annotation().argumentTypes;
MemberList::MemberMap possibleMembers = exprType->members().membersByName(memberName); MemberList::MemberMap possibleMembers = exprType->members(m_scope).membersByName(memberName);
if (possibleMembers.size() > 1 && argumentTypes) if (possibleMembers.size() > 1 && argumentTypes)
{ {
// do overload resolution // do overload resolution
@ -1131,7 +1131,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
DataLocation::Storage, DataLocation::Storage,
exprType exprType
); );
if (!storageType->members().membersByName(memberName).empty()) if (!storageType->members(m_scope).membersByName(memberName).empty())
fatalTypeError( fatalTypeError(
_memberAccess.location(), _memberAccess.location(),
"Member \"" + memberName + "\" is not available in " + "Member \"" + memberName + "\" is not available in " +
@ -1258,7 +1258,7 @@ bool TypeChecker::visit(Identifier const& _identifier)
for (Declaration const* declaration: annotation.overloadedDeclarations) for (Declaration const* declaration: annotation.overloadedDeclarations)
{ {
TypePointer function = declaration->type(_identifier.annotation().contractScope); TypePointer function = declaration->type();
solAssert(!!function, "Requested type not present."); solAssert(!!function, "Requested type not present.");
auto const* functionType = dynamic_cast<FunctionType const*>(function.get()); auto const* functionType = dynamic_cast<FunctionType const*>(function.get());
if (functionType && functionType->canTakeArguments(*annotation.argumentTypes)) if (functionType && functionType->canTakeArguments(*annotation.argumentTypes))
@ -1277,7 +1277,7 @@ bool TypeChecker::visit(Identifier const& _identifier)
"Referenced declaration is null after overload resolution." "Referenced declaration is null after overload resolution."
); );
annotation.isLValue = annotation.referencedDeclaration->isLValue(); annotation.isLValue = annotation.referencedDeclaration->isLValue();
annotation.type = annotation.referencedDeclaration->type(_identifier.annotation().contractScope); annotation.type = annotation.referencedDeclaration->type();
if (!annotation.type) if (!annotation.type)
fatalTypeError(_identifier.location(), "Declaration referenced before type could be determined."); fatalTypeError(_identifier.location(), "Declaration referenced before type could be determined.");
return false; return false;

View File

@ -117,6 +117,8 @@ private:
/// Runs type checks on @a _expression to infer its type and then checks that it is an LValue. /// Runs type checks on @a _expression to infer its type and then checks that it is an LValue.
void requireLValue(Expression const& _expression); void requireLValue(Expression const& _expression);
ContractDefinition const* m_scope = nullptr;
ErrorList& m_errors; ErrorList& m_errors;
}; };

View File

@ -74,29 +74,29 @@ map<FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions()
FunctionDefinition const* ContractDefinition::constructor() const FunctionDefinition const* ContractDefinition::constructor() const
{ {
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions) for (FunctionDefinition const* f: definedFunctions())
if (f->isConstructor()) if (f->isConstructor())
return f.get(); return f;
return nullptr; return nullptr;
} }
FunctionDefinition const* ContractDefinition::fallbackFunction() const FunctionDefinition const* ContractDefinition::fallbackFunction() const
{ {
for (ContractDefinition const* contract: annotation().linearizedBaseContracts) for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
for (ASTPointer<FunctionDefinition> const& f: contract->definedFunctions()) for (FunctionDefinition const* f: contract->definedFunctions())
if (f->name().empty()) if (f->name().empty())
return f.get(); return f;
return nullptr; return nullptr;
} }
vector<ASTPointer<EventDefinition>> const& ContractDefinition::interfaceEvents() const vector<EventDefinition const*> const& ContractDefinition::interfaceEvents() const
{ {
if (!m_interfaceEvents) if (!m_interfaceEvents)
{ {
set<string> eventsSeen; set<string> eventsSeen;
m_interfaceEvents.reset(new vector<ASTPointer<EventDefinition>>()); m_interfaceEvents.reset(new vector<EventDefinition const*>());
for (ContractDefinition const* contract: annotation().linearizedBaseContracts) for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
for (ASTPointer<EventDefinition> const& e: contract->events()) for (EventDefinition const* e: contract->events())
if (eventsSeen.count(e->name()) == 0) if (eventsSeen.count(e->name()) == 0)
{ {
eventsSeen.insert(e->name()); eventsSeen.insert(e->name());
@ -116,10 +116,10 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::inter
for (ContractDefinition const* contract: annotation().linearizedBaseContracts) for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
{ {
vector<FunctionTypePointer> functions; vector<FunctionTypePointer> functions;
for (ASTPointer<FunctionDefinition> const& f: contract->definedFunctions()) for (FunctionDefinition const* f: contract->definedFunctions())
if (f->isPartOfExternalInterface()) if (f->isPartOfExternalInterface())
functions.push_back(make_shared<FunctionType>(*f, false)); functions.push_back(make_shared<FunctionType>(*f, false));
for (ASTPointer<VariableDeclaration> const& v: contract->stateVariables()) for (VariableDeclaration const* v: contract->stateVariables())
if (v->isPartOfExternalInterface()) if (v->isPartOfExternalInterface())
functions.push_back(make_shared<FunctionType>(*v)); functions.push_back(make_shared<FunctionType>(*v));
for (FunctionTypePointer const& fun: functions) for (FunctionTypePointer const& fun: functions)
@ -176,21 +176,21 @@ vector<Declaration const*> const& ContractDefinition::inheritableMembers() const
} }
}; };
for (ASTPointer<FunctionDefinition> const& f: definedFunctions()) for (FunctionDefinition const* f: definedFunctions())
addInheritableMember(f.get()); addInheritableMember(f);
for (ASTPointer<VariableDeclaration> const& v: stateVariables()) for (VariableDeclaration const* v: stateVariables())
addInheritableMember(v.get()); addInheritableMember(v);
for (ASTPointer<StructDefinition> const& s: definedStructs()) for (StructDefinition const* s: definedStructs())
addInheritableMember(s.get()); addInheritableMember(s);
} }
return *m_inheritableMembers; return *m_inheritableMembers;
} }
TypePointer ContractDefinition::type(ContractDefinition const* m_currentContract) const TypePointer ContractDefinition::type() const
{ {
return make_shared<TypeType>(make_shared<ContractType>(*this), m_currentContract); return make_shared<TypeType>(make_shared<ContractType>(*this));
} }
ContractDefinitionAnnotation& ContractDefinition::annotation() const ContractDefinitionAnnotation& ContractDefinition::annotation() const
@ -207,7 +207,7 @@ TypeNameAnnotation& TypeName::annotation() const
return static_cast<TypeNameAnnotation&>(*m_annotation); return static_cast<TypeNameAnnotation&>(*m_annotation);
} }
TypePointer StructDefinition::type(ContractDefinition const*) const TypePointer StructDefinition::type() const
{ {
return make_shared<TypeType>(make_shared<StructType>(*this)); return make_shared<TypeType>(make_shared<StructType>(*this));
} }
@ -219,14 +219,14 @@ TypeDeclarationAnnotation& StructDefinition::annotation() const
return static_cast<TypeDeclarationAnnotation&>(*m_annotation); return static_cast<TypeDeclarationAnnotation&>(*m_annotation);
} }
TypePointer EnumValue::type(ContractDefinition const*) const TypePointer EnumValue::type() const
{ {
auto parentDef = dynamic_cast<EnumDefinition const*>(scope()); auto parentDef = dynamic_cast<EnumDefinition const*>(scope());
solAssert(parentDef, "Enclosing Scope of EnumValue was not set"); solAssert(parentDef, "Enclosing Scope of EnumValue was not set");
return make_shared<EnumType>(*parentDef); return make_shared<EnumType>(*parentDef);
} }
TypePointer EnumDefinition::type(ContractDefinition const*) const TypePointer EnumDefinition::type() const
{ {
return make_shared<TypeType>(make_shared<EnumType>(*this)); return make_shared<TypeType>(make_shared<EnumType>(*this));
} }
@ -238,7 +238,7 @@ TypeDeclarationAnnotation& EnumDefinition::annotation() const
return static_cast<TypeDeclarationAnnotation&>(*m_annotation); return static_cast<TypeDeclarationAnnotation&>(*m_annotation);
} }
TypePointer FunctionDefinition::type(ContractDefinition const*) const TypePointer FunctionDefinition::type() const
{ {
return make_shared<FunctionType>(*this); return make_shared<FunctionType>(*this);
} }
@ -255,7 +255,7 @@ FunctionDefinitionAnnotation& FunctionDefinition::annotation() const
return static_cast<FunctionDefinitionAnnotation&>(*m_annotation); return static_cast<FunctionDefinitionAnnotation&>(*m_annotation);
} }
TypePointer ModifierDefinition::type(ContractDefinition const*) const TypePointer ModifierDefinition::type() const
{ {
return make_shared<ModifierType>(*this); return make_shared<ModifierType>(*this);
} }
@ -267,7 +267,7 @@ ModifierDefinitionAnnotation& ModifierDefinition::annotation() const
return static_cast<ModifierDefinitionAnnotation&>(*m_annotation); return static_cast<ModifierDefinitionAnnotation&>(*m_annotation);
} }
TypePointer EventDefinition::type(ContractDefinition const*) const TypePointer EventDefinition::type() const
{ {
return make_shared<FunctionType>(*this); return make_shared<FunctionType>(*this);
} }
@ -324,7 +324,7 @@ bool VariableDeclaration::canHaveAutoType() const
return (!!callable && !isCallableParameter()); return (!!callable && !isCallableParameter());
} }
TypePointer VariableDeclaration::type(ContractDefinition const*) const TypePointer VariableDeclaration::type() const
{ {
return annotation().type; return annotation().type;
} }

View File

@ -58,18 +58,22 @@ public:
virtual void accept(ASTVisitor& _visitor) = 0; virtual void accept(ASTVisitor& _visitor) = 0;
virtual void accept(ASTConstVisitor& _visitor) const = 0; virtual void accept(ASTConstVisitor& _visitor) const = 0;
template <class T> template <class T>
static void listAccept(std::vector<ASTPointer<T>>& _list, ASTVisitor& _visitor) static void listAccept(std::vector<T> const& _list, ASTVisitor& _visitor)
{ {
for (ASTPointer<T>& element: _list) for (T const& element: _list)
element->accept(_visitor); element->accept(_visitor);
} }
template <class T> template <class T>
static void listAccept(std::vector<ASTPointer<T>> const& _list, ASTConstVisitor& _visitor) static void listAccept(std::vector<T> const& _list, ASTConstVisitor& _visitor)
{ {
for (ASTPointer<T> const& element: _list) for (T const& element: _list)
element->accept(_visitor); element->accept(_visitor);
} }
/// @returns a copy of the vector containing only the nodes which derive from T.
template <class _T>
static std::vector<_T const*> filteredNodes(std::vector<ASTPointer<ASTNode>> const& _nodes);
/// Returns the source code location of this node. /// Returns the source code location of this node.
SourceLocation const& location() const { return m_location; } SourceLocation const& location() const { return m_location; }
@ -95,6 +99,16 @@ private:
SourceLocation m_location; SourceLocation m_location;
}; };
template <class _T>
std::vector<_T const*> ASTNode::filteredNodes(std::vector<ASTPointer<ASTNode>> const& _nodes)
{
std::vector<_T const*> ret;
for (auto const& n: _nodes)
if (auto const* nt = dynamic_cast<_T const*>(n.get()))
ret.push_back(nt);
return ret;
}
/** /**
* Source unit containing import directives and contract definitions. * Source unit containing import directives and contract definitions.
*/ */
@ -168,7 +182,7 @@ public:
/// The current contract has to be given since this context can change the type, especially of /// The current contract has to be given since this context can change the type, especially of
/// contract types. /// contract types.
/// This can only be called once types of variable declarations have already been resolved. /// This can only be called once types of variable declarations have already been resolved.
virtual TypePointer type(ContractDefinition const* m_currentContract = nullptr) const = 0; virtual TypePointer type() const = 0;
protected: protected:
virtual Visibility defaultVisibility() const { return Visibility::Public; } virtual Visibility defaultVisibility() const { return Visibility::Public; }
@ -238,23 +252,13 @@ public:
ASTPointer<ASTString> const& _name, ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation, ASTPointer<ASTString> const& _documentation,
std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts, std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts,
std::vector<ASTPointer<StructDefinition>> const& _definedStructs, std::vector<ASTPointer<ASTNode>> const& _subNodes,
std::vector<ASTPointer<EnumDefinition>> const& _definedEnums,
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions,
std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers,
std::vector<ASTPointer<EventDefinition>> const& _events,
bool _isLibrary bool _isLibrary
): ):
Declaration(_location, _name), Declaration(_location, _name),
Documented(_documentation), Documented(_documentation),
m_baseContracts(_baseContracts), m_baseContracts(_baseContracts),
m_definedStructs(_definedStructs), m_subNodes(_subNodes),
m_definedEnums(_definedEnums),
m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions),
m_functionModifiers(_functionModifiers),
m_events(_events),
m_isLibrary(_isLibrary) m_isLibrary(_isLibrary)
{} {}
@ -262,13 +266,14 @@ public:
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<InheritanceSpecifier>> const& baseContracts() const { return m_baseContracts; } std::vector<ASTPointer<InheritanceSpecifier>> const& baseContracts() const { return m_baseContracts; }
std::vector<ASTPointer<StructDefinition>> const& definedStructs() const { return m_definedStructs; } std::vector<ASTPointer<ASTNode>> const& subNodes() const { return m_subNodes; }
std::vector<ASTPointer<EnumDefinition>> const& definedEnums() const { return m_definedEnums; } std::vector<StructDefinition const*> definedStructs() const { return filteredNodes<StructDefinition>(m_subNodes); }
std::vector<ASTPointer<VariableDeclaration>> const& stateVariables() const { return m_stateVariables; } std::vector<EnumDefinition const*> definedEnums() const { return filteredNodes<EnumDefinition>(m_subNodes); }
std::vector<ASTPointer<ModifierDefinition>> const& functionModifiers() const { return m_functionModifiers; } std::vector<VariableDeclaration const*> stateVariables() const { return filteredNodes<VariableDeclaration>(m_subNodes); }
std::vector<ASTPointer<FunctionDefinition>> const& definedFunctions() const { return m_definedFunctions; } std::vector<ModifierDefinition const*> functionModifiers() const { return filteredNodes<ModifierDefinition>(m_subNodes); }
std::vector<ASTPointer<EventDefinition>> const& events() const { return m_events; } std::vector<FunctionDefinition const*> definedFunctions() const { return filteredNodes<FunctionDefinition>(m_subNodes); }
std::vector<ASTPointer<EventDefinition>> const& interfaceEvents() const; std::vector<EventDefinition const*> events() const { return filteredNodes<EventDefinition>(m_subNodes); }
std::vector<EventDefinition const*> const& interfaceEvents() const;
bool isLibrary() const { return m_isLibrary; } bool isLibrary() const { return m_isLibrary; }
/// @returns a map of canonical function signatures to FunctionDefinitions /// @returns a map of canonical function signatures to FunctionDefinitions
@ -290,18 +295,13 @@ public:
std::string const& devDocumentation() const; std::string const& devDocumentation() const;
void setDevDocumentation(std::string const& _devDocumentation); void setDevDocumentation(std::string const& _devDocumentation);
virtual TypePointer type(ContractDefinition const* m_currentContract) const override; virtual TypePointer type() const override;
virtual ContractDefinitionAnnotation& annotation() const override; virtual ContractDefinitionAnnotation& annotation() const override;
private: private:
std::vector<ASTPointer<InheritanceSpecifier>> m_baseContracts; std::vector<ASTPointer<InheritanceSpecifier>> m_baseContracts;
std::vector<ASTPointer<StructDefinition>> m_definedStructs; std::vector<ASTPointer<ASTNode>> m_subNodes;
std::vector<ASTPointer<EnumDefinition>> m_definedEnums;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers;
std::vector<ASTPointer<EventDefinition>> m_events;
bool m_isLibrary; bool m_isLibrary;
// parsed Natspec documentation of the contract. // parsed Natspec documentation of the contract.
@ -310,7 +310,7 @@ private:
std::vector<ContractDefinition const*> m_linearizedBaseContracts; std::vector<ContractDefinition const*> m_linearizedBaseContracts;
mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList; mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList;
mutable std::unique_ptr<std::vector<ASTPointer<EventDefinition>>> m_interfaceEvents; mutable std::unique_ptr<std::vector<EventDefinition const*>> m_interfaceEvents;
mutable std::unique_ptr<std::vector<Declaration const*>> m_inheritableMembers; mutable std::unique_ptr<std::vector<Declaration const*>> m_inheritableMembers;
}; };
@ -350,7 +350,7 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& members() const { return m_members; } std::vector<ASTPointer<VariableDeclaration>> const& members() const { return m_members; }
virtual TypePointer type(ContractDefinition const* m_currentContract) const override; virtual TypePointer type() const override;
virtual TypeDeclarationAnnotation& annotation() const override; virtual TypeDeclarationAnnotation& annotation() const override;
@ -372,7 +372,7 @@ public:
std::vector<ASTPointer<EnumValue>> const& members() const { return m_members; } std::vector<ASTPointer<EnumValue>> const& members() const { return m_members; }
virtual TypePointer type(ContractDefinition const* m_currentContract) const override; virtual TypePointer type() const override;
virtual TypeDeclarationAnnotation& annotation() const override; virtual TypeDeclarationAnnotation& annotation() const override;
@ -392,7 +392,7 @@ public:
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
virtual TypePointer type(ContractDefinition const* m_currentContract) const override; virtual TypePointer type() const override;
}; };
/** /**
@ -490,7 +490,7 @@ public:
/// arguments separated by commas all enclosed in parentheses without any spaces. /// arguments separated by commas all enclosed in parentheses without any spaces.
std::string externalSignature() const; std::string externalSignature() const;
virtual TypePointer type(ContractDefinition const* m_currentContract) const override; virtual TypePointer type() const override;
virtual FunctionDefinitionAnnotation& annotation() const override; virtual FunctionDefinitionAnnotation& annotation() const override;
@ -551,7 +551,7 @@ public:
bool isConstant() const { return m_isConstant; } bool isConstant() const { return m_isConstant; }
Location referenceLocation() const { return m_location; } Location referenceLocation() const { return m_location; }
virtual TypePointer type(ContractDefinition const* m_currentContract) const override; virtual TypePointer type() const override;
virtual VariableDeclarationAnnotation& annotation() const override; virtual VariableDeclarationAnnotation& annotation() const override;
@ -593,7 +593,7 @@ public:
Block const& body() const { return *m_body; } Block const& body() const { return *m_body; }
virtual TypePointer type(ContractDefinition const* m_currentContract) const override; virtual TypePointer type() const override;
virtual ModifierDefinitionAnnotation& annotation() const override; virtual ModifierDefinitionAnnotation& annotation() const override;
@ -649,7 +649,7 @@ public:
bool isAnonymous() const { return m_anonymous; } bool isAnonymous() const { return m_anonymous; }
virtual TypePointer type(ContractDefinition const* m_currentContract) const override; virtual TypePointer type() const override;
virtual EventDefinitionAnnotation& annotation() const override; virtual EventDefinitionAnnotation& annotation() const override;
@ -675,7 +675,7 @@ public:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("MagicVariableDeclaration used inside real AST.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("MagicVariableDeclaration used inside real AST."));
} }
virtual TypePointer type(ContractDefinition const*) const override { return m_type; } virtual TypePointer type() const override { return m_type; }
private: private:
std::shared_ptr<Type const> m_type; std::shared_ptr<Type const> m_type;

View File

@ -138,9 +138,6 @@ struct ExpressionAnnotation: ASTAnnotation
struct IdentifierAnnotation: ExpressionAnnotation struct IdentifierAnnotation: ExpressionAnnotation
{ {
/// Stores a reference to the current contract.
/// This is needed because types of base contracts change depending on the context.
ContractDefinition const* contractScope = nullptr;
/// Referenced declaration, set at latest during overload resolution stage. /// Referenced declaration, set at latest during overload resolution stage.
Declaration const* referencedDeclaration = nullptr; Declaration const* referencedDeclaration = nullptr;
/// List of possible declarations it could refer to. /// List of possible declarations it could refer to.

View File

@ -62,12 +62,7 @@ void ContractDefinition::accept(ASTVisitor& _visitor)
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{ {
listAccept(m_baseContracts, _visitor); listAccept(m_baseContracts, _visitor);
listAccept(m_definedStructs, _visitor); listAccept(m_subNodes, _visitor);
listAccept(m_definedEnums, _visitor);
listAccept(m_stateVariables, _visitor);
listAccept(m_events, _visitor);
listAccept(m_functionModifiers, _visitor);
listAccept(m_definedFunctions, _visitor);
} }
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
@ -77,12 +72,7 @@ void ContractDefinition::accept(ASTConstVisitor& _visitor) const
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{ {
listAccept(m_baseContracts, _visitor); listAccept(m_baseContracts, _visitor);
listAccept(m_definedStructs, _visitor); listAccept(m_subNodes, _visitor);
listAccept(m_definedEnums, _visitor);
listAccept(m_stateVariables, _visitor);
listAccept(m_events, _visitor);
listAccept(m_functionModifiers, _visitor);
listAccept(m_definedFunctions, _visitor);
} }
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }

View File

@ -858,7 +858,7 @@ string ArrayType::canonicalName(bool _addDataLocation) const
return ret; return ret;
} }
MemberList const& ArrayType::members() const MemberList const& ArrayType::members(ContractDefinition const*) const
{ {
if (!m_members) if (!m_members)
{ {
@ -956,7 +956,7 @@ string ContractType::canonicalName(bool) const
return m_contract.annotation().canonicalName; return m_contract.annotation().canonicalName;
} }
MemberList const& ContractType::members() const MemberList const& ContractType::members(ContractDefinition const*) const
{ {
// We need to lazy-initialize it because of recursive references. // We need to lazy-initialize it because of recursive references.
if (!m_members) if (!m_members)
@ -970,7 +970,7 @@ MemberList const& ContractType::members() const
{ {
// add the most derived of all functions which are visible in derived contracts // add the most derived of all functions which are visible in derived contracts
for (ContractDefinition const* base: m_contract.annotation().linearizedBaseContracts) for (ContractDefinition const* base: m_contract.annotation().linearizedBaseContracts)
for (ASTPointer<FunctionDefinition> const& function: base->definedFunctions()) for (FunctionDefinition const* function: base->definedFunctions())
{ {
if (!function->isVisibleInDerivedContracts()) if (!function->isVisibleInDerivedContracts())
continue; continue;
@ -991,7 +991,7 @@ MemberList const& ContractType::members() const
members.push_back(MemberList::Member( members.push_back(MemberList::Member(
function->name(), function->name(),
functionType, functionType,
function.get() function
)); ));
} }
} }
@ -1024,9 +1024,9 @@ vector<tuple<VariableDeclaration const*, u256, unsigned>> ContractType::stateVar
{ {
vector<VariableDeclaration const*> variables; vector<VariableDeclaration const*> variables;
for (ContractDefinition const* contract: boost::adaptors::reverse(m_contract.annotation().linearizedBaseContracts)) for (ContractDefinition const* contract: boost::adaptors::reverse(m_contract.annotation().linearizedBaseContracts))
for (ASTPointer<VariableDeclaration> const& variable: contract->stateVariables()) for (VariableDeclaration const* variable: contract->stateVariables())
if (!variable->isConstant()) if (!variable->isConstant())
variables.push_back(variable.get()); variables.push_back(variable);
TypePointers types; TypePointers types;
for (auto variable: variables) for (auto variable: variables)
types.push_back(variable->annotation().type); types.push_back(variable->annotation().type);
@ -1064,7 +1064,7 @@ bool StructType::operator==(Type const& _other) const
unsigned StructType::calldataEncodedSize(bool _padded) const unsigned StructType::calldataEncodedSize(bool _padded) const
{ {
unsigned size = 0; unsigned size = 0;
for (auto const& member: members()) for (auto const& member: members(nullptr))
if (!member.type->canLiveOutsideStorage()) if (!member.type->canLiveOutsideStorage())
return 0; return 0;
else else
@ -1080,7 +1080,7 @@ unsigned StructType::calldataEncodedSize(bool _padded) const
u256 StructType::memorySize() const u256 StructType::memorySize() const
{ {
u256 size; u256 size;
for (auto const& member: members()) for (auto const& member: members(nullptr))
if (member.type->canLiveOutsideStorage()) if (member.type->canLiveOutsideStorage())
size += member.type->memoryHeadSize(); size += member.type->memoryHeadSize();
return size; return size;
@ -1088,7 +1088,7 @@ u256 StructType::memorySize() const
u256 StructType::storageSize() const u256 StructType::storageSize() const
{ {
return max<u256>(1, members().storageSize()); return max<u256>(1, members(nullptr).storageSize());
} }
string StructType::toString(bool _short) const string StructType::toString(bool _short) const
@ -1099,7 +1099,7 @@ string StructType::toString(bool _short) const
return ret; return ret;
} }
MemberList const& StructType::members() const MemberList const& StructType::members(ContractDefinition const*) const
{ {
// We need to lazy-initialize it because of recursive references. // We need to lazy-initialize it because of recursive references.
if (!m_members) if (!m_members)
@ -1149,7 +1149,7 @@ FunctionTypePointer StructType::constructorType() const
{ {
TypePointers paramTypes; TypePointers paramTypes;
strings paramNames; strings paramNames;
for (auto const& member: members()) for (auto const& member: members(nullptr))
{ {
if (!member.type->canLiveOutsideStorage()) if (!member.type->canLiveOutsideStorage())
continue; continue;
@ -1167,7 +1167,7 @@ FunctionTypePointer StructType::constructorType() const
pair<u256, unsigned> const& StructType::storageOffsetsOfMember(string const& _name) const pair<u256, unsigned> const& StructType::storageOffsetsOfMember(string const& _name) const
{ {
auto const* offsets = members().memberStorageOffset(_name); auto const* offsets = members(nullptr).memberStorageOffset(_name);
solAssert(offsets, "Storage offset of non-existing member requested."); solAssert(offsets, "Storage offset of non-existing member requested.");
return *offsets; return *offsets;
} }
@ -1175,7 +1175,7 @@ pair<u256, unsigned> const& StructType::storageOffsetsOfMember(string const& _na
u256 StructType::memoryOffsetOfMember(string const& _name) const u256 StructType::memoryOffsetOfMember(string const& _name) const
{ {
u256 offset; u256 offset;
for (auto const& member: members()) for (auto const& member: members(nullptr))
if (member.name == _name) if (member.name == _name)
return offset; return offset;
else else
@ -1395,7 +1395,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
vector<string> retParamNames; vector<string> retParamNames;
if (auto structType = dynamic_cast<StructType const*>(returnType.get())) if (auto structType = dynamic_cast<StructType const*>(returnType.get()))
{ {
for (auto const& member: structType->members()) for (auto const& member: structType->members(nullptr))
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()))
@ -1533,7 +1533,7 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const
return make_shared<FunctionType>(paramTypes, retParamTypes, m_parameterNames, m_returnParameterNames, m_location, m_arbitraryParameters); return make_shared<FunctionType>(paramTypes, retParamTypes, m_parameterNames, m_returnParameterNames, m_location, m_arbitraryParameters);
} }
MemberList const& FunctionType::members() const MemberList const& FunctionType::members(ContractDefinition const*) const
{ {
switch (m_location) switch (m_location)
{ {
@ -1784,10 +1784,10 @@ unsigned TypeType::sizeOnStack() const
return 0; return 0;
} }
MemberList const& TypeType::members() const MemberList const& TypeType::members(ContractDefinition const* _currentScope) const
{ {
// We need to lazy-initialize it because of recursive references. // We need to lazy-initialize it because of recursive references.
if (!m_members) if (!m_members || m_cachedScope != _currentScope)
{ {
MemberList::MemberMap members; MemberList::MemberMap members;
if (m_actualType->category() == Category::Contract) if (m_actualType->category() == Category::Contract)
@ -1800,9 +1800,9 @@ MemberList const& TypeType::members() const
it.second->asMemberFunction(true), // use callcode it.second->asMemberFunction(true), // use callcode
&it.second->declaration() &it.second->declaration()
)); ));
else if (m_currentContract != nullptr) else if (_currentScope != nullptr)
{ {
auto const& currentBases = m_currentContract->annotation().linearizedBaseContracts; auto const& currentBases = _currentScope->annotation().linearizedBaseContracts;
if (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end()) if (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end())
// We are accessing the type of a base contract, so add all public and protected // We are accessing the type of a base contract, so add all public and protected
// members. Note that this does not add inherited functions on purpose. // members. Note that this does not add inherited functions on purpose.
@ -1818,6 +1818,7 @@ MemberList const& TypeType::members() const
members.push_back(MemberList::Member(enumValue->name(), enumType)); members.push_back(MemberList::Member(enumValue->name(), enumType));
} }
m_members.reset(new MemberList(members)); m_members.reset(new MemberList(members));
m_cachedScope = _currentScope;
} }
return *m_members; return *m_members;
} }

View File

@ -217,9 +217,13 @@ public:
} }
/// Returns the list of all members of this type. Default implementation: no members. /// Returns the list of all members of this type. Default implementation: no members.
virtual MemberList const& members() const { return EmptyMemberList; } /// @param _currentScope scope in which the members are accessed.
virtual MemberList const& members(ContractDefinition const* /*_currentScope*/) const { return EmptyMemberList; }
/// Convenience method, returns the type of the given named member or an empty pointer if no such member exists. /// Convenience method, returns the type of the given named member or an empty pointer if no such member exists.
TypePointer memberType(std::string const& _name) const { return members().memberType(_name); } TypePointer memberType(std::string const& _name, ContractDefinition const* _currentScope = nullptr) const
{
return members(_currentScope).memberType(_name);
}
virtual std::string toString(bool _short) const = 0; virtual std::string toString(bool _short) const = 0;
std::string toString() const { return toString(false); } std::string toString() const { return toString(false); }
@ -277,7 +281,10 @@ public:
virtual unsigned storageBytes() const override { return m_bits / 8; } virtual unsigned storageBytes() const override { return m_bits / 8; }
virtual bool isValueType() const override { return true; } virtual bool isValueType() const override { return true; }
virtual MemberList const& members() const override { return isAddress() ? AddressMemberList : EmptyMemberList; } virtual MemberList const& members(ContractDefinition const* /*_currentScope*/) const override
{
return isAddress() ? AddressMemberList : EmptyMemberList;
}
virtual std::string toString(bool _short) const override; virtual std::string toString(bool _short) const override;
@ -510,7 +517,7 @@ public:
virtual unsigned sizeOnStack() const override; virtual unsigned sizeOnStack() const override;
virtual std::string toString(bool _short) const override; virtual std::string toString(bool _short) const override;
virtual std::string canonicalName(bool _addDataLocation) const override; virtual std::string canonicalName(bool _addDataLocation) const override;
virtual MemberList const& members() const override; virtual MemberList const& members(ContractDefinition const* _currentScope) const override;
virtual TypePointer encodingType() const override; virtual TypePointer encodingType() const override;
virtual TypePointer decodingType() const override; virtual TypePointer decodingType() const override;
virtual TypePointer interfaceType(bool _inLibrary) const override; virtual TypePointer interfaceType(bool _inLibrary) const override;
@ -563,7 +570,7 @@ public:
virtual std::string toString(bool _short) const override; virtual std::string toString(bool _short) const override;
virtual std::string canonicalName(bool _addDataLocation) const override; virtual std::string canonicalName(bool _addDataLocation) const override;
virtual MemberList const& members() const override; virtual MemberList const& members(ContractDefinition const* _currentScope) const override;
virtual TypePointer encodingType() const override virtual TypePointer encodingType() const override
{ {
return std::make_shared<IntegerType>(160, IntegerType::Modifier::Address); return std::make_shared<IntegerType>(160, IntegerType::Modifier::Address);
@ -616,7 +623,7 @@ public:
virtual bool canLiveOutsideStorage() const override { return true; } virtual bool canLiveOutsideStorage() const override { return true; }
virtual std::string toString(bool _short) const override; virtual std::string toString(bool _short) const override;
virtual MemberList const& members() const override; virtual MemberList const& members(ContractDefinition const* _currentScope) const override;
virtual TypePointer encodingType() const override virtual TypePointer encodingType() const override
{ {
return location() == DataLocation::Storage ? std::make_shared<IntegerType>(256) : TypePointer(); return location() == DataLocation::Storage ? std::make_shared<IntegerType>(256) : TypePointer();
@ -810,7 +817,7 @@ public:
virtual u256 storageSize() const override; virtual u256 storageSize() const override;
virtual bool canLiveOutsideStorage() const override { return false; } virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned sizeOnStack() const override; virtual unsigned sizeOnStack() const override;
virtual MemberList const& members() const override; virtual MemberList const& members(ContractDefinition const* _currentScope) const override;
/// @returns TypePointer of a new FunctionType object. All input/return parameters are an /// @returns TypePointer of a new FunctionType object. All input/return parameters are an
/// appropriate external types (i.e. the interfaceType()s) of input/return parameters of /// appropriate external types (i.e. the interfaceType()s) of input/return parameters of
@ -918,8 +925,7 @@ class TypeType: public Type
{ {
public: public:
virtual Category category() const override { return Category::TypeType; } virtual Category category() const override { return Category::TypeType; }
explicit TypeType(TypePointer const& _actualType, ContractDefinition const* _currentContract = nullptr): explicit TypeType(TypePointer const& _actualType): m_actualType(_actualType) {}
m_actualType(_actualType), m_currentContract(_currentContract) {}
TypePointer const& actualType() const { return m_actualType; } TypePointer const& actualType() const { return m_actualType; }
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
@ -929,14 +935,13 @@ public:
virtual bool canLiveOutsideStorage() const override { return false; } virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned sizeOnStack() const override; virtual unsigned sizeOnStack() const override;
virtual std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; } virtual std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
virtual MemberList const& members() const override; virtual MemberList const& members(ContractDefinition const* _currentScope) const override;
private: private:
TypePointer m_actualType; TypePointer m_actualType;
/// Context in which this type is used (influences visibility etc.), can be nullptr.
ContractDefinition const* m_currentContract;
/// List of member types, will be lazy-initialized because of recursive references. /// List of member types, will be lazy-initialized because of recursive references.
mutable std::unique_ptr<MemberList> m_members; mutable std::unique_ptr<MemberList> m_members;
mutable ContractDefinition const* m_cachedScope = nullptr;
}; };
@ -983,7 +988,7 @@ public:
virtual bool canBeStored() const override { return false; } virtual bool canBeStored() const override { return false; }
virtual bool canLiveOutsideStorage() const override { return true; } virtual bool canLiveOutsideStorage() const override { return true; }
virtual unsigned sizeOnStack() const override { return 0; } virtual unsigned sizeOnStack() const override { return 0; }
virtual MemberList const& members() const override { return m_members; } virtual MemberList const& members(ContractDefinition const*) const override { return m_members; }
virtual std::string toString(bool _short) const override; virtual std::string toString(bool _short) const override;

View File

@ -385,7 +385,7 @@ void Compiler::registerStateVariables(ContractDefinition const& _contract)
void Compiler::initializeStateVariables(ContractDefinition const& _contract) void Compiler::initializeStateVariables(ContractDefinition const& _contract)
{ {
for (ASTPointer<VariableDeclaration> const& variable: _contract.stateVariables()) for (VariableDeclaration const* variable: _contract.stateVariables())
if (variable->value() && !variable->isConstant()) if (variable->value() && !variable->isConstant())
ExpressionCompiler(m_context, m_optimize).appendStateVariableInitialization(*variable); ExpressionCompiler(m_context, m_optimize).appendStateVariableInitialization(*variable);
} }

View File

@ -133,9 +133,9 @@ ModifierDefinition const& CompilerContext::functionModifier(string const& _name)
{ {
solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
for (ContractDefinition const* contract: m_inheritanceHierarchy) for (ContractDefinition const* contract: m_inheritanceHierarchy)
for (ASTPointer<ModifierDefinition> const& modifier: contract->functionModifiers()) for (ModifierDefinition const* modifier: contract->functionModifiers())
if (modifier->name() == _name) if (modifier->name() == _name)
return *modifier.get(); return *modifier;
BOOST_THROW_EXCEPTION(InternalCompilerError() BOOST_THROW_EXCEPTION(InternalCompilerError()
<< errinfo_comment("Function modifier " + _name + " not found.")); << errinfo_comment("Function modifier " + _name + " not found."));
} }
@ -195,7 +195,7 @@ eth::AssemblyItem CompilerContext::virtualFunctionEntryLabel(
FunctionType functionType(_function); FunctionType functionType(_function);
auto it = _searchStart; auto it = _searchStart;
for (; it != m_inheritanceHierarchy.end(); ++it) for (; it != m_inheritanceHierarchy.end(); ++it)
for (ASTPointer<FunctionDefinition> const& function: (*it)->definedFunctions()) for (FunctionDefinition const* function: (*it)->definedFunctions())
if ( if (
function->name() == name && function->name() == name &&
!function->isConstructor() && !function->isConstructor() &&

View File

@ -542,7 +542,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
allocateMemory(); allocateMemory();
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2; m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2;
// stack: <memory ptr> <source ref> <memory ptr> // stack: <memory ptr> <source ref> <memory ptr>
for (auto const& member: typeOnStack.members()) for (auto const& member: typeOnStack.members(nullptr))
{ {
if (!member.type->canLiveOutsideStorage()) if (!member.type->canLiveOutsideStorage())
continue; continue;
@ -642,7 +642,7 @@ void CompilerUtils::pushZeroValue(Type const& _type)
m_context << eth::Instruction::DUP1; m_context << eth::Instruction::DUP1;
if (auto structType = dynamic_cast<StructType const*>(&_type)) if (auto structType = dynamic_cast<StructType const*>(&_type))
for (auto const& member: structType->members()) for (auto const& member: structType->members(nullptr))
{ {
pushZeroValue(*member.type); pushZeroValue(*member.type);
storeInMemoryDynamic(*member.type); storeInMemoryDynamic(*member.type);

View File

@ -888,10 +888,6 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
case Type::Category::TypeType: case Type::Category::TypeType:
{ {
TypeType const& type = dynamic_cast<TypeType const&>(*_memberAccess.expression().annotation().type); TypeType const& type = dynamic_cast<TypeType const&>(*_memberAccess.expression().annotation().type);
solAssert(
!type.members().membersByName(_memberAccess.memberName()).empty(),
"Invalid member access to " + type.toString(false)
);
if (dynamic_cast<ContractType const*>(type.actualType().get())) if (dynamic_cast<ContractType const*>(type.actualType().get()))
{ {
@ -1043,11 +1039,11 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
Declaration const* declaration = _identifier.annotation().referencedDeclaration; Declaration const* declaration = _identifier.annotation().referencedDeclaration;
if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration)) if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration))
{ {
switch (magicVar->type(_identifier.annotation().contractScope)->category()) switch (magicVar->type()->category())
{ {
case Type::Category::Contract: case Type::Category::Contract:
// "this" or "super" // "this" or "super"
if (!dynamic_cast<ContractType const&>(*magicVar->type(_identifier.annotation().contractScope)).isSuper()) if (!dynamic_cast<ContractType const&>(*magicVar->type()).isSuper())
m_context << eth::Instruction::ADDRESS; m_context << eth::Instruction::ADDRESS;
break; break;
case Type::Category::Integer: case Type::Category::Integer:

View File

@ -273,7 +273,7 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
"Struct assignment with conversion." "Struct assignment with conversion."
); );
solAssert(sourceType.location() != DataLocation::CallData, "Structs in calldata not supported."); solAssert(sourceType.location() != DataLocation::CallData, "Structs in calldata not supported.");
for (auto const& member: structType.members()) for (auto const& member: structType.members(nullptr))
{ {
// assign each member that is not a mapping // assign each member that is not a mapping
TypePointer const& memberType = member.type; TypePointer const& memberType = member.type;
@ -336,7 +336,7 @@ void StorageItem::setToZero(SourceLocation const&, bool _removeReference) const
// @todo this can be improved: use StorageItem for non-value types, and just store 0 in // @todo this can be improved: use StorageItem for non-value types, and just store 0 in
// all slots that contain value types later. // all slots that contain value types later.
auto const& structType = dynamic_cast<StructType const&>(*m_dataType); auto const& structType = dynamic_cast<StructType const&>(*m_dataType);
for (auto const& member: structType.members()) for (auto const& member: structType.members(nullptr))
{ {
// zero each member that is not a mapping // zero each member that is not a mapping
TypePointer const& memberType = member.type; TypePointer const& memberType = member.type;

View File

@ -145,8 +145,8 @@ bool Why3Translator::visit(ContractDefinition const& _contract)
addLine("type state = {"); addLine("type state = {");
indent(); indent();
m_stateVariables = &_contract.stateVariables(); m_stateVariables = _contract.stateVariables();
for (auto const& variable: _contract.stateVariables()) for (VariableDeclaration const* variable: m_stateVariables)
{ {
string varType = toFormalType(*variable->annotation().type); string varType = toFormalType(*variable->annotation().type);
if (varType.empty()) if (varType.empty())
@ -174,7 +174,7 @@ bool Why3Translator::visit(ContractDefinition const& _contract)
void Why3Translator::endVisit(ContractDefinition const& _contract) void Why3Translator::endVisit(ContractDefinition const& _contract)
{ {
m_stateVariables = nullptr; m_stateVariables.clear();
addSourceFromDocStrings(_contract.annotation()); addSourceFromDocStrings(_contract.annotation());
unindent(); unindent();
addLine("end"); addLine("end");
@ -600,17 +600,12 @@ bool Why3Translator::visit(Literal const& _literal)
bool Why3Translator::isStateVariable(VariableDeclaration const* _var) const bool Why3Translator::isStateVariable(VariableDeclaration const* _var) const
{ {
solAssert(!!m_stateVariables, ""); return contains(m_stateVariables, _var);
for (auto const& var: *m_stateVariables)
if (var.get() == _var)
return true;
return false;
} }
bool Why3Translator::isStateVariable(string const& _name) const bool Why3Translator::isStateVariable(string const& _name) const
{ {
solAssert(!!m_stateVariables, ""); for (auto const& var: m_stateVariables)
for (auto const& var: *m_stateVariables)
if (var->name() == _name) if (var->name() == _name)
return true; return true;
return false; return false;

View File

@ -117,7 +117,7 @@ private:
bool m_seenContract = false; bool m_seenContract = false;
bool m_errorOccured = false; bool m_errorOccured = false;
std::vector<ASTPointer<VariableDeclaration>> const* m_stateVariables = nullptr; std::vector<VariableDeclaration const*> m_stateVariables;
std::map<std::string, VariableDeclaration const*> m_localVariables; std::map<std::string, VariableDeclaration const*> m_localVariables;
struct Line struct Line

View File

@ -120,7 +120,7 @@ string InterfaceHandler::ABISolidityInterface(ContractDefinition const& _contrac
{ {
ret += "struct " + stru->name() + "{"; ret += "struct " + stru->name() + "{";
for (ASTPointer<VariableDeclaration> const& _member: stru->members()) for (ASTPointer<VariableDeclaration> const& _member: stru->members())
ret += _member->type(nullptr)->canonicalName(false) + " " + _member->name() + ";"; ret += _member->type()->canonicalName(false) + " " + _member->name() + ";";
ret += "}"; ret += "}";
} }
for (auto const& enu: _contractDef.definedEnums()) for (auto const& enu: _contractDef.definedEnums())

View File

@ -132,12 +132,6 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary)
expectToken(_isLibrary ? Token::Library : Token::Contract); expectToken(_isLibrary ? Token::Library : Token::Contract);
ASTPointer<ASTString> name = expectIdentifierToken(); ASTPointer<ASTString> name = expectIdentifierToken();
vector<ASTPointer<InheritanceSpecifier>> baseContracts; vector<ASTPointer<InheritanceSpecifier>> baseContracts;
vector<ASTPointer<StructDefinition>> structs;
vector<ASTPointer<EnumDefinition>> enums;
vector<ASTPointer<VariableDeclaration>> stateVariables;
vector<ASTPointer<FunctionDefinition>> functions;
vector<ASTPointer<ModifierDefinition>> modifiers;
vector<ASTPointer<EventDefinition>> events;
if (m_scanner->currentToken() == Token::Is) if (m_scanner->currentToken() == Token::Is)
do do
{ {
@ -145,6 +139,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary)
baseContracts.push_back(parseInheritanceSpecifier()); baseContracts.push_back(parseInheritanceSpecifier());
} }
while (m_scanner->currentToken() == Token::Comma); while (m_scanner->currentToken() == Token::Comma);
vector<ASTPointer<ASTNode>> subNodes;
expectToken(Token::LBrace); expectToken(Token::LBrace);
while (true) while (true)
{ {
@ -152,11 +147,11 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary)
if (currentTokenValue == Token::RBrace) if (currentTokenValue == Token::RBrace)
break; break;
else if (currentTokenValue == Token::Function) else if (currentTokenValue == Token::Function)
functions.push_back(parseFunctionDefinition(name.get())); subNodes.push_back(parseFunctionDefinition(name.get()));
else if (currentTokenValue == Token::Struct) else if (currentTokenValue == Token::Struct)
structs.push_back(parseStructDefinition()); subNodes.push_back(parseStructDefinition());
else if (currentTokenValue == Token::Enum) else if (currentTokenValue == Token::Enum)
enums.push_back(parseEnumDefinition()); subNodes.push_back(parseEnumDefinition());
else if ( else if (
currentTokenValue == Token::Identifier || currentTokenValue == Token::Identifier ||
currentTokenValue == Token::Mapping || currentTokenValue == Token::Mapping ||
@ -166,13 +161,13 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary)
VarDeclParserOptions options; VarDeclParserOptions options;
options.isStateVariable = true; options.isStateVariable = true;
options.allowInitialValue = true; options.allowInitialValue = true;
stateVariables.push_back(parseVariableDeclaration(options)); subNodes.push_back(parseVariableDeclaration(options));
expectToken(Token::Semicolon); expectToken(Token::Semicolon);
} }
else if (currentTokenValue == Token::Modifier) else if (currentTokenValue == Token::Modifier)
modifiers.push_back(parseModifierDefinition()); subNodes.push_back(parseModifierDefinition());
else if (currentTokenValue == Token::Event) else if (currentTokenValue == Token::Event)
events.push_back(parseEventDefinition()); subNodes.push_back(parseEventDefinition());
else else
fatalParserError(std::string("Function, variable, struct or modifier declaration expected.")); fatalParserError(std::string("Function, variable, struct or modifier declaration expected."));
} }
@ -182,12 +177,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary)
name, name,
docString, docString,
baseContracts, baseContracts,
structs, subNodes,
enums,
stateVariables,
functions,
modifiers,
events,
_isLibrary _isLibrary
); );
} }

View File

@ -73,7 +73,7 @@ bool successParse(std::string const& _source)
} }
void checkFunctionNatspec( void checkFunctionNatspec(
ASTPointer<FunctionDefinition> _function, FunctionDefinition const* _function,
std::string const& _expectedDoc std::string const& _expectedDoc
) )
{ {
@ -191,20 +191,18 @@ BOOST_AUTO_TEST_CASE(function_natspec_documentation)
" function functionName(bytes32 input) returns (bytes32 out) {}\n" " function functionName(bytes32 input) returns (bytes32 out) {}\n"
"}\n"; "}\n";
BOOST_CHECK(successParse(text)); BOOST_CHECK(successParse(text));
ErrorList e;
ASTPointer<ContractDefinition> contract = parseText(text, e);
ASTPointer<FunctionDefinition> function;
ErrorList errors; ErrorList errors;
auto functions = parseText(text, errors)->definedFunctions(); ASTPointer<ContractDefinition> contract = parseText(text, errors);
FunctionDefinition const* function = nullptr;
auto functions = contract->definedFunctions();
ETH_TEST_REQUIRE_NO_THROW(function = functions.at(0), "Failed to retrieve function"); ETH_TEST_REQUIRE_NO_THROW(function = functions.at(0), "Failed to retrieve function");
checkFunctionNatspec(function, "This is a test function"); checkFunctionNatspec(function, "This is a test function");
} }
BOOST_AUTO_TEST_CASE(function_normal_comments) BOOST_AUTO_TEST_CASE(function_normal_comments)
{ {
ASTPointer<ContractDefinition> contract; FunctionDefinition const* function = nullptr;
ASTPointer<FunctionDefinition> function;
char const* text = "contract test {\n" char const* text = "contract test {\n"
" uint256 stateVar;\n" " uint256 stateVar;\n"
" // We won't see this comment\n" " // We won't see this comment\n"
@ -212,7 +210,8 @@ BOOST_AUTO_TEST_CASE(function_normal_comments)
"}\n"; "}\n";
BOOST_CHECK(successParse(text)); BOOST_CHECK(successParse(text));
ErrorList errors; ErrorList errors;
auto functions = parseText(text, errors)->definedFunctions(); ASTPointer<ContractDefinition> contract = parseText(text, errors);
auto functions = contract->definedFunctions();
ETH_TEST_REQUIRE_NO_THROW(function = functions.at(0), "Failed to retrieve function"); ETH_TEST_REQUIRE_NO_THROW(function = functions.at(0), "Failed to retrieve function");
BOOST_CHECK_MESSAGE(function->documentation() == nullptr, BOOST_CHECK_MESSAGE(function->documentation() == nullptr,
"Should not have gotten a Natspecc comment for this function"); "Should not have gotten a Natspecc comment for this function");
@ -220,8 +219,7 @@ BOOST_AUTO_TEST_CASE(function_normal_comments)
BOOST_AUTO_TEST_CASE(multiple_functions_natspec_documentation) BOOST_AUTO_TEST_CASE(multiple_functions_natspec_documentation)
{ {
ASTPointer<ContractDefinition> contract; FunctionDefinition const* function = nullptr;
ASTPointer<FunctionDefinition> function;
char const* text = "contract test {\n" char const* text = "contract test {\n"
" uint256 stateVar;\n" " uint256 stateVar;\n"
" /// This is test function 1\n" " /// This is test function 1\n"
@ -235,7 +233,8 @@ BOOST_AUTO_TEST_CASE(multiple_functions_natspec_documentation)
"}\n"; "}\n";
BOOST_CHECK(successParse(text)); BOOST_CHECK(successParse(text));
ErrorList errors; ErrorList errors;
auto functions = parseText(text, errors)->definedFunctions(); ASTPointer<ContractDefinition> contract = parseText(text, errors);
auto functions = contract->definedFunctions();
ETH_TEST_REQUIRE_NO_THROW(function = functions.at(0), "Failed to retrieve function"); ETH_TEST_REQUIRE_NO_THROW(function = functions.at(0), "Failed to retrieve function");
checkFunctionNatspec(function, "This is test function 1"); checkFunctionNatspec(function, "This is test function 1");
@ -253,8 +252,7 @@ BOOST_AUTO_TEST_CASE(multiple_functions_natspec_documentation)
BOOST_AUTO_TEST_CASE(multiline_function_documentation) BOOST_AUTO_TEST_CASE(multiline_function_documentation)
{ {
ASTPointer<ContractDefinition> contract; FunctionDefinition const* function = nullptr;
ASTPointer<FunctionDefinition> function;
char const* text = "contract test {\n" char const* text = "contract test {\n"
" uint256 stateVar;\n" " uint256 stateVar;\n"
" /// This is a test function\n" " /// This is a test function\n"
@ -263,7 +261,8 @@ BOOST_AUTO_TEST_CASE(multiline_function_documentation)
"}\n"; "}\n";
BOOST_CHECK(successParse(text)); BOOST_CHECK(successParse(text));
ErrorList errors; ErrorList errors;
auto functions = parseText(text, errors)->definedFunctions(); ASTPointer<ContractDefinition> contract = parseText(text, errors);
auto functions = contract->definedFunctions();
ETH_TEST_REQUIRE_NO_THROW(function = functions.at(0), "Failed to retrieve function"); ETH_TEST_REQUIRE_NO_THROW(function = functions.at(0), "Failed to retrieve function");
checkFunctionNatspec(function, "This is a test function\n" checkFunctionNatspec(function, "This is a test function\n"
" and it has 2 lines"); " and it has 2 lines");
@ -271,8 +270,7 @@ BOOST_AUTO_TEST_CASE(multiline_function_documentation)
BOOST_AUTO_TEST_CASE(natspec_comment_in_function_body) BOOST_AUTO_TEST_CASE(natspec_comment_in_function_body)
{ {
ASTPointer<ContractDefinition> contract; FunctionDefinition const* function = nullptr;
ASTPointer<FunctionDefinition> function;
char const* text = "contract test {\n" char const* text = "contract test {\n"
" /// fun1 description\n" " /// fun1 description\n"
" function fun1(uint256 a) {\n" " function fun1(uint256 a) {\n"
@ -288,7 +286,8 @@ BOOST_AUTO_TEST_CASE(natspec_comment_in_function_body)
"}\n"; "}\n";
BOOST_CHECK(successParse(text)); BOOST_CHECK(successParse(text));
ErrorList errors; ErrorList errors;
auto functions = parseText(text, errors)->definedFunctions(); ASTPointer<ContractDefinition> contract = parseText(text, errors);
auto functions = contract->definedFunctions();
ETH_TEST_REQUIRE_NO_THROW(function = functions.at(0), "Failed to retrieve function"); ETH_TEST_REQUIRE_NO_THROW(function = functions.at(0), "Failed to retrieve function");
checkFunctionNatspec(function, "fun1 description"); checkFunctionNatspec(function, "fun1 description");
@ -300,8 +299,7 @@ BOOST_AUTO_TEST_CASE(natspec_comment_in_function_body)
BOOST_AUTO_TEST_CASE(natspec_docstring_between_keyword_and_signature) BOOST_AUTO_TEST_CASE(natspec_docstring_between_keyword_and_signature)
{ {
ASTPointer<ContractDefinition> contract; FunctionDefinition const* function = nullptr;
ASTPointer<FunctionDefinition> function;
char const* text = "contract test {\n" char const* text = "contract test {\n"
" uint256 stateVar;\n" " uint256 stateVar;\n"
" function ///I am in the wrong place \n" " function ///I am in the wrong place \n"
@ -315,7 +313,8 @@ BOOST_AUTO_TEST_CASE(natspec_docstring_between_keyword_and_signature)
"}\n"; "}\n";
BOOST_CHECK(successParse(text)); BOOST_CHECK(successParse(text));
ErrorList errors; ErrorList errors;
auto functions = parseText(text, errors)->definedFunctions(); ASTPointer<ContractDefinition> contract = parseText(text, errors);
auto functions = contract->definedFunctions();
ETH_TEST_REQUIRE_NO_THROW(function = functions.at(0), "Failed to retrieve function"); ETH_TEST_REQUIRE_NO_THROW(function = functions.at(0), "Failed to retrieve function");
BOOST_CHECK_MESSAGE(!function->documentation(), BOOST_CHECK_MESSAGE(!function->documentation(),
@ -324,8 +323,7 @@ BOOST_AUTO_TEST_CASE(natspec_docstring_between_keyword_and_signature)
BOOST_AUTO_TEST_CASE(natspec_docstring_after_signature) BOOST_AUTO_TEST_CASE(natspec_docstring_after_signature)
{ {
ASTPointer<ContractDefinition> contract; FunctionDefinition const* function = nullptr;
ASTPointer<FunctionDefinition> function;
char const* text = "contract test {\n" char const* text = "contract test {\n"
" uint256 stateVar;\n" " uint256 stateVar;\n"
" function fun1(uint256 a) {\n" " function fun1(uint256 a) {\n"
@ -339,7 +337,8 @@ BOOST_AUTO_TEST_CASE(natspec_docstring_after_signature)
"}\n"; "}\n";
BOOST_CHECK(successParse(text)); BOOST_CHECK(successParse(text));
ErrorList errors; ErrorList errors;
auto functions = parseText(text, errors)->definedFunctions(); ASTPointer<ContractDefinition> contract = parseText(text, errors);
auto functions = contract->definedFunctions();
ETH_TEST_REQUIRE_NO_THROW(function = functions.at(0), "Failed to retrieve function"); ETH_TEST_REQUIRE_NO_THROW(function = functions.at(0), "Failed to retrieve function");
BOOST_CHECK_MESSAGE(!function->documentation(), BOOST_CHECK_MESSAGE(!function->documentation(),