diff options
Diffstat (limited to 'libsolidity')
-rw-r--r-- | libsolidity/analysis/ReferencesResolver.cpp | 15 | ||||
-rw-r--r-- | libsolidity/ast/AST.h | 18 | ||||
-rw-r--r-- | libsolidity/parsing/Parser.cpp | 30 | ||||
-rw-r--r-- | libsolidity/parsing/Token.h | 7 |
4 files changed, 59 insertions, 11 deletions
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 8750b47b..4b678c3b 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -112,7 +112,20 @@ bool ReferencesResolver::visit(Identifier const& _identifier) bool ReferencesResolver::visit(ElementaryTypeName const& _typeName) { - _typeName.annotation().type = Type::fromElementaryTypeName(_typeName.typeName()); + if (!_typeName.annotation().type) + { + _typeName.annotation().type = Type::fromElementaryTypeName(_typeName.typeName()); + if (_typeName.stateMutability().is_initialized()) + { + // for non-address types this was already caught by the parser + solAssert(_typeName.annotation().type->category() == Type::Category::Address, ""); + if (!( + *_typeName.stateMutability() == StateMutability::Payable || + *_typeName.stateMutability() == StateMutability::NonPayable + )) + m_errorReporter.typeError(_typeName.location(), "Address types can only be payable or non-payable."); + } + } return true; } diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index a5cd277d..f3464f92 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -876,23 +876,31 @@ public: }; /** - * Any pre-defined type name represented by a single keyword, i.e. it excludes mappings, - * contracts, functions, etc. + * Any pre-defined type name represented by a single keyword (and possibly a state mutability for address types), + * i.e. it excludes mappings, contracts, functions, etc. */ class ElementaryTypeName: public TypeName { public: - ElementaryTypeName(SourceLocation const& _location, ElementaryTypeNameToken const& _elem): - TypeName(_location), m_type(_elem) - {} + ElementaryTypeName( + SourceLocation const& _location, + ElementaryTypeNameToken const& _elem, + boost::optional<StateMutability> _stateMutability = {} + ): TypeName(_location), m_type(_elem), m_stateMutability(_stateMutability) + { + solAssert(!_stateMutability.is_initialized() || _elem.token() == Token::Address, ""); + } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; ElementaryTypeNameToken const& typeName() const { return m_type; } + boost::optional<StateMutability> const& stateMutability() const { return m_stateMutability; } + private: ElementaryTypeNameToken m_type; + boost::optional<StateMutability> m_stateMutability; ///< state mutability for address type }; /** diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 0c41e332..1228b833 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -57,7 +57,7 @@ public: solAssert(m_location.sourceName, ""); if (m_location.end < 0) markEndPosition(); - return make_shared<NodeType>(m_location, forward<Args>(_args)...); + return make_shared<NodeType>(m_location, std::forward<Args>(_args)...); } private: @@ -813,8 +813,24 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar) unsigned secondSize; tie(firstSize, secondSize) = m_scanner->currentTokenInfo(); ElementaryTypeNameToken elemTypeName(token, firstSize, secondSize); - type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(elemTypeName); + ASTNodeFactory nodeFactory(*this); + nodeFactory.markEndPosition(); m_scanner->next(); + auto stateMutability = boost::make_optional(elemTypeName.token() == Token::Address, StateMutability::NonPayable); + if (Token::isStateMutabilitySpecifier(m_scanner->currentToken(), false)) + { + if (elemTypeName.token() == Token::Address) + { + nodeFactory.markEndPosition(); + stateMutability = parseStateMutability(); + } + else + { + parserError("State mutability can only be specified for address types."); + m_scanner->next(); + } + } + type = nodeFactory.createNode<ElementaryTypeName>(elemTypeName, stateMutability); } else if (token == Token::Var) { @@ -1615,8 +1631,8 @@ Parser::LookAheadInfo Parser::peekStatementType() const // Distinguish between variable declaration (and potentially assignment) and expression statement // (which include assignments to other expressions and pre-declared variables). // We have a variable declaration if we get a keyword that specifies a type name. - // If it is an identifier or an elementary type name followed by an identifier, we also have - // a variable declaration. + // If it is an identifier or an elementary type name followed by an identifier + // or a mutability specifier, we also have a variable declaration. // If we get an identifier followed by a "[" or ".", it can be both ("lib.type[9] a;" or "variable.el[9] = 7;"). // In all other cases, we have an expression statement. Token::Value token(m_scanner->currentToken()); @@ -1627,6 +1643,12 @@ Parser::LookAheadInfo Parser::peekStatementType() const if (mightBeTypeName) { Token::Value next = m_scanner->peekNextToken(); + // So far we only allow ``address payable`` in variable declaration statements and in no other + // kind of statement. This means, for example, that we do not allow type expressions of the form + // ``address payable;``. + // If we want to change this in the future, we need to consider another scanner token here. + if (Token::isElementaryTypeName(token) && Token::isStateMutabilitySpecifier(next, false)) + return LookAheadInfo::VariableDeclaration; if (next == Token::Identifier || Token::isLocationSpecifier(next)) return LookAheadInfo::VariableDeclaration; if (next == Token::LBrack || next == Token::Period) diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index 7ce24e69..73c85482 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -312,7 +312,12 @@ public: static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; } static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; } static bool isLocationSpecifier(Value op) { return op == Memory || op == Storage || op == CallData; } - static bool isStateMutabilitySpecifier(Value op) { return op == Pure || op == Constant || op == View || op == Payable; } + static bool isStateMutabilitySpecifier(Value op, bool _allowConstant = true) + { + if (op == Constant && _allowConstant) + return true; + return op == Pure || op == View || op == Payable; + } static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; } static bool isTimeSubdenomination(Value op) { return op == SubSecond || op == SubMinute || op == SubHour || op == SubDay || op == SubWeek || op == SubYear; } static bool isReservedKeyword(Value op) { return (Abstract <= op && op <= Unchecked); } |