#pragma once
#include <libsolidity/analysis/OverrideChecker.h>
#include <libsolidity/ast/AST.h>
#include <liblangutil/CharStreamProvider.h>
#include <liblangutil/Scanner.h>
#include <sstream>
#include <regex>
namespace solidity::tools
{
class LocationHelper
{
std::stringstream m_stream;
public:
template <typename T>
LocationHelper& operator<<(T const& data)
{
m_stream << data;
return *this;
}
operator std::string() { return m_stream.str(); }
};
class SourceTextRetriever
{
public:
explicit SourceTextRetriever(langutil::CharStreamProvider const& _charStreamProvider):
m_charStreamProvider(_charStreamProvider) {}
std::string text(langutil::SourceLocation const& _location) const
{
solAssert(_location.hasText(), "");
return std::string{m_charStreamProvider.charStream(*_location.sourceName).text(_location)};
}
protected:
langutil::CharStreamProvider const& m_charStreamProvider;
};
class SourceAnalysis: public SourceTextRetriever
{
public:
using SourceTextRetriever::SourceTextRetriever;
bool isMultilineKeyword(
langutil::SourceLocation const& _location,
std::string const& _keyword
) const
{
return regex_search(
text(_location),
std::regex{"(\\b" + _keyword + "\\b\\n|\\r|\\r\\n)"}
);
}
bool hasMutabilityKeyword(langutil::SourceLocation const& _location) const
{
return regex_search(
text(_location),
std::regex{"(\\b(pure|view|nonpayable|payable)\\b)"}
);
}
bool hasVirtualKeyword(langutil::SourceLocation const& _location) const
{
return regex_search(text(_location), std::regex{"(\\b(virtual)\\b)"});
}
bool hasVisibilityKeyword(langutil::SourceLocation const& _location) const
{
return regex_search(text(_location), std::regex{"\\bpublic\\b"});
}
};
class SourceGeneration
{
public:
using CompareFunction = frontend::OverrideChecker::CompareByID;
using Contracts = std::set<frontend::ContractDefinition const*, CompareFunction>;
static std::string functionOverride(Contracts const& _contracts)
{
if (_contracts.size() <= 1)
return "override";
std::string overrideList;
for (auto inheritedContract: _contracts)
overrideList += inheritedContract->name() + ",";
return "override(" + overrideList.substr(0, overrideList.size() - 1) + ")";
}
};
class SourceTransform: public SourceTextRetriever
{
public:
using SourceTextRetriever::SourceTextRetriever;
std::string insertBeforeKeyword(
langutil::SourceLocation const& _location,
std::string const& _keyword,
std::string const& _expression
) const
{
auto _regex = std::regex{"(\\b" + _keyword + "\\b)"};
if (regex_search(text(_location), _regex))
return regex_replace(
text(_location),
_regex,
_expression + " " + _keyword,
std::regex_constants::format_first_only
);
else
solAssert(
false,
LocationHelper() << "Could not fix: " << text(_location) << " at " << _location <<
"\nNeeds to be fixed manually."
);
return "";
}
std::string insertAfterKeyword(
langutil::SourceLocation const& _location,
std::string const& _keyword,
std::string const& _expression
) const
{
bool isMultiline = SourceAnalysis{m_charStreamProvider}.isMultilineKeyword(_location, _keyword);
std::string toAppend = isMultiline ? ("\n " + _expression) : (" " + _expression);
std::regex keyword{"(\\b" + _keyword + "\\b)"};
if (regex_search(text(_location), keyword))
return regex_replace(text(_location), keyword, _keyword + toAppend);
else
solAssert(
false,
LocationHelper() << "Could not fix: " << text(_location) << " at " << _location <<
"\nNeeds to be fixed manually."
);
return "";
}
std::string insertAfterRightParenthesis(
langutil::SourceLocation const& _location,
std::string const& _expression
) const
{
auto _regex = std::regex{"(\\))"};
if (regex_search(text(_location), _regex))
return regex_replace(
text(_location),
std::regex{"(\\))"},
") " + _expression
);
else
solAssert(
false,
LocationHelper() << "Could not fix: " << text(_location) << " at " << _location <<
"\nNeeds to be fixed manually."
);
return "";
}
std::string replaceFunctionName(
langutil::SourceLocation const& _location,
std::string const& _name,
std::string const& _expression
) const
{
auto _regex = std::regex{ "(\\bfunction\\s*" + _name + "\\b)"};
if (regex_search(text(_location), _regex))
return regex_replace(
text(_location),
_regex,
_expression
);
else
solAssert(
false,
LocationHelper() << "Could not fix: " << text(_location) << " at " << _location <<
"\nNeeds to be fixed manually."
);
return "";
}
std::string gasUpdate(langutil::SourceLocation const& _location) const
{
std::regex gasReg{"\\.gas\\s*\\("};
if (regex_search(text(_location), gasReg))
{
std::string out = regex_replace(
text(_location),
gasReg,
"{gas: ",
std::regex_constants::format_first_only
);
return regex_replace(out, std::regex{"\\)$"}, "}");
}
else
solAssert(
false,
LocationHelper() << "Could not fix: " << text(_location) << " at " << _location <<
"\nNeeds to be fixed manually."
);
return "";
}
std::string valueUpdate(langutil::SourceLocation const& _location) const
{
std::regex valueReg{"\\.value\\s*\\("};
if (regex_search(text(_location), valueReg))
{
std::string out = regex_replace(
text(_location),
valueReg,
"{value: ",
std::regex_constants::format_first_only
);
return regex_replace(out, std::regex{"\\)$"}, "}");
}
else
solAssert(
false,
LocationHelper() << "Could not fix: " << text(_location) << " at " << _location <<
"\nNeeds to be fixed manually."
);
return "";
}
std::string nowUpdate(langutil::SourceLocation const& _location) const
{
return regex_replace(text(_location), std::regex{"now"}, "block.timestamp");
}
std::string removeVisibility(langutil::SourceLocation const& _location) const
{
std::string replacement = text(_location);
for (auto const& replace: {"public ", "public", "internal ", "internal", "external ", "external"})
replacement = regex_replace(replacement, std::regex{replace}, "");
return replacement;
}
};
}