User-defined literal suffixes: AST

This commit is contained in:
Kamil Śliwak 2023-02-24 19:47:00 +01:00
parent 3b067616de
commit e06e2c72ce
5 changed files with 124 additions and 14 deletions

View File

@ -29,6 +29,7 @@
#include <libsolidity/ast/TypeProvider.h> #include <libsolidity/ast/TypeProvider.h>
#include <libsolutil/FunctionSelector.h> #include <libsolutil/FunctionSelector.h>
#include <libsolutil/Keccak256.h> #include <libsolutil/Keccak256.h>
#include <libsolutil/Visitor.h>
#include <range/v3/range/conversion.hpp> #include <range/v3/range/conversion.hpp>
#include <range/v3/view/tail.hpp> #include <range/v3/view/tail.hpp>
@ -986,11 +987,85 @@ IdentifierAnnotation& Identifier::annotation() const
return initAnnotation<IdentifierAnnotation>(); return initAnnotation<IdentifierAnnotation>();
} }
Type const& Literal::typeOfValue() const
{
// no suffix/denomination -> literal is the whole expression and types match.
// suffix -> type comes from return value of suffix function.
return std::visit(util::GenericVisitor{
[](ASTPointer<Identifier> const& _identifier)-> Type const& {
solAssert(_identifier->annotation().arguments.has_value());
solAssert(_identifier->annotation().arguments->numArguments() == 1);
solAssert(_identifier->annotation().arguments->types[0]);
return *_identifier->annotation().arguments->types[0];
},
[](ASTPointer<MemberAccess> const& _memberAccess)-> Type const& {
solAssert(_memberAccess->annotation().arguments.has_value());
solAssert(_memberAccess->annotation().arguments->numArguments() == 1);
solAssert(_memberAccess->annotation().arguments->types[0]);
return *_memberAccess->annotation().arguments->types[0];
},
[&](Literal::SubDenomination) -> Type const& {
solAssert(annotation().type);
return *annotation().type;
},
}, m_suffix);
}
ASTString Literal::valueWithoutUnderscores() const ASTString Literal::valueWithoutUnderscores() const
{ {
return boost::erase_all_copy(value(), "_"); return boost::erase_all_copy(value(), "_");
} }
Literal::SubDenomination Literal::subDenomination() const
{
solAssert(holds_alternative<SubDenomination>(m_suffix));
return get<SubDenomination>(m_suffix);
}
FunctionDefinition const* Literal::suffixFunction() const
{
if (holds_alternative<SubDenomination>(m_suffix))
return nullptr;
Declaration const* referencedDeclaration = visit(util::GenericVisitor{
[&](ASTPointer<Identifier> const& _identifier) { return _identifier->annotation().referencedDeclaration; },
[&](ASTPointer<MemberAccess> const& _memberAccess) { return _memberAccess->annotation().referencedDeclaration; },
[&](SubDenomination) -> Declaration const* { solAssert(false); },
}, m_suffix);
solAssert(referencedDeclaration, "Literal suffix must have a definition.");
auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(referencedDeclaration);
solAssert(functionDefinition, "Non-denomination literal suffix must be a function.");
return functionDefinition;
}
FunctionType const* Literal::suffixFunctionType() const
{
return dynamic_cast<FunctionType const*>(visit(util::GenericVisitor{
[&](ASTPointer<Identifier> const& _identifier) { return _identifier->annotation().type; },
[&](ASTPointer<MemberAccess> const& _memberAccess) { return _memberAccess->annotation().type; },
[&](Literal::SubDenomination) -> Type const* { return nullptr; },
}, suffix()));
}
bool Literal::isSuffixed() const
{
return visit(util::GenericVisitor{
[&](ASTPointer<Identifier> const&) { return true; },
[&](ASTPointer<MemberAccess> const&) { return true; },
[&](Literal::SubDenomination) { return hasSubDenomination(); },
}, suffix());
}
bool Literal::hasSubDenomination() const
{
return visit(util::GenericVisitor{
[&](ASTPointer<Identifier> const&) { return false; },
[&](ASTPointer<MemberAccess> const&) { return false; },
[&](Literal::SubDenomination _subDenomination) { return _subDenomination != SubDenomination::None; },
}, suffix());
}
bool Literal::isHexNumber() const bool Literal::isHexNumber() const
{ {
if (token() != Token::Number) if (token() != Token::Number)
@ -1000,7 +1075,8 @@ bool Literal::isHexNumber() const
bool Literal::looksLikeAddress() const bool Literal::looksLikeAddress() const
{ {
if (subDenomination() != SubDenomination::None) // User suffixes are fine.
if (hasSubDenomination())
return false; return false;
if (!isHexNumber()) if (!isHexNumber())

View File

@ -2377,6 +2377,8 @@ private:
/** /**
* A literal string or number. @see ExpressionCompiler::endVisit() is used to actually parse its value. * A literal string or number. @see ExpressionCompiler::endVisit() is used to actually parse its value.
*
* It can have a suffix that can lead to a function call.
*/ */
class Literal: public PrimaryExpression class Literal: public PrimaryExpression
{ {
@ -2394,24 +2396,41 @@ public:
Week = static_cast<int>(Token::SubWeek), Week = static_cast<int>(Token::SubWeek),
Year = static_cast<int>(Token::SubYear) Year = static_cast<int>(Token::SubYear)
}; };
using Suffix = std::variant<SubDenomination, ASTPointer<Identifier>, ASTPointer<MemberAccess>>;
Literal( Literal(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
Token _token, Token _token,
ASTPointer<ASTString> _value, ASTPointer<ASTString> _value,
SubDenomination _sub = SubDenomination::None Suffix _suffix = SubDenomination::None
): ):
PrimaryExpression(_id, _location), m_token(_token), m_value(std::move(_value)), m_subDenomination(_sub) {} PrimaryExpression(_id, _location), m_token(_token), m_value(std::move(_value)), m_suffix(std::move(_suffix)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
Token token() const { return m_token; } Token token() const { return m_token; }
/// @returns the non-parsed value of the literal /// @returns the non-parsed value of the literal
ASTString const& value() const { return *m_value; } ASTString const& value() const { return *m_value; }
Type const& typeOfValue() const;
ASTString valueWithoutUnderscores() const; ASTString valueWithoutUnderscores() const;
SubDenomination subDenomination() const { return m_subDenomination; } /// @returns The subdenomination represented by the suffix.
/// Must not be called if the suffix is not a subdenomination.
SubDenomination subDenomination() const;
/// @returns The suffix of the literal. Does not check whether the suffix is present or is a
/// subdenomination.
/// @note Missing suffix is represented by SubDenomination::None.
Suffix const& suffix() const { return m_suffix; }
/// @returns Function definition associated with the suffix if the suffix is not a subdenomination.
/// nullptr otherwise.
FunctionDefinition const* suffixFunction() const;
FunctionType const* suffixFunctionType() const;
/// @returns true if the literal is suffixed with either a subdenomination or a suffix function.
bool isSuffixed() const;
/// @returns true if the literal is suffixed a subdenomination.
bool hasSubDenomination() const;
/// @returns true if this is a number with a hex prefix. /// @returns true if this is a number with a hex prefix.
bool isHexNumber() const; bool isHexNumber() const;
@ -2426,7 +2445,7 @@ public:
private: private:
Token m_token; Token m_token;
ASTPointer<ASTString> m_value; ASTPointer<ASTString> m_value;
SubDenomination m_subDenomination; Suffix m_suffix;
}; };
/// @} /// @}

View File

@ -302,6 +302,8 @@ struct IdentifierAnnotation: ExpressionAnnotation
std::vector<Declaration const*> candidateDeclarations; std::vector<Declaration const*> candidateDeclarations;
/// List of possible declarations it could refer to. /// List of possible declarations it could refer to.
std::vector<Declaration const*> overloadedDeclarations; std::vector<Declaration const*> overloadedDeclarations;
/// If the identifier is used as a literal suffix, this points at the literal.
Literal const* suffixedLiteral = nullptr;
}; };
struct MemberAccessAnnotation: ExpressionAnnotation struct MemberAccessAnnotation: ExpressionAnnotation
@ -310,6 +312,8 @@ struct MemberAccessAnnotation: ExpressionAnnotation
Declaration const* referencedDeclaration = nullptr; Declaration const* referencedDeclaration = nullptr;
/// What kind of lookup needs to be done (static, virtual, super) find the declaration. /// What kind of lookup needs to be done (static, virtual, super) find the declaration.
util::SetOnce<VirtualLookup> requiredLookup; util::SetOnce<VirtualLookup> requiredLookup;
/// If the expression is used as a literal suffix, this points at the literal.
Literal const* suffixedLiteral = nullptr;
}; };
struct OperationAnnotation: ExpressionAnnotation struct OperationAnnotation: ExpressionAnnotation

