mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Parsing and type checking of libraries without inheritance.
This commit is contained in:
parent
c5b6d9d2a9
commit
337fde9d11
@ -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(
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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) \
|
||||
|
@ -1,4 +1,4 @@
|
||||
ContractDefinition = 'contract' Identifier
|
||||
ContractDefinition = ( 'contract' | 'library' ) Identifier
|
||||
( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )?
|
||||
'{' ContractPart* '}'
|
||||
ContractPart = StateVariableDeclaration | StructDefinition | ModifierDefinition | FunctionDefinition | EnumDefinition
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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()
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user