2019-12-09 16:01:31 +00:00
|
|
|
/*
|
|
|
|
This file is part of solidity.
|
|
|
|
|
|
|
|
solidity is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
solidity is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <libsolidity/analysis/OverrideChecker.h>
|
|
|
|
|
|
|
|
#include <libsolidity/ast/AST.h>
|
|
|
|
|
2020-04-22 16:26:59 +00:00
|
|
|
#include <sstream>
|
2019-12-09 16:01:31 +00:00
|
|
|
#include <regex>
|
|
|
|
|
|
|
|
namespace solidity::tools
|
|
|
|
{
|
|
|
|
|
2020-04-22 16:26:59 +00:00
|
|
|
/**
|
|
|
|
* Helper for displaying location during asserts
|
|
|
|
*/
|
|
|
|
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(); }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2019-12-09 16:01:31 +00:00
|
|
|
/**
|
|
|
|
* Helper that provides functions which analyze certain source locations
|
|
|
|
* on a textual base. They utilize regular expression to search for
|
|
|
|
* keywords or to determine formatting.
|
|
|
|
*/
|
2020-04-24 10:10:04 +00:00
|
|
|
class SourceAnalysis
|
|
|
|
{
|
2019-12-09 16:01:31 +00:00
|
|
|
public:
|
|
|
|
static bool isMultilineKeyword(
|
|
|
|
langutil::SourceLocation const& _location,
|
|
|
|
std::string const& _keyword
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return regex_search(
|
|
|
|
_location.text(),
|
|
|
|
std::regex{"(\\b" + _keyword + "\\b\\n|\\r|\\r\\n)"}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool hasMutabilityKeyword(langutil::SourceLocation const& _location)
|
|
|
|
{
|
|
|
|
return regex_search(
|
|
|
|
_location.text(),
|
|
|
|
std::regex{"(\\b(pure|view|nonpayable|payable)\\b)"}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool hasVirtualKeyword(langutil::SourceLocation const& _location)
|
|
|
|
{
|
|
|
|
return regex_search(_location.text(), std::regex{"(\\b(virtual)\\b)"});
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool hasVisibilityKeyword(langutil::SourceLocation const& _location)
|
|
|
|
{
|
|
|
|
return regex_search(_location.text(), std::regex{"\\bpublic\\b"});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper that provides functions which can analyse declarations and
|
|
|
|
* generate source snippets based on the information retrieved.
|
|
|
|
*/
|
|
|
|
class SourceGeneration
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
using CompareFunction = frontend::OverrideChecker::CompareByID;
|
|
|
|
using Contracts = std::set<frontend::ContractDefinition const*, CompareFunction>;
|
|
|
|
|
|
|
|
/// Generates an `override` declaration for single overrides
|
|
|
|
/// or `override(...)` with contract list for multiple overrides.
|
|
|
|
static std::string functionOverride(Contracts const& _contracts)
|
|
|
|
{
|
|
|
|
if (_contracts.size() <= 1)
|
|
|
|
return "override";
|
|
|
|
|
|
|
|
std::string overrideList;
|
|
|
|
for (auto inheritedContract: _contracts)
|
|
|
|
overrideList += inheritedContract->name() + ",";
|
|
|
|
|
2020-04-24 10:10:04 +00:00
|
|
|
// Note: Can create incorrect replacements
|
2019-12-09 16:01:31 +00:00
|
|
|
return "override(" + overrideList.substr(0, overrideList.size() - 1) + ")";
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper that provides functions which apply changes to Solidity source code
|
|
|
|
* on a textual base. In general, these utilize regular expressions applied
|
|
|
|
* to the given source location.
|
|
|
|
*/
|
|
|
|
class SourceTransform
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
/// Searches for the keyword given and prepends the expression.
|
|
|
|
/// E.g. `function f() view;` -> `function f() public view;`
|
|
|
|
static std::string insertBeforeKeyword(
|
|
|
|
langutil::SourceLocation const& _location,
|
|
|
|
std::string const& _keyword,
|
|
|
|
std::string const& _expression
|
|
|
|
)
|
|
|
|
{
|
2020-04-24 10:10:04 +00:00
|
|
|
auto _regex = std::regex{"(\\b" + _keyword + "\\b)"};
|
|
|
|
if (regex_search(_location.text(), _regex))
|
|
|
|
return regex_replace(
|
|
|
|
_location.text(),
|
|
|
|
_regex,
|
|
|
|
_expression + " " + _keyword
|
|
|
|
);
|
|
|
|
else
|
|
|
|
solAssert(
|
|
|
|
false,
|
|
|
|
LocationHelper() << "Could not fix: " << _location.text() << " at " << _location <<
|
|
|
|
"\nNeeds to be fixed manually."
|
|
|
|
);
|
|
|
|
|
|
|
|
return "";
|
2019-12-09 16:01:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Searches for the keyword given and appends the expression.
|
|
|
|
/// E.g. `function f() public {}` -> `function f() public override {}`
|
|
|
|
static std::string insertAfterKeyword(
|
|
|
|
langutil::SourceLocation const& _location,
|
|
|
|
std::string const& _keyword,
|
|
|
|
std::string const& _expression
|
|
|
|
)
|
|
|
|
{
|
|
|
|
bool isMultiline = SourceAnalysis::isMultilineKeyword(_location, _keyword);
|
|
|
|
std::string toAppend = isMultiline ? ("\n " + _expression) : (" " + _expression);
|
|
|
|
std::regex keyword{"(\\b" + _keyword + "\\b)"};
|
|
|
|
|
2020-04-24 10:10:04 +00:00
|
|
|
if (regex_search(_location.text(), keyword))
|
|
|
|
return regex_replace(_location.text(), keyword, _keyword + toAppend);
|
|
|
|
else
|
|
|
|
solAssert(
|
|
|
|
false,
|
|
|
|
LocationHelper() << "Could not fix: " << _location.text() << " at " << _location <<
|
|
|
|
"\nNeeds to be fixed manually."
|
|
|
|
);
|
|
|
|
|
|
|
|
return "";
|
|
|
|
|
2019-12-09 16:01:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Searches for the first right parenthesis and appends the expression
|
|
|
|
/// given.
|
|
|
|
/// E.g. `function f() {}` -> `function f() public {}`
|
|
|
|
static std::string insertAfterRightParenthesis(
|
|
|
|
langutil::SourceLocation const& _location,
|
|
|
|
std::string const& _expression
|
|
|
|
)
|
|
|
|
{
|
2020-04-24 10:10:04 +00:00
|
|
|
auto _regex = std::regex{"(\\))"};
|
|
|
|
if (regex_search(_location.text(), _regex))
|
|
|
|
return regex_replace(
|
|
|
|
_location.text(),
|
|
|
|
std::regex{"(\\))"},
|
|
|
|
") " + _expression
|
|
|
|
);
|
|
|
|
else
|
|
|
|
solAssert(
|
|
|
|
false,
|
|
|
|
LocationHelper() << "Could not fix: " << _location.text() << " at " << _location <<
|
|
|
|
"\nNeeds to be fixed manually."
|
|
|
|
);
|
|
|
|
|
|
|
|
return "";
|
2019-12-09 16:01:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Searches for the `function` keyword and its identifier and replaces
|
|
|
|
/// both by the expression given.
|
|
|
|
/// E.g. `function Storage() {}` -> `constructor() {}`
|
|
|
|
static std::string replaceFunctionName(
|
|
|
|
langutil::SourceLocation const& _location,
|
|
|
|
std::string const& _name,
|
|
|
|
std::string const& _expression
|
|
|
|
)
|
|
|
|
{
|
2020-04-24 10:10:04 +00:00
|
|
|
auto _regex = std::regex{ "(\\bfunction\\s*" + _name + "\\b)"};
|
|
|
|
if (regex_search(_location.text(), _regex))
|
|
|
|
return regex_replace(
|
|
|
|
_location.text(),
|
|
|
|
_regex,
|
|
|
|
_expression
|
|
|
|
);
|
|
|
|
else
|
|
|
|
solAssert(
|
|
|
|
false,
|
|
|
|
LocationHelper() << "Could not fix: " << _location.text() << " at " << _location <<
|
|
|
|
"\nNeeds to be fixed manually."
|
|
|
|
);
|
|
|
|
|
|
|
|
return "";
|
2019-12-09 16:01:31 +00:00
|
|
|
}
|
2020-04-22 16:26:59 +00:00
|
|
|
|
|
|
|
static std::string gasUpdate(langutil::SourceLocation const& _location)
|
|
|
|
{
|
|
|
|
// dot, "gas", any number of whitespaces, left bracket
|
|
|
|
std::regex gasReg{"\\.gas\\s*\\("};
|
|
|
|
|
|
|
|
if (regex_search(_location.text(), gasReg))
|
|
|
|
{
|
|
|
|
std::string out = regex_replace(
|
|
|
|
_location.text(),
|
|
|
|
gasReg,
|
|
|
|
"{gas: ",
|
|
|
|
std::regex_constants::format_first_only
|
|
|
|
);
|
|
|
|
return regex_replace(out, std::regex{"\\)$"}, "}");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
solAssert(
|
|
|
|
false,
|
2020-04-24 10:10:04 +00:00
|
|
|
LocationHelper() << "Could not fix: " << _location.text() << " at " << _location <<
|
|
|
|
"\nNeeds to be fixed manually."
|
2020-04-22 16:26:59 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string valueUpdate(langutil::SourceLocation const& _location)
|
|
|
|
{
|
|
|
|
// dot, "value", any number of whitespaces, left bracket
|
|
|
|
std::regex valueReg{"\\.value\\s*\\("};
|
|
|
|
|
|
|
|
if (regex_search(_location.text(), valueReg))
|
|
|
|
{
|
|
|
|
std::string out = regex_replace(
|
|
|
|
_location.text(),
|
|
|
|
valueReg,
|
|
|
|
"{value: ",
|
|
|
|
std::regex_constants::format_first_only
|
|
|
|
);
|
|
|
|
return regex_replace(out, std::regex{"\\)$"}, "}");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
solAssert(
|
|
|
|
false,
|
2020-04-24 10:10:04 +00:00
|
|
|
LocationHelper() << "Could not fix: " << _location.text() << " at " << _location <<
|
|
|
|
"\nNeeds to be fixed manually."
|
2020-04-22 16:26:59 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
2020-05-05 11:04:01 +00:00
|
|
|
|
|
|
|
static std::string nowUpdate(langutil::SourceLocation const& _location)
|
|
|
|
{
|
|
|
|
std::regex nowRegex{"now"};
|
|
|
|
|
|
|
|
if (regex_search(_location.text(), nowRegex))
|
|
|
|
{
|
|
|
|
return regex_replace(_location.text(), nowRegex, "block.timestamp");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
solAssert(
|
|
|
|
false,
|
|
|
|
LocationHelper() << "Could not fix: " << _location.text() << " at " << _location <<
|
|
|
|
"\nNeeds to be fixed manually."
|
|
|
|
);
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
2019-12-09 16:01:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
}
|