View File

@ -984,17 +984,15 @@ bool ASTJsonExporter::visit(Literal const& _node)
Json::Value value{_node.value()}; Json::Value value{_node.value()};
if (!util::validateUTF8(_node.value())) if (!util::validateUTF8(_node.value()))
value = Json::nullValue; value = Json::nullValue;
Token subdenomination = Token(_node.subDenomination()); Json::Value subdenomination = Json::nullValue;
// if (auto subden = get_if<Literal::SubDenomination>(&_node.suffix()))
// subdenomination = Json::Value{TokenTraits::toString(*subden)};
// TODO suffix
std::vector<pair<string, Json::Value>> attributes = { std::vector<pair<string, Json::Value>> attributes = {
make_pair("kind", literalTokenKind(_node.token())), make_pair("kind", literalTokenKind(_node.token())),
make_pair("value", value), make_pair("value", value),
make_pair("hexValue", util::toHex(util::asBytes(_node.value()))), make_pair("hexValue", util::toHex(util::asBytes(_node.value()))),
make_pair( make_pair("subdenomination", subdenomination)
"subdenomination",
subdenomination == Token::Illegal ?
Json::nullValue :
Json::Value{TokenTraits::toString(subdenomination)}
)
}; };
appendExpressionAttributes(attributes, _node.annotation()); appendExpressionAttributes(attributes, _node.annotation());
setJsonNode(_node, "Literal", std::move(attributes)); setJsonNode(_node, "Literal", std::move(attributes));

