Parsing and type checking of libraries without inheritance.

This commit is contained in:
chriseth 2015-09-08 16:48:33 +02:00
parent c5b6d9d2a9
commit 337fde9d11
8 changed files with 85 additions and 13 deletions

View File

@ -103,6 +103,9 @@ void ContractDefinition::checkTypeRequirements()
));
hashes.insert(hash);
}
if (isLibrary())
checkLibraryRequirements();
}
map<FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions() const
@ -332,6 +335,17 @@ void ContractDefinition::checkExternalTypeClashes() const
));
}
void ContractDefinition::checkLibraryRequirements() const
{
solAssert(m_isLibrary, "");
if (!m_baseContracts.empty())
BOOST_THROW_EXCEPTION(createTypeError("Library is not allowed to inherit."));
for (auto const& var: m_stateVariables)
if (!var->isConstant())
BOOST_THROW_EXCEPTION(var->createTypeError("Library cannot have non-constant state variables"));
}
vector<ASTPointer<EventDefinition>> const& ContractDefinition::interfaceEvents() const
{
if (!m_interfaceEvents)
@ -449,6 +463,10 @@ void InheritanceSpecifier::checkTypeRequirements()
ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(&m_baseName->referencedDeclaration());
solAssert(base, "Base contract not available.");
if (base->isLibrary())
BOOST_THROW_EXCEPTION(createTypeError("Libraries cannot be inherited from."));
TypePointers parameterTypes = ContractType(*base).constructorType()->parameterTypes();
if (!m_arguments.empty() && parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError(

View File

@ -215,7 +215,7 @@ protected:
/// @}
/**
* Definition of a contract. This is the only AST nodes where child nodes are not visited in
* Definition of a contract or library. This is the only AST nodes where child nodes are not visited in
* document order. It first visits all struct declarations, then all variable declarations and
* finally all function declarations.
*/
@ -232,7 +232,8 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions,
std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers,
std::vector<ASTPointer<EventDefinition>> const& _events
std::vector<ASTPointer<EventDefinition>> const& _events,
bool _isLibrary
):
Declaration(_location, _name),
Documented(_documentation),
@ -243,7 +244,8 @@ public:
m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions),
m_functionModifiers(_functionModifiers),
m_events(_events)
m_events(_events),
m_isLibrary(_isLibrary)
{}
virtual void accept(ASTVisitor& _visitor) override;
@ -257,6 +259,7 @@ public:
std::vector<ASTPointer<FunctionDefinition>> const& definedFunctions() const { return m_definedFunctions; }
std::vector<ASTPointer<EventDefinition>> const& events() const { return m_events; }
std::vector<ASTPointer<EventDefinition>> const& interfaceEvents() const;
bool isLibrary() const { return m_isLibrary; }
virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
@ -297,6 +300,8 @@ private:
/// Checks that different functions with external visibility end up having different
/// external argument types (i.e. different signature).
void checkExternalTypeClashes() const;
/// Checks that all requirements for a library are fulfilled if this is a library.
void checkLibraryRequirements() const;
std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& interfaceFunctionList() const;
@ -307,6 +312,7 @@ private:
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers;
std::vector<ASTPointer<EventDefinition>> m_events;
bool m_isLibrary;
// parsed Natspec documentation of the contract.
std::string m_userDocumentation;

View File

@ -71,13 +71,14 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
vector<ASTPointer<ASTNode>> nodes;
while (m_scanner->currentToken() != Token::EOS)
{
switch (m_scanner->currentToken())
switch (auto token = m_scanner->currentToken())
{
case Token::Import:
nodes.push_back(parseImportDirective());
break;
case Token::Contract:
nodes.push_back(parseContractDefinition());
case Token::Library:
nodes.push_back(parseContractDefinition(token == Token::Library));
break;
default:
BOOST_THROW_EXCEPTION(createParserError(std::string("Expected import directive or contract definition.")));
@ -113,13 +114,13 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
return nodeFactory.createNode<ImportDirective>(url);
}
ASTPointer<ContractDefinition> Parser::parseContractDefinition()
ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary)
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docString;
if (m_scanner->currentCommentLiteral() != "")
docString = make_shared<ASTString>(m_scanner->currentCommentLiteral());
expectToken(Token::Contract);
expectToken(_isLibrary ? Token::Library : Token::Contract);
ASTPointer<ASTString> name = expectIdentifierToken();
vector<ASTPointer<InheritanceSpecifier>> baseContracts;
vector<ASTPointer<StructDefinition>> structs;
@ -177,7 +178,8 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
stateVariables,
functions,
modifiers,
events
events,
_isLibrary
);
}

View File

@ -61,15 +61,17 @@ private:
///@{
///@name Parsing functions for the AST nodes
ASTPointer<ImportDirective> parseImportDirective();
ASTPointer<ContractDefinition> parseContractDefinition();
ASTPointer<ContractDefinition> parseContractDefinition(bool _isLibrary);
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
Declaration::Visibility parseVisibilitySpecifier(Token::Value _token);
ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName);
ASTPointer<StructDefinition> parseStructDefinition();
ASTPointer<EnumDefinition> parseEnumDefinition();
ASTPointer<EnumValue> parseEnumValue();
ASTPointer<VariableDeclaration> parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions(),
ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>());
ASTPointer<VariableDeclaration> parseVariableDeclaration(
VarDeclParserOptions const& _options = VarDeclParserOptions(),
ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>()
);
ASTPointer<ModifierDefinition> parseModifierDefinition();
ASTPointer<EventDefinition> parseEventDefinition();
ASTPointer<ModifierInvocation> parseModifierInvocation();

View File

@ -160,6 +160,7 @@ namespace solidity
K(Internal, "internal", 0) \
K(Import, "import", 0) \
K(Is, "is", 0) \
K(Library, "library", 0) \
K(Mapping, "mapping", 0) \
K(Memory, "memory", 0) \
K(Modifier, "modifier", 0) \
@ -305,7 +306,7 @@ namespace solidity
/* Identifiers (not keywords or future reserved words). */ \
T(Identifier, NULL, 0) \
\
/* Keywords reserved for future. use. */ \
/* Keywords reserved for future use. */ \
K(As, "as", 0) \
K(Case, "case", 0) \
K(Catch, "catch", 0) \

View File

@ -1,4 +1,4 @@
ContractDefinition = 'contract' Identifier
ContractDefinition = ( 'contract' | 'library' ) Identifier
( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )?
'{' ContractPart* '}'
ContractPart = StateVariableDeclaration | StructDefinition | ModifierDefinition | FunctionDefinition | EnumDefinition

View File

@ -2194,6 +2194,39 @@ BOOST_AUTO_TEST_CASE(string_bytes_conversion)
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_CASE(inheriting_from_library)
{
char const* text = R"(
library Lib {}
contract Test is Lib {}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(inheriting_library)
{
char const* text = R"(
contract Test {}
library Lib is Test {}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(library_having_variables)
{
char const* text = R"(
library Lib { uint x; }
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(valid_library)
{
char const* text = R"(
library Lib { uint constant x = 9; }
)";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_CASE(creating_contract_within_the_contract)
{

View File

@ -924,6 +924,16 @@ BOOST_AUTO_TEST_CASE(empty_comment)
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_CASE(library_simple)
{
char const* text = R"(
library Lib {
function f() { }
}
)";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_SUITE_END()
}