diff --git a/docs/grammar/SolidityParser.g4 b/docs/grammar/SolidityParser.g4 index aa90b30bc..673f1c071 100644 --- a/docs/grammar/SolidityParser.g4 +++ b/docs/grammar/SolidityParser.g4 @@ -311,11 +311,31 @@ errorDefinition: LParen (parameters+=errorParameter (Comma parameters+=errorParameter)*)? RParen Semicolon; +/** + * Operators that users are allowed to implement for some types with `using for`. + */ +userDefinableOperator: + BitAnd + | BitNot + | BitOr + | BitXor + | Add + | Div + | Mod + | Mul + | Sub + | Equal + | GreaterThan + | GreaterThanOrEqual + | LessThan + | LessThanOrEqual + | NotEqual; + /** * Using directive to attach library functions and free functions to types. * Can occur within contracts and libraries and at the file level. */ -usingDirective: Using (identifierPath | (LBrace identifierPath (Comma identifierPath)* RBrace)) For (Mul | typeName) Global? Semicolon; +usingDirective: Using (identifierPath | (LBrace identifierPath (As userDefinableOperator)? (Comma identifierPath (As userDefinableOperator)?)* RBrace)) For (Mul | typeName) Global? Semicolon; /** * A type name can be an elementary type, a function type, a mapping type, a user-defined type * (e.g. a contract or struct) or an array type. diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index a9065b176..d3bdf549f 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -23,6 +23,7 @@ #include +#include #include #include #include @@ -976,6 +977,7 @@ ASTPointer Parser::parseUsingDirective() expectToken(Token::Using); vector> functions; + vector> operators; bool const usesBraces = m_scanner->currentToken() == Token::LBrace; if (usesBraces) { @@ -983,12 +985,35 @@ ASTPointer Parser::parseUsingDirective() { advance(); functions.emplace_back(parseIdentifierPath()); + if (m_scanner->currentToken() == Token::As) + { + advance(); + Token operator_ = m_scanner->currentToken(); + if (!util::contains(userDefinableOperators, operator_)) + { + parserError( + 4403_error, + fmt::format( + "Not a user-definable operator: {}. Only the following operators can be user-defined: {}", + (!m_scanner->currentLiteral().empty() ? m_scanner->currentLiteral() : string(TokenTraits::toString(operator_))), + util::joinHumanReadable(userDefinableOperators | ranges::views::transform([](Token _t) { return string{TokenTraits::toString(_t)}; })) + ) + ); + } + operators.emplace_back(operator_); + advance(); + } + else + operators.emplace_back(nullopt); } while (m_scanner->currentToken() == Token::Comma); expectToken(Token::RBrace); } else + { functions.emplace_back(parseIdentifierPath()); + operators.emplace_back(nullopt); + } ASTPointer typeName; expectToken(Token::For); @@ -1004,7 +1029,7 @@ ASTPointer Parser::parseUsingDirective() } nodeFactory.markEndPosition(); expectToken(Token::Semicolon); - return nodeFactory.createNode(std::move(functions), usesBraces, typeName, global); + return nodeFactory.createNode(std::move(functions), std::move(operators), usesBraces, typeName, global); } ASTPointer Parser::parseModifierInvocation()