View File

@ -27,6 +27,8 @@
#include <libsolidity/ast/AST.h> #include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTVisitor.h> #include <libsolidity/ast/ASTVisitor.h>
#include <libsolutil/Visitor.h>
namespace solidity::frontend namespace solidity::frontend
{ {
@ -1014,13 +1016,24 @@ void ElementaryTypeNameExpression::accept(ASTConstVisitor& _visitor) const
void Literal::accept(ASTVisitor& _visitor) void Literal::accept(ASTVisitor& _visitor)
{ {
_visitor.visit(*this); if (_visitor.visit(*this))
std::visit(solidity::util::GenericVisitor{
[&](ASTPointer<Identifier> const& _identifier) { _identifier->accept(_visitor); },
[&](ASTPointer<MemberAccess> const& _memberAccess) { _memberAccess->accept(_visitor); },
[&](SubDenomination) {},
}, m_suffix);
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
void Literal::accept(ASTConstVisitor& _visitor) const void Literal::accept(ASTConstVisitor& _visitor) const
{ {
_visitor.visit(*this); if (_visitor.visit(*this))
std::visit(solidity::util::GenericVisitor{
[&](ASTPointer<Identifier> const& _identifier) { _identifier->accept(_visitor); },
[&](ASTPointer<MemberAccess> const& _memberAccess) { _memberAccess->accept(_visitor); },
[&](SubDenomination) {},
}, m_suffix);
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }