libsolidity: Extend the AST for named AST nodes in order to get precise locations for names.

The actual SourceLocation on an ASTNode is representing the whole
ASTNode whereas in an LSP (for example) you are also interested in the
SourceLocation of a name of a construct (e.g. variable decarlation, function definition, ...).

This also properly encodes non-existend sources as `-1` in the JSON output (eliminating the use of `numeric_limits<size_t>::max()`).
This commit is contained in:
Christian Parpart 2021-01-21 16:01:39 +01:00 committed by chriseth
parent 72c6932bf5
commit 32ba5f5ae7
15 changed files with 112 additions and 46 deletions

View File

@ -15,6 +15,7 @@ Bugfixes:
AST Changes:
* Support field `documentation` to hold NatSpec comments above each statement.
* Adds `nameLocation` to declarations to represent the exact location of the symbolic name.
### 0.8.1 (2021-01-27)

View File

@ -34,9 +34,11 @@ SourceLocation const parseSourceLocation(std::string const& _input, std::string
boost::algorithm::split(pos, _input, boost::is_any_of(":"));
solAssert(pos.size() == 3, "SourceLocation string must have 3 colon separated numeric fields.");
auto const sourceIndex = stoi(pos[Index]);
astAssert(
pos.size() == 3 &&
_maxIndex >= static_cast<size_t>(stoi(pos[Index])),
sourceIndex == -1 || _maxIndex >= static_cast<size_t>(sourceIndex),
"'src'-field ill-formatted or src-index too high"
);
@ -44,7 +46,9 @@ SourceLocation const parseSourceLocation(std::string const& _input, std::string
int end = start + stoi(pos[Length]);
// ASSUMPTION: only the name of source is used from here on, the m_source of the CharStream-Object can be empty
std::shared_ptr<langutil::CharStream> source = std::make_shared<langutil::CharStream>("", _sourceName);
std::shared_ptr<langutil::CharStream> source;
if (sourceIndex != -1)
source = std::make_shared<langutil::CharStream>("", _sourceName);
return SourceLocation{start, end, source};
}

View File

@ -244,12 +244,14 @@ public:
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> _name,
SourceLocation _nameLocation,
Visibility _visibility = Visibility::Default
):
ASTNode(_id, _location), m_name(std::move(_name)), m_visibility(_visibility) {}
ASTNode(_id, _location), m_name(std::move(_name)), m_nameLocation(std::move(_nameLocation)), m_visibility(_visibility) {}
/// @returns the declared name.
ASTString const& name() const { return *m_name; }
SourceLocation const& nameLocation() const noexcept { return m_nameLocation; }
bool noVisibilitySpecified() const { return m_visibility == Visibility::Default; }
Visibility visibility() const { return m_visibility == Visibility::Default ? defaultVisibility() : m_visibility; }
bool isPublic() const { return visibility() >= Visibility::Public; }
@ -287,6 +289,7 @@ protected:
private:
ASTPointer<ASTString> m_name;
SourceLocation m_nameLocation;
Visibility m_visibility;
};
@ -345,9 +348,10 @@ public:
SourceLocation const& _location,
ASTPointer<ASTString> _path,
ASTPointer<ASTString> const& _unitAlias,
SourceLocation _unitAliasLocation,
SymbolAliasList _symbolAliases
):
Declaration(_id, _location, _unitAlias),
Declaration(_id, _location, _unitAlias, std::move(_unitAliasLocation)),
m_path(std::move(_path)),
m_symbolAliases(move(_symbolAliases))
{ }
@ -477,13 +481,14 @@ public:
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
SourceLocation _nameLocation,
ASTPointer<StructuredDocumentation> const& _documentation,
std::vector<ASTPointer<InheritanceSpecifier>> _baseContracts,
std::vector<ASTPointer<ASTNode>> _subNodes,
ContractKind _contractKind = ContractKind::Contract,
bool _abstract = false
):
Declaration(_id, _location, _name),
Declaration(_id, _location, _name, std::move(_nameLocation)),
StructurallyDocumented(_documentation),
m_baseContracts(std::move(_baseContracts)),
m_subNodes(std::move(_subNodes)),
@ -643,9 +648,10 @@ public:
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
SourceLocation _nameLocation,
std::vector<ASTPointer<VariableDeclaration>> _members
):
Declaration(_id, _location, _name), m_members(std::move(_members)) {}
Declaration(_id, _location, _name, std::move(_nameLocation)), m_members(std::move(_members)) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -670,9 +676,10 @@ public:
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
SourceLocation _nameLocation,
std::vector<ASTPointer<EnumValue>> _members
):
Declaration(_id, _location, _name), m_members(std::move(_members)) {}
Declaration(_id, _location, _name, std::move(_nameLocation)), m_members(std::move(_members)) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -696,7 +703,7 @@ class EnumValue: public Declaration
{
public:
EnumValue(int64_t _id, SourceLocation const& _location, ASTPointer<ASTString> const& _name):
Declaration(_id, _location, _name) {}
Declaration(_id, _location, _name, _location) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -738,13 +745,14 @@ public:
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
SourceLocation _nameLocation,
Visibility _visibility,
ASTPointer<ParameterList> _parameters,
bool _isVirtual = false,
ASTPointer<OverrideSpecifier> _overrides = nullptr,
ASTPointer<ParameterList> _returnParameters = ASTPointer<ParameterList>()
):
Declaration(_id, _location, _name, _visibility),
Declaration(_id, _location, _name, std::move(_nameLocation), _visibility),
m_parameters(std::move(_parameters)),
m_overrides(std::move(_overrides)),
m_returnParameters(std::move(_returnParameters)),
@ -815,6 +823,7 @@ public:
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
SourceLocation const& _nameLocation,
Visibility _visibility,
StateMutability _stateMutability,
bool _free,
@ -827,7 +836,7 @@ public:
ASTPointer<ParameterList> const& _returnParameters,
ASTPointer<Block> const& _body
):
CallableDeclaration(_id, _location, _name, _visibility, _parameters, _isVirtual, _overrides, _returnParameters),
CallableDeclaration(_id, _location, _name, std::move(_nameLocation), _visibility, _parameters, _isVirtual, _overrides, _returnParameters),
StructurallyDocumented(_documentation),
ImplementationOptional(_body != nullptr),
m_stateMutability(_stateMutability),
@ -928,6 +937,7 @@ public:
SourceLocation const& _location,
ASTPointer<TypeName> _type,
ASTPointer<ASTString> const& _name,
SourceLocation _nameLocation,
ASTPointer<Expression> _value,
Visibility _visibility,
ASTPointer<StructuredDocumentation> const _documentation = nullptr,
@ -936,7 +946,7 @@ public:
ASTPointer<OverrideSpecifier> _overrides = nullptr,
Location _referenceLocation = Location::Unspecified
):
Declaration(_id, _location, _name, _visibility),
Declaration(_id, _location, _name, std::move(_nameLocation), _visibility),
StructurallyDocumented(std::move(_documentation)),
m_typeName(std::move(_type)),
m_value(std::move(_value)),
@ -1033,13 +1043,14 @@ public:
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
SourceLocation _nameLocation,
ASTPointer<StructuredDocumentation> const& _documentation,
ASTPointer<ParameterList> const& _parameters,
bool _isVirtual,
ASTPointer<OverrideSpecifier> const& _overrides,
ASTPointer<Block> const& _body
):
CallableDeclaration(_id, _location, _name, Visibility::Internal, _parameters, _isVirtual, _overrides),
CallableDeclaration(_id, _location, _name, std::move(_nameLocation), Visibility::Internal, _parameters, _isVirtual, _overrides),
StructurallyDocumented(_documentation),
ImplementationOptional(_body != nullptr),
m_body(_body)
@ -1108,11 +1119,12 @@ public:
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
SourceLocation _nameLocation,
ASTPointer<StructuredDocumentation> const& _documentation,
ASTPointer<ParameterList> const& _parameters,
bool _anonymous = false
):
CallableDeclaration(_id, _location, _name, Visibility::Default, _parameters),
CallableDeclaration(_id, _location, _name, std::move(_nameLocation), Visibility::Default, _parameters),
StructurallyDocumented(_documentation),
m_anonymous(_anonymous)
{
@ -1151,7 +1163,7 @@ class MagicVariableDeclaration: public Declaration
{
public:
MagicVariableDeclaration(int _id, ASTString const& _name, Type const* _type):
Declaration(_id, SourceLocation(), std::make_shared<ASTString>(_name)), m_type(_type) { }
Declaration(_id, SourceLocation(), std::make_shared<ASTString>(_name), {}), m_type(_type) { }
void accept(ASTVisitor&) override
{

View File

@ -107,21 +107,21 @@ void ASTJsonConverter::setJsonNode(
m_currentValue[e.first] = std::move(e.second);
}
size_t ASTJsonConverter::sourceIndexFromLocation(SourceLocation const& _location) const
optional<size_t> ASTJsonConverter::sourceIndexFromLocation(SourceLocation const& _location) const
{
if (_location.source && m_sourceIndices.count(_location.source->name()))
return m_sourceIndices.at(_location.source->name());
else
return numeric_limits<size_t>::max();
return nullopt;
}
string ASTJsonConverter::sourceLocationToString(SourceLocation const& _location) const
{
size_t sourceIndex = sourceIndexFromLocation(_location);
optional<size_t> sourceIndexOpt = sourceIndexFromLocation(_location);
int length = -1;
if (_location.start >= 0 && _location.end >= 0)
length = _location.end - _location.start;
return to_string(_location.start) + ":" + to_string(length) + ":" + to_string(sourceIndex);
return to_string(_location.start) + ":" + to_string(length) + ":" + (sourceIndexOpt.has_value() ? to_string(sourceIndexOpt.value()) : "-1");
}
string ASTJsonConverter::namePathToString(std::vector<ASTString> const& _namePath)
@ -243,6 +243,8 @@ bool ASTJsonConverter::visit(ImportDirective const& _node)
addIfSet(attributes, "absolutePath", _node.annotation().absolutePath);
attributes.emplace_back("unitAlias", _node.name());
attributes.emplace_back("nameLocation", Json::Value(sourceLocationToString(_node.nameLocation())));
Json::Value symbolAliases(Json::arrayValue);
for (auto const& symbolAlias: _node.symbolAliases())
{
@ -250,6 +252,7 @@ bool ASTJsonConverter::visit(ImportDirective const& _node)
solAssert(symbolAlias.symbol, "");
tuple["foreign"] = toJson(*symbolAlias.symbol);
tuple["local"] = symbolAlias.alias ? Json::Value(*symbolAlias.alias) : Json::nullValue;
tuple["nameLocation"] = sourceLocationToString(_node.nameLocation());
symbolAliases.append(tuple);
}
attributes.emplace_back("symbolAliases", std::move(symbolAliases));
@ -261,6 +264,7 @@ bool ASTJsonConverter::visit(ContractDefinition const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.name()),
make_pair("nameLocation", sourceLocationToString(_node.nameLocation())),
make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue),
make_pair("contractKind", contractKind(_node.contractKind())),
make_pair("abstract", _node.abstract()),
@ -310,6 +314,7 @@ bool ASTJsonConverter::visit(StructDefinition const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.name()),
make_pair("nameLocation", sourceLocationToString(_node.nameLocation())),
make_pair("visibility", Declaration::visibilityToString(_node.visibility())),
make_pair("members", toJson(_node.members())),
make_pair("scope", idOrNull(_node.scope()))
@ -326,6 +331,7 @@ bool ASTJsonConverter::visit(EnumDefinition const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.name()),
make_pair("nameLocation", sourceLocationToString(_node.nameLocation())),
make_pair("members", toJson(_node.members()))
};
@ -339,7 +345,8 @@ bool ASTJsonConverter::visit(EnumDefinition const& _node)
bool ASTJsonConverter::visit(EnumValue const& _node)
{
setJsonNode(_node, "EnumValue", {
make_pair("name", _node.name())
make_pair("name", _node.name()),
make_pair("nameLocation", sourceLocationToString(_node.nameLocation())),
});
return false;
}
@ -364,6 +371,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.name()),
make_pair("nameLocation", sourceLocationToString(_node.nameLocation())),
make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue),
make_pair("kind", _node.isFree() ? "freeFunction" : TokenTraits::toString(_node.kind())),
make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())),
@ -401,6 +409,7 @@ bool ASTJsonConverter::visit(VariableDeclaration const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.name()),
make_pair("nameLocation", sourceLocationToString(_node.nameLocation())),
make_pair("typeName", toJson(_node.typeName())),
make_pair("constant", _node.isConstant()),
make_pair("mutability", VariableDeclaration::mutabilityToString(_node.mutability())),
@ -428,6 +437,7 @@ bool ASTJsonConverter::visit(ModifierDefinition const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.name()),
make_pair("nameLocation", sourceLocationToString(_node.nameLocation())),
make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue),
make_pair("visibility", Declaration::visibilityToString(_node.visibility())),
make_pair("parameters", toJson(_node.parameterList())),
@ -455,6 +465,7 @@ bool ASTJsonConverter::visit(EventDefinition const& _node)
m_inEvent = true;
setJsonNode(_node, "EventDefinition", {
make_pair("name", _node.name()),
make_pair("nameLocation", sourceLocationToString(_node.nameLocation())),
make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue),
make_pair("parameters", toJson(_node.parameterList())),
make_pair("anonymous", _node.isAnonymous())

View File

@ -137,7 +137,8 @@ private:
std::string const& _nodeName,
std::vector<std::pair<std::string, Json::Value>>&& _attributes
);
size_t sourceIndexFromLocation(langutil::SourceLocation const& _location) const;
/// Maps source location to an index, if source is valid and a mapping does exist, otherwise returns std::nullopt.
std::optional<size_t> sourceIndexFromLocation(langutil::SourceLocation const& _location) const;
std::string sourceLocationToString(langutil::SourceLocation const& _location) const;
static std::string namePathToString(std::vector<ASTString> const& _namePath);
static Json::Value idOrNull(ASTNode const* _pt)

View File

@ -99,6 +99,13 @@ SourceLocation const ASTJsonImporter::createSourceLocation(Json::Value const& _n
return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_currentSourceName, m_sourceLocations.size());
}
SourceLocation ASTJsonImporter::createNameSourceLocation(Json::Value const& _node)
{
astAssert(member(_node, "nameLocation").isString(), "'nameLocation' must be a string");
return solidity::langutil::parseSourceLocation(_node["nameLocation"].asString(), m_currentSourceName, m_sourceLocations.size());
}
template<class T>
ASTPointer<T> ASTJsonImporter::convertJsonToASTNode(Json::Value const& _node)
{
@ -272,6 +279,7 @@ ASTPointer<ImportDirective> ASTJsonImporter::createImportDirective(Json::Value c
_node,
path,
unitAlias,
createNameSourceLocation(_node),
move(symbolAliases)
);
@ -298,6 +306,7 @@ ASTPointer<ContractDefinition> ASTJsonImporter::createContractDefinition(Json::V
return createASTNode<ContractDefinition>(
_node,
make_shared<ASTString>(_node["name"].asString()),
createNameSourceLocation(_node),
_node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")),
baseContracts,
subNodes,
@ -352,6 +361,7 @@ ASTPointer<ASTNode> ASTJsonImporter::createStructDefinition(Json::Value const& _
return createASTNode<StructDefinition>(
_node,
memberAsASTString(_node, "name"),
createNameSourceLocation(_node),
members
);
}
@ -364,6 +374,7 @@ ASTPointer<EnumDefinition> ASTJsonImporter::createEnumDefinition(Json::Value con
return createASTNode<EnumDefinition>(
_node,
memberAsASTString(_node, "name"),
createNameSourceLocation(_node),
members
);
}
@ -434,6 +445,7 @@ ASTPointer<FunctionDefinition> ASTJsonImporter::createFunctionDefinition(Json::V
return createASTNode<FunctionDefinition>(
_node,
memberAsASTString(_node, "name"),
createNameSourceLocation(_node),
vis,
stateMutability(_node),
freeFunction,
@ -475,6 +487,7 @@ ASTPointer<VariableDeclaration> ASTJsonImporter::createVariableDeclaration(Json:
_node,
nullOrCast<TypeName>(member(_node, "typeName")),
make_shared<ASTString>(member(_node, "name").asString()),
createNameSourceLocation(_node),
nullOrCast<Expression>(member(_node, "value")),
visibility(_node),
_node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")),
@ -490,6 +503,7 @@ ASTPointer<ModifierDefinition> ASTJsonImporter::createModifierDefinition(Json::V
return createASTNode<ModifierDefinition>(
_node,
memberAsASTString(_node, "name"),
createNameSourceLocation(_node),
_node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")),
createParameterList(member(_node, "parameters")),
memberAsBool(_node, "virtual"),
@ -515,6 +529,7 @@ ASTPointer<EventDefinition> ASTJsonImporter::createEventDefinition(Json::Value c
return createASTNode<EventDefinition>(
_node,
memberAsASTString(_node, "name"),
createNameSourceLocation(_node),
_node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")),
createParameterList(member(_node, "parameters")),
memberAsBool(_node, "anonymous")

View File

@ -67,6 +67,7 @@ private:
template<class T>
ASTPointer<T> convertJsonToASTNode(Json::Value const& _node);
langutil::SourceLocation createNameSourceLocation(Json::Value const& _node);
/// \defgroup nodeCreators JSON to AST-Nodes
///@{

View File

@ -226,6 +226,7 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
expectToken(Token::Import);
ASTPointer<ASTString> path;
ASTPointer<ASTString> unitAlias = make_shared<string>();
SourceLocation unitAliasLocation{};
ImportDirective::SymbolAliasList symbolAliases;
if (m_scanner->currentToken() == Token::StringLiteral)
@ -234,7 +235,7 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
if (m_scanner->currentToken() == Token::As)
{
m_scanner->next();
unitAlias = expectIdentifierToken();
tie(unitAlias, unitAliasLocation) = expectIdentifierWithLocation();
}
}
else
@ -250,8 +251,7 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
if (m_scanner->currentToken() == Token::As)
{
expectToken(Token::As);
aliasLocation = currentLocation();
alias = expectIdentifierToken();
tie(alias, aliasLocation) = expectIdentifierWithLocation();
}
symbolAliases.emplace_back(ImportDirective::SymbolAlias{move(id), move(alias), aliasLocation});
if (m_scanner->currentToken() != Token::Comma)
@ -264,7 +264,7 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
{
m_scanner->next();
expectToken(Token::As);
unitAlias = expectIdentifierToken();
tie(unitAlias, unitAliasLocation) = expectIdentifierWithLocation();
}
else
fatalParserError(9478_error, "Expected string literal (path), \"*\" or alias list.");
@ -281,7 +281,7 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
fatalParserError(6326_error, "Import path cannot be empty.");
nodeFactory.markEndPosition();
expectToken(Token::Semicolon);
return nodeFactory.createNode<ImportDirective>(path, unitAlias, move(symbolAliases));
return nodeFactory.createNode<ImportDirective>(path, unitAlias, unitAliasLocation, move(symbolAliases));
}
std::pair<ContractKind, bool> Parser::parseContractKind()
@ -317,6 +317,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> name = nullptr;
SourceLocation nameLocation{};
ASTPointer<StructuredDocumentation> documentation;
vector<ASTPointer<InheritanceSpecifier>> baseContracts;
vector<ASTPointer<ASTNode>> subNodes;
@ -325,7 +326,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
{
documentation = parseStructuredDocumentation();
contractKind = parseContractKind();
name = expectIdentifierToken();
tie(name, nameLocation) = expectIdentifierWithLocation();
if (m_scanner->currentToken() == Token::Is)
do
{
@ -385,6 +386,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
expectToken(Token::RBrace);
return nodeFactory.createNode<ContractDefinition>(
name,
nameLocation,
documentation,
baseContracts,
subNodes,
@ -572,6 +574,7 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinition(bool _freeFunction)
Token kind = m_scanner->currentToken();
ASTPointer<ASTString> name;
SourceLocation nameLocation;
if (kind == Token::Function)
{
m_scanner->next();
@ -586,6 +589,7 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinition(bool _freeFunction)
{Token::Fallback, "fallback function"},
{Token::Receive, "receive function"},
}.at(m_scanner->currentToken());
nameLocation = currentLocation();
name = make_shared<ASTString>(TokenTraits::toString(m_scanner->currentToken()));
string message{
"This function is named \"" + *name + "\" but is not the " + expected + " of the contract. "
@ -599,7 +603,7 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinition(bool _freeFunction)
m_scanner->next();
}
else
name = expectIdentifierToken();
tie(name, nameLocation) = expectIdentifierWithLocation();
}
else
{
@ -621,6 +625,7 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinition(bool _freeFunction)
}
return nodeFactory.createNode<FunctionDefinition>(
name,
nameLocation,
header.visibility,
header.stateMutability,
_freeFunction,
@ -640,7 +645,7 @@ ASTPointer<StructDefinition> Parser::parseStructDefinition()
RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::Struct);
ASTPointer<ASTString> name = expectIdentifierToken();
auto [name, nameLocation] = expectIdentifierWithLocation();
vector<ASTPointer<VariableDeclaration>> members;
expectToken(Token::LBrace);
while (m_scanner->currentToken() != Token::RBrace)
@ -650,7 +655,7 @@ ASTPointer<StructDefinition> Parser::parseStructDefinition()
}
nodeFactory.markEndPosition();
expectToken(Token::RBrace);
return nodeFactory.createNode<StructDefinition>(name, members);
return nodeFactory.createNode<StructDefinition>(move(name), move(nameLocation), move(members));
}
ASTPointer<EnumValue> Parser::parseEnumValue()
@ -666,7 +671,7 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::Enum);
ASTPointer<ASTString> name = expectIdentifierToken();
auto [name, nameLocation] = expectIdentifierWithLocation();
vector<ASTPointer<EnumValue>> members;
expectToken(Token::LBrace);
@ -684,7 +689,7 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
nodeFactory.markEndPosition();
expectToken(Token::RBrace);
return nodeFactory.createNode<EnumDefinition>(name, members);
return nodeFactory.createNode<EnumDefinition>(name, nameLocation, members);
}
ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
@ -717,6 +722,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
Visibility visibility(Visibility::Default);
VariableDeclaration::Location location = VariableDeclaration::Location::Unspecified;
ASTPointer<ASTString> identifier;
SourceLocation nameLocation{};
while (true)
{
@ -795,7 +801,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
else
{
nodeFactory.markEndPosition();
identifier = expectIdentifierToken();
tie(identifier, nameLocation) = expectIdentifierWithLocation();
}
ASTPointer<Expression> value;
if (_options.allowInitialValue)
@ -810,6 +816,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
return nodeFactory.createNode<VariableDeclaration>(
type,
identifier,
nameLocation,
value,
visibility,
documentation,
@ -830,7 +837,7 @@ ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
ASTPointer<StructuredDocumentation> documentation = parseStructuredDocumentation();
expectToken(Token::Modifier);
ASTPointer<ASTString> name(expectIdentifierToken());
auto [name, nameLocation] = expectIdentifierWithLocation();
ASTPointer<ParameterList> parameters;
if (m_scanner->currentToken() == Token::LParen)
{
@ -875,7 +882,15 @@ ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
else
m_scanner->next(); // just consume the ';'
return nodeFactory.createNode<ModifierDefinition>(name, documentation, parameters, isVirtual, overrides, block);
return nodeFactory.createNode<ModifierDefinition>(name, nameLocation, documentation, parameters, isVirtual, overrides, block);
}
pair<ASTPointer<ASTString>, SourceLocation> Parser::expectIdentifierWithLocation()
{
SourceLocation nameLocation = currentLocation();
ASTPointer<ASTString> name = expectIdentifierToken();
return {move(name), move(nameLocation)};
}
ASTPointer<EventDefinition> Parser::parseEventDefinition()
@ -885,7 +900,7 @@ ASTPointer<EventDefinition> Parser::parseEventDefinition()
ASTPointer<StructuredDocumentation> documentation = parseStructuredDocumentation();
expectToken(Token::Event);
ASTPointer<ASTString> name(expectIdentifierToken());
auto [name, nameLocation] = expectIdentifierWithLocation();
VarDeclParserOptions options;
options.allowIndexed = true;
@ -899,7 +914,7 @@ ASTPointer<EventDefinition> Parser::parseEventDefinition()
}
nodeFactory.markEndPosition();
expectToken(Token::Semicolon);
return nodeFactory.createNode<EventDefinition>(name, documentation, parameters, anonymous);
return nodeFactory.createNode<EventDefinition>(name, nameLocation, documentation, parameters, anonymous);
}
ASTPointer<UsingForDirective> Parser::parseUsingDirective()

View File

@ -151,6 +151,7 @@ private:
std::vector<ASTPointer<Expression>> parseFunctionCallListArguments();
std::pair<std::vector<ASTPointer<Expression>>, std::vector<ASTPointer<ASTString>>> parseFunctionCallArguments();
std::pair<std::vector<ASTPointer<Expression>>, std::vector<ASTPointer<ASTString>>> parseNamedArguments();
std::pair<ASTPointer<ASTString>, langutil::SourceLocation> expectIdentifierWithLocation();
///@}
///@{

View File

@ -187,7 +187,7 @@ Json::Value AsmJsonConverter::createAstNode(langutil::SourceLocation const& _loc
int length = -1;
if (_location.start >= 0 && _location.end >= 0)
length = _location.end - _location.start;
ret["src"] = to_string(_location.start) + ":" + to_string(length) + ":" + m_sourceIndex;
ret["src"] = to_string(_location.start) + ":" + to_string(length) + ":" + (m_sourceIndex.has_value() ? to_string(m_sourceIndex.value()) : "-1");
return ret;
}

View File

@ -27,6 +27,7 @@
#include <liblangutil/SourceLocation.h>
#include <json/json.h>
#include <boost/variant.hpp>
#include <optional>
#include <vector>
namespace solidity::yul
@ -40,7 +41,7 @@ class AsmJsonConverter: public boost::static_visitor<Json::Value>
public:
/// Create a converter to JSON for any block of inline assembly
/// @a _sourceIndex to be used to abbreviate source name in the source locations
explicit AsmJsonConverter(size_t _sourceIndex): m_sourceIndex(std::to_string(_sourceIndex)) {}
explicit AsmJsonConverter(std::optional<size_t> _sourceIndex): m_sourceIndex(_sourceIndex) {}
Json::Value operator()(Block const& _node) const;
Json::Value operator()(TypedName const& _node) const;
@ -65,7 +66,7 @@ private:
template <class T>
Json::Value vectorOfVariantsToJson(std::vector<T> const& vec) const;
std::string const m_sourceIndex;
std::optional<size_t> const m_sourceIndex;
};
}

View File

@ -40,6 +40,7 @@ JSON AST (compact format):
18
],
"name": "Error1",
"nameLocation": "71:6:0",
"nodeType": "ContractDefinition",
"nodes":
[
@ -59,6 +60,7 @@ JSON AST (compact format):
"kind": "constructor",
"modifiers": [],
"name": "",
"nameLocation": "-1:-1:-1",
"nodeType": "FunctionDefinition",
"parameters":
{
@ -120,6 +122,7 @@ JSON AST (compact format):
"kind": "function",
"modifiers": [],
"name": "five",
"nameLocation": "407:4:0",
"nodeType": "FunctionDefinition",
"parameters":
{
@ -139,6 +142,7 @@ JSON AST (compact format):
"id": 12,
"mutability": "mutable",
"name": "",
"nameLocation": "-1:-1:-1",
"nodeType": "VariableDeclaration",
"scope": 17,
"src": "434:4:0",

View File

@ -10,4 +10,4 @@
2 | pragma solidity >=0.0; contract Errort6 { using foo for ; /* missing type name */ }
| ^
","message":"Recovered in ContractDefinition at '}'.","severity":"warning","sourceLocation":{"end":120,"file":"A","start":119},"type":"Warning"}],"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"Errort6":[3]},"id":4,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"abstract":false,"baseContracts":[],"contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":3,"linearizedBaseContracts":[3],"name":"Errort6","nodeType":"ContractDefinition","nodes":[],"scope":4,"src":"59:35:0"}],"src":"36:84:0"},"id":0}}}
","message":"Recovered in ContractDefinition at '}'.","severity":"warning","sourceLocation":{"end":120,"file":"A","start":119},"type":"Warning"}],"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"Errort6":[3]},"id":4,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"abstract":false,"baseContracts":[],"contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":3,"linearizedBaseContracts":[3],"name":"Errort6","nameLocation":"68:7:0","nodeType":"ContractDefinition","nodes":[],"scope":4,"src":"59:35:0"}],"src":"36:84:0"},"id":0}}}

View File

@ -1 +1 @@
{"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"C":[6]},"id":7,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"abstract":false,"baseContracts":[],"contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":6,"linearizedBaseContracts":[6],"name":"C","nodeType":"ContractDefinition","nodes":[{"body":{"id":4,"nodeType":"Block","src":"97:2:0","statements":[]},"functionSelector":"26121ff0","id":5,"implemented":true,"kind":"function","modifiers":[],"name":"f","nodeType":"FunctionDefinition","parameters":{"id":2,"nodeType":"ParameterList","parameters":[],"src":"82:2:0"},"returnParameters":{"id":3,"nodeType":"ParameterList","parameters":[],"src":"97:0:0"},"scope":6,"src":"72:27:0","stateMutability":"pure","virtual":false,"visibility":"public"}],"scope":7,"src":"59:42:0"}],"src":"36:65:0"},"id":0}}}
{"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"C":[6]},"id":7,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"abstract":false,"baseContracts":[],"contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":6,"linearizedBaseContracts":[6],"name":"C","nameLocation":"68:1:0","nodeType":"ContractDefinition","nodes":[{"body":{"id":4,"nodeType":"Block","src":"97:2:0","statements":[]},"functionSelector":"26121ff0","id":5,"implemented":true,"kind":"function","modifiers":[],"name":"f","nameLocation":"81:1:0","nodeType":"FunctionDefinition","parameters":{"id":2,"nodeType":"ParameterList","parameters":[],"src":"82:2:0"},"returnParameters":{"id":3,"nodeType":"ParameterList","parameters":[],"src":"97:0:0"},"scope":6,"src":"72:27:0","stateMutability":"pure","virtual":false,"visibility":"public"}],"scope":7,"src":"59:42:0"}],"src":"36:65:0"},"id":0}}}

View File

@ -179,15 +179,15 @@ BOOST_AUTO_TEST_CASE(type_identifiers)
TypePointer multiArray = TypeProvider::array(DataLocation::Storage, stringArray);
BOOST_CHECK_EQUAL(multiArray->identifier(), "t_array$_t_array$_t_string_storage_$20_storage_$dyn_storage_ptr");
ContractDefinition c(++id, SourceLocation{}, make_shared<string>("MyContract$"), {}, {}, {}, ContractKind::Contract);
ContractDefinition c(++id, SourceLocation{}, make_shared<string>("MyContract$"), SourceLocation{}, {}, {}, {}, ContractKind::Contract);
BOOST_CHECK_EQUAL(c.type()->identifier(), "t_type$_t_contract$_MyContract$$$_$2_$");
BOOST_CHECK_EQUAL(ContractType(c, true).identifier(), "t_super$_MyContract$$$_$2");
StructDefinition s(++id, {}, make_shared<string>("Struct"), {});
StructDefinition s(++id, {}, make_shared<string>("Struct"), {}, {});
s.annotation().recursive = false;
BOOST_CHECK_EQUAL(s.type()->identifier(), "t_type$_t_struct$_Struct_$3_storage_ptr_$");
EnumDefinition e(++id, {}, make_shared<string>("Enum"), {});
EnumDefinition e(++id, {}, make_shared<string>("Enum"), {}, {});
BOOST_CHECK_EQUAL(e.type()->identifier(), "t_type$_t_enum$_Enum_$4_$");
TupleType t({e.type(), s.type(), stringArray, nullptr});
@ -206,7 +206,7 @@ BOOST_AUTO_TEST_CASE(type_identifiers)
// TypeType is tested with contract
auto emptyParams = make_shared<ParameterList>(++id, SourceLocation(), std::vector<ASTPointer<VariableDeclaration>>());
ModifierDefinition mod(++id, SourceLocation{}, make_shared<string>("modif"), {}, emptyParams, {}, {}, {});
ModifierDefinition mod(++id, SourceLocation{}, make_shared<string>("modif"), SourceLocation{}, {}, emptyParams, {}, {}, {});
BOOST_CHECK_EQUAL(ModifierType(mod).identifier(), "t_modifier$__$");
SourceUnit su(++id, {}, {}, {});