Adds documentation for Solidity source upgrader.

This commit is contained in:
Erik Kundt 2019-12-09 17:01:31 +01:00 committed by chriseth
parent 5de3379d82
commit f2701db0aa
22 changed files with 1911 additions and 11 deletions

View File

@ -62,6 +62,11 @@ defaults:
path: build/solc/solc path: build/solc/solc
destination: solc destination: solc
# compiled tool executable target
- artifacts_tools: &artifacts_tools
path: build/tools/solidity-upgrade
destination: solidity-upgrade
# compiled executable targets # compiled executable targets
- artifacts_executables: &artifacts_executables - artifacts_executables: &artifacts_executables
root: build root: build
@ -324,6 +329,7 @@ jobs:
- checkout - checkout
- run: *run_build - run: *run_build
- store_artifacts: *artifacts_solc - store_artifacts: *artifacts_solc
- store_artifacts: *artifacts_tools
- persist_to_workspace: *artifacts_executables - persist_to_workspace: *artifacts_executables
b_ubu_release: &build_ubuntu1904_release b_ubu_release: &build_ubuntu1904_release
@ -455,6 +461,7 @@ jobs:
- /usr/local/Homebrew - /usr/local/Homebrew
- run: *run_build - run: *run_build
- store_artifacts: *artifacts_solc - store_artifacts: *artifacts_solc
- store_artifacts: *artifacts_tools
- persist_to_workspace: *artifacts_build_dir - persist_to_workspace: *artifacts_build_dir
t_osx_soltest: t_osx_soltest:

View File

@ -55,6 +55,7 @@ add_subdirectory(libevmasm)
add_subdirectory(libyul) add_subdirectory(libyul)
add_subdirectory(libsolidity) add_subdirectory(libsolidity)
add_subdirectory(libsolc) add_subdirectory(libsolc)
add_subdirectory(tools)
if (NOT EMSCRIPTEN) if (NOT EMSCRIPTEN)
add_subdirectory(solc) add_subdirectory(solc)

View File

@ -474,3 +474,240 @@ Error types
11. ``CompilerError``: Invalid use of the compiler stack - this should be reported as an issue. 11. ``CompilerError``: Invalid use of the compiler stack - this should be reported as an issue.
12. ``FatalError``: Fatal error not processed correctly - this should be reported as an issue. 12. ``FatalError``: Fatal error not processed correctly - this should be reported as an issue.
13. ``Warning``: A warning, which didn't stop the compilation, but should be addressed if possible. 13. ``Warning``: A warning, which didn't stop the compilation, but should be addressed if possible.
.. _compiler-tools:
Compiler tools
**************
solidity-upgrade
----------------
``solidity-upgrade`` can help you to semi-automatically upgrade your contracts
to breaking language changes. While it does not and cannot implement all
required changes for every breaking release, it still supports the ones, that
would need plenty of repetitive manual adjustments otherwise.
.. note::
``solidity-upgrade`` carries out a large part of the work, but your
contracts will most likely need further manual adjustments. We recommend
using a version control system for your files. This helps reviewing and
eventually rolling back the changes made.
.. warning::
``solidity-upgrade`` is not considered to be complete or free from bugs, so
please use with care.
How it works
~~~~~~~~~~~~
You can pass (a) Solidity source file(s) to ``solidity-upgrade [files]``. If
these make use of ``import`` statement which refer to files outside the
current source file's directory, you need to specify directories that
are allowed to read and import files from, by passing
``--allow-paths [directory]``. You can ignore missing files by passing
``--ignore-missing``.
``solidity-upgrade`` is based on ``libsolidity`` and can parse, compile and
analyse your source files, and might find applicable source upgrades in them.
Source upgrades are considered to be small textual changes to your source code.
They are applied to an in-memory representation of the source files
given. The corresponding source file is updated by default, but you can pass
``--dry-run`` to simulate to whole upgrade process without writing to any file.
The upgrade process itself has two phases. In the first phase source files are
parsed, and since it is not possible to upgrade source code on that level,
errors are collected and can be logged by passing ``--verbose``. No source
upgrades available at this point.
In the second phase, all sources are compiled and all activated upgrade analysis
modules are run alongside compilation. By default, all available modules are
activated. Please read the documentation on
:ref:`available modules <upgrade-modules>` for further details.
This can result in compilation errors that may
be fixed by source upgrades. If no errors occur, no source upgrades are being
reported and you're done.
If errors occur and some upgrade module reported a source upgrade, the first
reported one gets applied and compilation is triggered again for all given
source files. The previous step is repeated as long as source upgrades are
reported. If errors still occur, you can log them by passing ``--verbose``.
If no errors occur, your contracts are up to date and can be compiled with
the latest version of the compiler.
.. _upgrade-modules:
Available upgrade modules
~~~~~~~~~~~~~~~~~~~~~~~~~
+-----------------+---------+--------------------------------------------------+
| Module | Version | Description |
+=================+=========+==================================================+
| ``constructor`` | 0.5.0 | Constructors must now be defined using the |
| | | ``constructor`` keyword. |
+-----------------+---------+--------------------------------------------------+
| ``visibility`` | 0.5.0 | Explicit function visibility is now mandatory, |
| | | defaults to ``public``. |
+-----------------+---------+--------------------------------------------------+
| ``abstract`` | 0.6.0 | The keyword ``abstract`` has to be used if a |
| | | contract does not implement all its functions. |
+-----------------+---------+--------------------------------------------------+
| ``virtual`` | 0.6.0 | Functions without implementation outside an |
| | | interface have to be marked ``virtual``. |
+-----------------+---------+--------------------------------------------------+
| ``override`` | 0.6.0 | When overriding a function or modifier, the new |
| | | keyword ``override`` must be used. |
+-----------------+---------+--------------------------------------------------+
Please read :doc:`0.5.0 release notes <050-breaking-changes>` and
:doc:`0.6.0 release notes <060-breaking-changes>` for further details.
Synopsis
~~~~~~~~
.. code-block:: none
Usage: solidity-upgrade [options] contract.sol
Allowed options:
--help Show help message and exit.
--version Show version and exit.
--allow-paths path(s)
Allow a given path for imports. A list of paths can be
supplied by separating them with a comma.
--ignore-missing Ignore missing files.
--modules module(s) Only activate a specific upgrade module. A list of
modules can be supplied by separating them with a comma.
--dry-run Apply changes in-memory only and don't write to input
file.
--verbose Print logs, errors and changes. Shortens output of
upgrade patches.
--unsafe Accept *unsafe* changes.
Bug Reports / Feature requests
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you found a bug or if you have a feature request, please
`file an issue <https://github.com/ethereum/solidity/issues/new/choose>`_ on Github.
Example
~~~~~~~
Assume you have the following contracts you want to update declared in ``Source.sol``:
.. code-block:: none
// This will not compile
pragma solidity >0.4.23;
contract Updateable {
function run() public view returns (bool);
function update() public;
}
contract Upgradable {
function run() public view returns (bool);
function upgrade();
}
contract Source is Updateable, Upgradable {
function Source() public {}
function run()
public
view
returns (bool) {}
function update() {}
function upgrade() {}
}
Required changes
^^^^^^^^^^^^^^^^
To bring the contracts up to date with the current Solidity version, the
following upgrade modules have to be executed: ``constructor``,
``visibility``, ``abstract``, ``override`` and ``virtual``. Please read the
documentation on :ref:`available modules <upgrade-modules>` for further details.
Running the upgrade
^^^^^^^^^^^^^^^^^^^
In this example, all modules needed to upgrade the contracts above,
are available and all of them are activated by default. Therefore you
do not need to specify the ``--modules`` option.
.. code-block:: none
$ solidity-upgrade Source.sol --dry-run
.. code-block:: none
Running analysis (and upgrade) on given source files.
..............
After upgrade:
Found 0 errors.
Found 0 upgrades.
The above performs a dry-ran upgrade on the given file and logs statistics after all.
In this case, the upgrade was successful and no further adjustments are needed.
Finally, you can run the upgrade and also write to the source file.
.. code-block:: none
$ solidity-upgrade Source.sol
.. code-block:: none
Running analysis (and upgrade) on given source files.
..............
After upgrade:
Found 0 errors.
Found 0 upgrades.
Review changes
^^^^^^^^^^^^^^
The command above applies all changes as shown below. Please review them carefully.
.. code-block:: none
pragma solidity >0.4.23;
abstract contract Updateable {
function run() public view virtual returns (bool);
function update() public virtual;
}
abstract contract Upgradable {
function run() public view virtual returns (bool);
function upgrade() public virtual;
}
contract Source is Updateable, Upgradable {
constructor() public {}
function run()
public
view
override(Updateable,Upgradable)
returns (bool) {}
function update() public override {}
function upgrade() public override {}
}

View File

@ -144,8 +144,8 @@ vector<ContractDefinition const*> resolveDirectBaseContracts(ContractDefinition
Declaration const* baseDecl = Declaration const* baseDecl =
specifier->name().annotation().referencedDeclaration; specifier->name().annotation().referencedDeclaration;
auto contract = dynamic_cast<ContractDefinition const*>(baseDecl); auto contract = dynamic_cast<ContractDefinition const*>(baseDecl);
solAssert(contract, "contract is null"); if (contract)
resolvedContracts.emplace_back(contract); resolvedContracts.emplace_back(contract);
} }
return resolvedContracts; return resolvedContracts;

View File

@ -129,6 +129,7 @@ private:
class OverrideChecker class OverrideChecker
{ {
public: public:
using OverrideProxyBySignatureMultiSet = std::multiset<OverrideProxy, OverrideProxy::CompareBySignature>;
/// @param _errorReporter provides the error logging functionality. /// @param _errorReporter provides the error logging functionality.
explicit OverrideChecker(langutil::ErrorReporter& _errorReporter): explicit OverrideChecker(langutil::ErrorReporter& _errorReporter):
@ -137,12 +138,17 @@ public:
void check(ContractDefinition const& _contract); void check(ContractDefinition const& _contract);
private:
struct CompareByID struct CompareByID
{ {
bool operator()(ContractDefinition const* _a, ContractDefinition const* _b) const; bool operator()(ContractDefinition const* _a, ContractDefinition const* _b) const;
}; };
/// Returns all functions of bases (including public state variables) that have not yet been overwritten.
/// May contain the same function multiple times when used with shared bases.
OverrideProxyBySignatureMultiSet const& inheritedFunctions(ContractDefinition const& _contract) const;
OverrideProxyBySignatureMultiSet const& inheritedModifiers(ContractDefinition const& _contract) const;
private:
void checkIllegalOverrides(ContractDefinition const& _contract); void checkIllegalOverrides(ContractDefinition const& _contract);
/// Performs various checks related to @a _overriding overriding @a _super like /// Performs various checks related to @a _overriding overriding @a _super like
/// different return type, invalid visibility change, etc. /// different return type, invalid visibility change, etc.
@ -174,15 +180,8 @@ private:
/// Resolves an override list of UserDefinedTypeNames to a list of contracts. /// Resolves an override list of UserDefinedTypeNames to a list of contracts.
std::set<ContractDefinition const*, CompareByID> resolveOverrideList(OverrideSpecifier const& _overrides) const; std::set<ContractDefinition const*, CompareByID> resolveOverrideList(OverrideSpecifier const& _overrides) const;
using OverrideProxyBySignatureMultiSet = std::multiset<OverrideProxy, OverrideProxy::CompareBySignature>;
void checkOverrideList(OverrideProxy _item, OverrideProxyBySignatureMultiSet const& _inherited); void checkOverrideList(OverrideProxy _item, OverrideProxyBySignatureMultiSet const& _inherited);
/// Returns all functions of bases (including public state variables) that have not yet been overwritten.
/// May contain the same function multiple times when used with shared bases.
OverrideProxyBySignatureMultiSet const& inheritedFunctions(ContractDefinition const& _contract) const;
OverrideProxyBySignatureMultiSet const& inheritedModifiers(ContractDefinition const& _contract) const;
langutil::ErrorReporter& m_errorReporter; langutil::ErrorReporter& m_errorReporter;
/// Cache for inheritedFunctions(). /// Cache for inheritedFunctions().

View File

@ -797,7 +797,7 @@ public:
{ {
return return
CallableDeclaration::virtualSemantics() || CallableDeclaration::virtualSemantics() ||
annotation().contract->isInterface(); (annotation().contract && annotation().contract->isInterface());
} }
private: private:
StateMutability m_stateMutability; StateMutability m_stateMutability;

14
tools/CMakeLists.txt Normal file
View File

@ -0,0 +1,14 @@
add_executable(solidity-upgrade
solidityUpgrade/main.cpp
solidityUpgrade/UpgradeChange.h
solidityUpgrade/UpgradeChange.cpp
solidityUpgrade/UpgradeSuite.h
solidityUpgrade/Upgrade050.cpp
solidityUpgrade/Upgrade060.cpp
solidityUpgrade/SourceTransform.h
solidityUpgrade/SourceUpgrade.cpp
)
target_link_libraries(solidity-upgrade PRIVATE solidity Boost::boost Boost::program_options Boost::system)
include(GNUInstallDirs)
install(TARGETS solidity-upgrade DESTINATION "${CMAKE_INSTALL_BINDIR}")

View File

@ -0,0 +1,159 @@
/*
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>
#include <regex>
namespace solidity::tools
{
/**
* 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.
*/
class SourceAnalysis {
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() + ",";
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
)
{
return regex_replace(
_location.text(),
std::regex{"(\\b" + _keyword + "\\b)"},
_expression + " " + _keyword
);
}
/// 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)"};
return regex_replace(_location.text(), keyword, _keyword + toAppend);
}
/// 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
)
{
return regex_replace(
_location.text(),
std::regex{"(\\))"},
") " + _expression
);
}
/// 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
)
{
return regex_replace(
_location.text(),
std::regex{"(\\bfunction\\s*" + _name + "\\b)"},
_expression
);
}
};
}

View File

@ -0,0 +1,525 @@
/*
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 <tools/solidityUpgrade/SourceUpgrade.h>
#include <liblangutil/Exceptions.h>
#include <liblangutil/SourceReferenceFormatterHuman.h>
#include <libsolidity/ast/AST.h>
#include <boost/filesystem.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/algorithm/string.hpp>
namespace po = boost::program_options;
namespace fs = boost::filesystem;
using namespace solidity;
using namespace solidity::langutil;
using namespace solidity::tools;
using namespace solidity::util;
using namespace solidity::frontend;
using namespace std;
static string const g_argHelp = "help";
static string const g_argVersion = "version";
static string const g_argInputFile = "input-file";
static string const g_argModules = "modules";
static string const g_argDryRun = "dry-run";
static string const g_argUnsafe = "unsafe";
static string const g_argVerbose = "verbose";
static string const g_argIgnoreMissingFiles = "ignore-missing";
static string const g_argAllowPaths = "allow-paths";
namespace
{
ostream& out()
{
return cout;
}
AnsiColorized log()
{
return AnsiColorized(cout, true, {});
}
AnsiColorized success()
{
return AnsiColorized(cout, true, {formatting::CYAN});
}
AnsiColorized warning()
{
return AnsiColorized(cout, true, {formatting::YELLOW});
}
AnsiColorized error()
{
return AnsiColorized(cout, true, {formatting::MAGENTA});
}
void logVersion()
{
/// TODO Replace by variable that can be set during build.
out() << "0.1.0" << endl;
}
void logProgress()
{
out() << ".";
out().flush();
}
}
bool SourceUpgrade::parseArguments(int _argc, char** _argv)
{
po::options_description desc(R"(solidity-upgrade, the Solidity upgrade assistant.
The solidity-upgrade tool can help upgrade smart contracts to breaking language features.
It does not support all breaking changes for each version, but will hopefully assist
upgrading your contracts to the desired Solidity version.
solidity-upgrade is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY. Please be careful when running upgrades on
your contracts.
Usage: solidity-upgrade [options] contract.sol
Allowed options)",
po::options_description::m_default_line_length,
po::options_description::m_default_line_length - 23
);
desc.add_options()
(g_argHelp.c_str(), "Show help message and exit.")
(g_argVersion.c_str(), "Show version and exit.")
(
g_argAllowPaths.c_str(),
po::value<string>()->value_name("path(s)"),
"Allow a given path for imports. A list of paths can be supplied by separating them "
"with a comma."
)
(g_argIgnoreMissingFiles.c_str(), "Ignore missing files.")
(
g_argModules.c_str(),
po::value<string>()->value_name("module(s)"),
"Only activate a specific upgrade module. A list of "
"modules can be supplied by separating them with a comma."
)
(g_argDryRun.c_str(), "Apply changes in-memory only and don't write to input file.")
(g_argVerbose.c_str(), "Print logs, errors and changes. Shortens output of upgrade patches.")
(g_argUnsafe.c_str(), "Accept *unsafe* changes.");
po::options_description allOptions = desc;
allOptions.add_options()("input-file", po::value<vector<string>>(), "input file");
po::positional_options_description filesPositions;
filesPositions.add("input-file", -1);
// parse the compiler arguments
try
{
po::command_line_parser cmdLineParser(_argc, _argv);
cmdLineParser.style(
po::command_line_style::default_style & (~po::command_line_style::allow_guessing)
);
cmdLineParser.options(allOptions).positional(filesPositions);
po::store(cmdLineParser.run(), m_args);
}
catch (po::error const& _exception)
{
error() << _exception.what() << endl;
return false;
}
if (m_args.count(g_argHelp) || (isatty(fileno(stdin)) && _argc == 1))
{
out() << endl;
log() << desc;
return false;
}
if (m_args.count(g_argVersion))
{
logVersion();
return false;
}
if (m_args.count(g_argModules))
{
vector<string> moduleArgs;
auto modules = boost::split(
moduleArgs, m_args[g_argModules].as<string>(), boost::is_any_of(",")
);
/// All modules are activated by default. Clear them before activating single ones.
m_suite.deactivateModules();
for (string const& module: modules)
{
if (module == "constructor")
m_suite.activateModule(Module::ConstructorKeyword);
else if (module == "visibility")
m_suite.activateModule(Module::VisibilitySpecifier);
else if (module == "abstract")
m_suite.activateModule(Module::AbstractContract);
else if (module == "override")
m_suite.activateModule(Module::OverridingFunction);
else if (module == "virtual")
m_suite.activateModule(Module::VirtualFunction);
else
{
error() << "Unknown upgrade module \"" + module + "\"" << endl;
return false;
}
}
}
/// TODO Share with solc commandline interface.
if (m_args.count(g_argAllowPaths))
{
vector<string> paths;
auto allowedPaths = boost::split(
paths, m_args[g_argAllowPaths].as<string>(), boost::is_any_of(",")
);
for (string const& path: allowedPaths)
{
auto filesystem_path = boost::filesystem::path(path);
/// If the given path had a trailing slash, the Boost filesystem
/// path will have it's last component set to '.'. This breaks
/// path comparison in later parts of the code, so we need to strip
/// it.
if (filesystem_path.filename() == ".")
filesystem_path.remove_filename();
m_allowedDirectories.push_back(filesystem_path);
}
}
return true;
}
void SourceUpgrade::printPrologue()
{
out() << endl;
out() << endl;
log() <<
"solidity-upgrade does not support all breaking changes for each version." <<
endl <<
"Please run `solidity-upgrade --help` and get a list of implemented upgrades." <<
endl <<
endl;
log() <<
"Running analysis (and upgrade) on given source files." <<
endl;
}
bool SourceUpgrade::processInput()
{
if (!readInputFiles())
return false;
resetCompiler(fileReader());
tryCompile();
runUpgrade();
printStatistics();
return true;
}
void SourceUpgrade::tryCompile() const
{
bool verbose = m_args.count(g_argVerbose);
if (verbose)
log() << "Running compilation phases." << endl << endl;
else
logProgress();
try
{
if (m_compiler->parse())
{
if (m_compiler->analyze())
m_compiler->compile();
else
if (verbose)
{
error() <<
"Compilation errors that solidity-upgrade may resolve occurred." <<
endl <<
endl;
printErrors();
}
}
else
if (verbose)
{
error() <<
"Compilation errors that solidity-upgrade cannot resolve occurred." <<
endl <<
endl;
printErrors();
}
}
catch (Exception const& _exception)
{
error() << "Exception during compilation: " << boost::diagnostic_information(_exception) << endl;
}
catch (std::exception const& _e)
{
error() << (_e.what() ? ": " + string(_e.what()) : ".") << endl;
}
catch (...)
{
error() << "Unknown exception during compilation." << endl;
}
}
void SourceUpgrade::runUpgrade()
{
bool recompile = true;
while (recompile && !m_compiler->errors().empty())
{
for (auto& sourceCode: m_sourceCodes)
{
recompile = analyzeAndUpgrade(sourceCode);
if (recompile)
break;
}
if (recompile)
{
m_suite.reset();
resetCompiler();
tryCompile();
}
}
}
bool SourceUpgrade::analyzeAndUpgrade(pair<string, string> const& _sourceCode)
{
bool applyUnsafe = m_args.count(g_argUnsafe);
bool verbose = m_args.count(g_argVerbose);
if (verbose)
log() << "Analyzing and upgrading " << _sourceCode.first << "." << endl;
if (m_compiler->state() >= CompilerStack::State::AnalysisPerformed)
m_suite.analyze(m_compiler->ast(_sourceCode.first));
if (!m_suite.changes().empty())
{
auto& change = m_suite.changes().front();
if (verbose)
change.log(true);
if (change.level() == UpgradeChange::Level::Safe)
{
applyChange(_sourceCode, change);
return true;
}
else if (change.level() == UpgradeChange::Level::Unsafe)
{
if (applyUnsafe)
{
applyChange(_sourceCode, change);
return true;
}
}
}
return false;
}
void SourceUpgrade::applyChange(
pair<string, string> const& _sourceCode,
UpgradeChange& _change
)
{
bool dryRun = m_args.count(g_argDryRun);
bool verbose = m_args.count(g_argVerbose);
if (verbose)
{
log() << "Applying change to " << _sourceCode.first << endl << endl;
log() << _change.patch();
}
_change.apply();
m_sourceCodes[_sourceCode.first] = _change.source();
if (!dryRun)
writeInputFile(_sourceCode.first, _change.source());
}
void SourceUpgrade::printErrors() const
{
auto formatter = make_unique<langutil::SourceReferenceFormatterHuman>(cout, true);
for (auto const& error: m_compiler->errors())
if (error->type() != langutil::Error::Type::Warning)
formatter->printErrorInformation(*error);
}
void SourceUpgrade::printStatistics() const
{
out() << endl;
out() << endl;
out() << "After upgrade:" << endl;
out() << endl;
error() << "Found " << m_compiler->errors().size() << " errors." << endl;
success() << "Found " << m_suite.changes().size() << " upgrades." << endl;
}
bool SourceUpgrade::readInputFiles()
{
bool ignoreMissing = m_args.count(g_argIgnoreMissingFiles);
/// TODO Share with solc commandline interface.
if (m_args.count(g_argInputFile))
for (string path: m_args[g_argInputFile].as<vector<string>>())
{
auto infile = boost::filesystem::path(path);
if (!boost::filesystem::exists(infile))
{
if (!ignoreMissing)
{
error() << infile << " is not found." << endl;
return false;
}
else
error() << infile << " is not found. Skipping." << endl;
continue;
}
if (!boost::filesystem::is_regular_file(infile))
{
if (!ignoreMissing)
{
error() << infile << " is not a valid file." << endl;
return false;
}
else
error() << infile << " is not a valid file. Skipping." << endl;
continue;
}
m_sourceCodes[infile.generic_string()] = readFileAsString(infile.string());
path = boost::filesystem::canonical(infile).string();
}
if (m_sourceCodes.size() == 0)
{
warning() << "No input files given. If you wish to use the standard input please specify \"-\" explicitly." << endl;
return false;
}
return true;
}
bool SourceUpgrade::writeInputFile(string const& _path, string const& _source)
{
bool verbose = m_args.count(g_argVerbose);
if (verbose)
{
out() << endl;
log() << "Writing to input file " << _path << "." << endl;
}
ofstream file(_path, ios::trunc);
file << _source;
return true;
}
ReadCallback::Callback SourceUpgrade::fileReader()
{
/// TODO Share with solc commandline interface.
ReadCallback::Callback fileReader = [this](string const&, string const& _path)
{
try
{
auto path = boost::filesystem::path(_path);
auto canonicalPath = boost::filesystem::weakly_canonical(path);
bool isAllowed = false;
for (auto const& allowedDir: m_allowedDirectories)
{
// If dir is a prefix of boostPath, we are fine.
if (
std::distance(allowedDir.begin(), allowedDir.end()) <= std::distance(canonicalPath.begin(), canonicalPath.end()) &&
std::equal(allowedDir.begin(), allowedDir.end(), canonicalPath.begin())
)
{
isAllowed = true;
break;
}
}
if (!isAllowed)
return ReadCallback::Result{false, "File outside of allowed directories."};
if (!boost::filesystem::exists(canonicalPath))
return ReadCallback::Result{false, "File not found."};
if (!boost::filesystem::is_regular_file(canonicalPath))
return ReadCallback::Result{false, "Not a valid file."};
auto contents = readFileAsString(canonicalPath.string());
m_sourceCodes[path.generic_string()] = contents;
return ReadCallback::Result{true, contents};
}
catch (Exception const& _exception)
{
return ReadCallback::Result{false, "Exception in read callback: " + boost::diagnostic_information(_exception)};
}
catch (...)
{
return ReadCallback::Result{false, "Unknown exception in read callback."};
}
};
return fileReader;
}
void SourceUpgrade::resetCompiler()
{
m_compiler->reset();
m_compiler->setSources(m_sourceCodes);
m_compiler->setParserErrorRecovery(true);
}
void SourceUpgrade::resetCompiler(ReadCallback::Callback const& _callback)
{
m_compiler.reset(new CompilerStack(_callback));
m_compiler->setSources(m_sourceCodes);
m_compiler->setParserErrorRecovery(true);
}

View File

@ -0,0 +1,158 @@
/*
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/>.
*/
#pragma once
#include <tools/solidityUpgrade/UpgradeChange.h>
#include <tools/solidityUpgrade/Upgrade050.h>
#include <tools/solidityUpgrade/Upgrade060.h>
#include <libsolidity/interface/CompilerStack.h>
#include <libsolidity/interface/DebugSettings.h>
#include <libyul/AssemblyStack.h>
#include <liblangutil/EVMVersion.h>
#include <boost/program_options.hpp>
#include <boost/filesystem/path.hpp>
#include <memory>
namespace solidity::tools
{
/**
* The Solidity source upgrade tool. It supplies a command line interface
* and connects this to a compiler stack that the upgrade logic is facilitated
* with.
*/
class SourceUpgrade
{
public:
/// Parse command line arguments and return false in case of a failure.
bool parseArguments(int _argc, char** _argv);
/// Prints additional information on the upgrade tool.
void printPrologue();
/// Parse / compile files and runs upgrade analysis on them.
bool processInput();
private:
/// All available upgrade modules
enum class Module
{
ConstructorKeyword,
VisibilitySpecifier,
AbstractContract,
OverridingFunction,
VirtualFunction
};
/// Upgrade suite that hosts all available modules.
class Suite: public UpgradeSuite
{
public:
void analyze(frontend::SourceUnit const& _sourceUnit)
{
/// Solidity 0.5.0
if (isActivated(Module::ConstructorKeyword))
ConstructorKeyword{m_changes}.analyze(_sourceUnit);
if (isActivated(Module::VisibilitySpecifier))
VisibilitySpecifier{m_changes}.analyze(_sourceUnit);
/// Solidity 0.6.0
if (isActivated(Module::AbstractContract))
AbstractContract{m_changes}.analyze(_sourceUnit);
if (isActivated(Module::OverridingFunction))
OverridingFunction{m_changes}.analyze(_sourceUnit);
if (isActivated(Module::VirtualFunction))
VirtualFunction{m_changes}.analyze(_sourceUnit);
}
void activateModule(Module _module) { m_modules.insert(_module); }
void deactivateModules() { m_modules.clear(); }
private:
bool isActivated(Module _module) const
{
return m_modules.find(_module) != m_modules.end();
}
/// All modules are activated by default. Clear them before activating
/// single ones.
std::set<Module> m_modules = {
Module::ConstructorKeyword,
Module::VisibilitySpecifier,
Module::AbstractContract,
Module::OverridingFunction,
Module::VirtualFunction
};
};
/// Parses the current sources and runs analyses as well as compilation on
/// them if parsing was successful.
void tryCompile() const;
/// Analyses and upgrades the sources given. The upgrade happens in a loop,
/// applying one change at a time, which is run until no applicable changes
/// are found any more. Only one change is done at a time and all sources
/// are being compiled again after each change.
void runUpgrade();
/// Runs upgrade analysis on source and applies upgrades changes to it.
/// Returns `true` if there're still changes that can be applied,
/// `false` otherwise.
bool analyzeAndUpgrade(
std::pair<std::string, std::string> const& _sourceCode
);
/// Applies the change given to its source code. If no `--dry-run` was
/// passed via the commandline, the upgraded source code is written back
/// to its file.
void applyChange(
std::pair<std::string, std::string> const& _sourceCode,
UpgradeChange& _change
);
/// Prints all errors (excluding warnings) the compiler currently reported.
void printErrors() const;
/// Prints error and upgrade overview at the end of each full run.
void printStatistics() const;
/// Reads all input files given and stores sources in the internal data
/// structure. Reports errors if files cannot be found.
bool readInputFiles();
/// Writes source to file given.
bool writeInputFile(std::string const& _path, std::string const& _source);
/// Returns a file reader function that fills `m_sources`.
frontend::ReadCallback::Callback fileReader();
/// Resets the compiler stack and configures sources to compile.
/// Also enables error recovery.
void resetCompiler();
/// Resets the compiler stack and configures sources to compile.
/// Also enables error recovery. Passes read callback to the compiler stack.
void resetCompiler(frontend::ReadCallback::Callback const& _callback);
/// Compiler arguments variable map
boost::program_options::variables_map m_args;
/// Map of input files to source code strings
std::map<std::string, std::string> m_sourceCodes;
/// Solidity compiler stack
std::unique_ptr<frontend::CompilerStack> m_compiler;
/// List of allowed directories to read files from
std::vector<boost::filesystem::path> m_allowedDirectories;
/// Holds all upgrade modules and source upgrades.
Suite m_suite;
};
}

View File

@ -0,0 +1,58 @@
/*
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 <tools/solidityUpgrade/Upgrade050.h>
#include <tools/solidityUpgrade/SourceTransform.h>
#include <libsolidity/analysis/OverrideChecker.h>
#include <libyul/AsmData.h>
#include <regex>
using namespace std;
using namespace solidity;
using namespace solidity::frontend;
using namespace solidity::tools;
void ConstructorKeyword::endVisit(ContractDefinition const& _contract)
{
for (auto const* function: _contract.definedFunctions())
if (function->name() == _contract.name())
m_changes.push_back(
UpgradeChange{
UpgradeChange::Level::Safe,
function->location(),
SourceTransform::replaceFunctionName(
function->location(),
function->name(),
"constructor"
)
}
);
}
void VisibilitySpecifier::endVisit(FunctionDefinition const& _function)
{
if (_function.noVisibilitySpecified())
m_changes.push_back(
UpgradeChange{
UpgradeChange::Level::Safe,
_function.location(),
SourceTransform::insertAfterRightParenthesis(_function.location(), "public")
}
);
}

View File

@ -0,0 +1,58 @@
/*
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/>.
*/
#pragma once
#include <tools/solidityUpgrade/UpgradeChange.h>
#include <tools/solidityUpgrade/UpgradeSuite.h>
#include <libsolidity/ast/ASTVisitor.h>
namespace solidity::tools
{
/**
* Module that performs analysis on the AST. It visits all contract
* definitions and its defined functions and reports a source upgrade,
* if one of the declared functions is the constructor but does not
* use the `constructor` keyword.
*/
class ConstructorKeyword: public AnalysisUpgrade
{
public:
using AnalysisUpgrade::AnalysisUpgrade;
void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); }
private:
void endVisit(frontend::ContractDefinition const& _contract);
};
/**
* Module that performs analysis on the AST. It visits function definitions
* and reports a source upgrade, if this function's visibility is `public`,
* but not marked explicitly as such.
*/
class VisibilitySpecifier: public AnalysisUpgrade
{
public:
using AnalysisUpgrade::AnalysisUpgrade;
void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); }
private:
void endVisit(frontend::FunctionDefinition const& _function);
};
}

View File

@ -0,0 +1,212 @@
/*
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 <tools/solidityUpgrade/Upgrade060.h>
#include <tools/solidityUpgrade/SourceTransform.h>
#include <libsolidity/analysis/OverrideChecker.h>
#include <libyul/AsmData.h>
#include <regex>
using namespace std;
using namespace solidity;
using namespace solidity::frontend;
using namespace solidity::tools;
using Contracts = set<ContractDefinition const*, OverrideChecker::CompareByID>;
namespace
{
inline string appendOverride(
FunctionDefinition const& _function,
Contracts const& _expectedContracts
)
{
auto location = _function.location();
string upgradedCode;
string overrideExpression = SourceGeneration::functionOverride(_expectedContracts);
if (SourceAnalysis::hasVirtualKeyword(location))
upgradedCode = SourceTransform::insertAfterKeyword(
location,
"virtual",
overrideExpression
);
else if (SourceAnalysis::hasMutabilityKeyword(location))
upgradedCode = SourceTransform::insertAfterKeyword(
location,
stateMutabilityToString(_function.stateMutability()),
overrideExpression
);
else if (SourceAnalysis::hasVisibilityKeyword(location))
upgradedCode = SourceTransform::insertAfterKeyword(
location,
Declaration::visibilityToString(_function.visibility()),
overrideExpression
);
else
upgradedCode = SourceTransform::insertAfterRightParenthesis(
location,
overrideExpression
);
return upgradedCode;
}
inline string appendVirtual(FunctionDefinition const& _function)
{
auto location = _function.location();
string upgradedCode;
if (SourceAnalysis::hasMutabilityKeyword(location))
upgradedCode = SourceTransform::insertAfterKeyword(
location,
stateMutabilityToString(_function.stateMutability()),
"virtual"
);
else if (SourceAnalysis::hasVisibilityKeyword(location))
upgradedCode = SourceTransform::insertAfterKeyword(
location,
Declaration::visibilityToString(_function.visibility()),
"virtual"
);
else
upgradedCode = SourceTransform::insertAfterRightParenthesis(
_function.location(),
"virtual"
);
return upgradedCode;
}
}
void AbstractContract::endVisit(ContractDefinition const& _contract)
{
bool isFullyImplemented = _contract.annotation().unimplementedFunctions.empty();
if (
!isFullyImplemented &&
!_contract.abstract() &&
!_contract.isInterface()
)
m_changes.push_back(
UpgradeChange{
UpgradeChange::Level::Safe,
_contract.location(),
SourceTransform::insertBeforeKeyword(_contract.location(), "contract", "abstract")
}
);
}
void OverridingFunction::endVisit(ContractDefinition const& _contract)
{
auto const& inheritedFunctions = m_overrideChecker.inheritedFunctions(_contract);
for (auto const* function: _contract.definedFunctions())
{
Contracts expectedContracts;
OverrideProxy proxy{function};
if (!function->isConstructor())
{
/// Build list of contracts expected to be mentioned in the override list (if any).
for (auto [begin, end] = inheritedFunctions.equal_range(proxy); begin != end; begin++)
expectedContracts.insert(&begin->contract());
/// Add override with contract list, if needed.
if (!function->overrides() && expectedContracts.size() > 1)
m_changes.push_back(
UpgradeChange{
UpgradeChange::Level::Safe,
function->location(),
appendOverride(*function, expectedContracts)
}
);
for (auto [begin, end] = inheritedFunctions.equal_range(proxy); begin != end; begin++)
{
auto& super = (*begin);
auto functionType = FunctionType(*function).asCallableFunction(false);
auto superType = super.functionType()->asCallableFunction(false);
if (functionType && functionType->hasEqualParameterTypes(*superType))
{
/// If function does not specify override and no override with
/// contract list was added before.
if (!function->overrides() && expectedContracts.size() <= 1)
m_changes.push_back(
UpgradeChange{
UpgradeChange::Level::Safe,
function->location(),
appendOverride(*function, expectedContracts)
}
);
}
}
}
}
}
void VirtualFunction::endVisit(ContractDefinition const& _contract)
{
auto const& inheritedFunctions = m_overrideChecker.inheritedFunctions(_contract);
for (FunctionDefinition const* function: _contract.definedFunctions())
{
OverrideProxy proxy{function};
if (!function->isConstructor())
{
if (
!function->markedVirtual() &&
!function->isImplemented() &&
!function->virtualSemantics() &&
function->visibility() > Visibility::Private
)
{
m_changes.push_back(
UpgradeChange{
UpgradeChange::Level::Safe,
function->location(),
appendVirtual(*function)
}
);
}
for (auto [begin, end] = inheritedFunctions.equal_range(proxy); begin != end; begin++)
{
auto& super = (*begin);
if (
!function->markedVirtual() &&
!super.virtualSemantics()
)
{
m_changes.push_back(
UpgradeChange{
UpgradeChange::Level::Safe,
function->location(),
appendVirtual(*function)
}
);
}
}
}
}
}

View File

@ -0,0 +1,69 @@
/*
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/>.
*/
#pragma once
#include <tools/solidityUpgrade/UpgradeChange.h>
#include <tools/solidityUpgrade/UpgradeSuite.h>
#include <libsolidity/ast/ASTVisitor.h>
namespace solidity::tools
{
/**
* Module that performs analysis on the AST. Finds abstract contracts that are
* not marked as such and adds the `abstract` keyword.
*/
class AbstractContract: public AnalysisUpgrade
{
public:
using AnalysisUpgrade::AnalysisUpgrade;
void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); }
private:
void endVisit(frontend::ContractDefinition const& _contract);
};
/**
* Module that performs analysis on the AST. Finds functions that need to be
* marked `override` and adds the keyword to the function header.
*/
class OverridingFunction: public AnalysisUpgrade
{
public:
using AnalysisUpgrade::AnalysisUpgrade;
void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); }
private:
void endVisit(frontend::ContractDefinition const& _contract);
};
/**
* Module that performs analysis on the AST. Finds functions that need to be
* marked `virtual` and adds the keyword to the function header.
*/
class VirtualFunction: public AnalysisUpgrade
{
public:
using AnalysisUpgrade::AnalysisUpgrade;
void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); }
private:
void endVisit(frontend::ContractDefinition const& _function);
};
}

View File

@ -0,0 +1,73 @@
/*
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 <tools/solidityUpgrade/UpgradeChange.h>
#include <liblangutil/SourceReferenceExtractor.h>
#include <liblangutil/SourceReferenceFormatterHuman.h>
using namespace std;
using namespace solidity;
using namespace solidity::langutil;
using namespace solidity::util;
using namespace solidity::tools;
void UpgradeChange::apply()
{
m_source.replace(m_location.start, m_location.end - m_location.start, m_patch);
}
void UpgradeChange::log(bool const _shorten) const
{
stringstream os;
SourceReferenceFormatterHuman formatter{os, true};
string start = to_string(m_location.start);
string end = to_string(m_location.end);
auto color = m_level == Level::Unsafe ? formatting::MAGENTA : formatting::CYAN;
auto level = m_level == Level::Unsafe ? "unsafe" : "safe";
os << endl;
AnsiColorized(os, true, {formatting::BOLD, color}) << "Upgrade change (" << level << ")" << endl;
os << "=======================" << endl;
formatter.printSourceLocation(SourceReferenceExtractor::extract(&m_location));
os << endl;
LineColumn lineEnd = m_location.source->translatePositionToLineColumn(m_location.end);
int const leftpad = static_cast<int>(log10(max(lineEnd.line, 1))) + 2;
stringstream output;
output << (_shorten ? shortenSource(m_patch) : m_patch);
string line;
while (getline(output, line))
{
os << string(leftpad, ' ');
AnsiColorized(os, true, {formatting::BOLD, formatting::BLUE}) << "| ";
AnsiColorized(os, true, {}) << line << endl;
}
cout << os.str();
cout << endl;
}
string UpgradeChange::shortenSource(string const& _source)
{
size_t constexpr maxSourceLength = 1000;
if (_source.size() > maxSourceLength)
return _source.substr(0, min(_source.size(), maxSourceLength)) + "...";
return _source;
}

View File

@ -0,0 +1,82 @@
/*
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/>.
*/
#pragma once
#include <libsolutil/AnsiColorized.h>
#include <liblangutil/SourceLocation.h>
#include <algorithm>
namespace solidity::tools
{
/**
* Models a single source code change, based on the initial source location
* and a patch, which needs to be applied.
* It implements the concept of level of confidence in the change and distiguishes
* safe from unsafe changes. A "safe" change is considered to not break
* compilation or change semantics. An "unsafe" change is considered to potentially
* change semantics or require further manual management.
*/
class UpgradeChange
{
public:
enum class Level
{
Safe,
Unsafe
};
UpgradeChange(
Level _level,
langutil::SourceLocation _location,
std::string _patch
)
:
m_location(_location),
m_source(_location.source->source()),
m_patch(_patch),
m_level(_level) {}
~UpgradeChange() {}
langutil::SourceLocation const& location() { return m_location; }
std::string source() const { return m_source; }
std::string patch() { return m_patch; }
Level level() const { return m_level; }
/// Does the actual replacement of code under at current source location.
/// The change is applied on the upgrade-specific copy of source code.
/// The altered code is then requested by the upgrade routine later on.
void apply();
/// Does a pretty-print of this upgrade change. It uses a source formatter
/// provided by the compiler in order to print affected code. Since the patch
/// can contain a lot of code lines, it can be shortened, which is signaled
/// by setting the flag.
void log(bool const _shorten = true) const;
private:
langutil::SourceLocation m_location;
std::string m_source;
std::string m_patch;
Level m_level;
/// Shortens the given source to a constant limit.
static std::string shortenSource(std::string const& _source);
};
}

View File

@ -0,0 +1,90 @@
/*
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/>.
*/
#pragma once
#include <tools/solidityUpgrade/UpgradeChange.h>
#include <liblangutil/ErrorReporter.h>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/analysis/OverrideChecker.h>
#include <regex>
namespace solidity::tools
{
/**
* The base upgrade module that can be inherited from. Doing so
* creates a basic upgrade module that facilitates access to
* change reporting.
*/
class Upgrade
{
public:
Upgrade(std::vector<UpgradeChange>& _changes): m_changes(_changes) {}
protected:
/// A reference to a suite-specific set of changes.
/// It is passed to all upgrade modules and meant to collect
/// reported changes.
std::vector<UpgradeChange>& m_changes;
};
/**
* A specific upgrade module meant to be run after the analysis phase
* of the compiler.
*/
class AnalysisUpgrade: public Upgrade, public frontend::ASTConstVisitor
{
public:
AnalysisUpgrade(std::vector<UpgradeChange>& _changes):
Upgrade(_changes),
m_errorReporter(m_errors),
m_overrideChecker(m_errorReporter)
{}
/// Interface function for all upgrade modules that are meant
/// be run after the analysis phase of the compiler.
void analyze(frontend::SourceUnit const&) {}
protected:
langutil::ErrorList m_errors;
langutil::ErrorReporter m_errorReporter;
frontend::OverrideChecker m_overrideChecker;
};
/**
* The generic upgrade suite. Should be inherited from for each set of
* desired upgrade modules.
*/
class UpgradeSuite
{
public:
/// The base interface function that needs to be implemented for each
/// suite. It should create suite-specific upgrade modules and trigger
/// their analysis.
void analyze(frontend::SourceUnit const& _sourceUnit);
/// Resets all changes collected so far.
void reset() { m_changes.clear(); }
std::vector<UpgradeChange>& changes() { return m_changes; }
std::vector<UpgradeChange> const& changes() const { return m_changes; }
protected:
std::vector<UpgradeChange> m_changes;
};
}

View File

@ -0,0 +1,23 @@
pragma solidity >0.4.23;
contract Updateable {
function run() public view returns (bool);
function update() public;
}
contract Upgradable {
function run() public view returns (bool);
function upgrade();
}
contract Source is Updateable, Upgradable {
function Source() public {}
function run()
public
view
returns (bool) {}
function update() {}
function upgrade() {}
}

View File

@ -0,0 +1,18 @@
pragma solidity >0.4.23;
contract Storage {
function Storage() public {}
function start();
function state() public view returns (bool);
function stop() public;
}
contract Observable {
function state() public view returns (bool);
}
contract VolatileStorage is Storage, Observable {
function start() {}
function state() public view returns (bool) {}
function stop() {}
}

View File

@ -0,0 +1,21 @@
pragma solidity >0.4.23;
contract Storage {
function Storage() {}
function init() public;
function idle();
function destroy() public view;
}
contract VolatileStorage is Storage {
function init()
public
{}
function idle() {}
function destroy()
public
view
{}
}

View File

@ -0,0 +1,15 @@
pragma solidity >0.4.23;
contract Storage {
function Storage() {}
function init() public;
function idle();
function destroy() public view;
}
contract VolatileStorage is Storage {
uint[] array;
function init() public { array.length = 3; }
function idle() {}
function destroy() public view {}
}

View File

@ -0,0 +1,81 @@
/*
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 <tools/solidityUpgrade/SourceUpgrade.h>
#include <libsolutil/CommonIO.h>
#include <libsolutil/AnsiColorized.h>
//#include <test/Common.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/filesystem.hpp>
#include <boost/program_options.hpp>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <queue>
#include <regex>
#if defined(_WIN32)
#include <windows.h>
#endif
using namespace solidity;
using namespace std;
namespace po = boost::program_options;
namespace fs = boost::filesystem;
namespace
{
void setupTerminal()
{
#if defined(_WIN32) && defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)
// Set output mode to handle virtual terminal (ANSI escape sequences)
// ignore any error, as this is just a "nice-to-have"
// only windows needs to be taken care of, as other platforms (Linux/OSX) support them natively.
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hOut == INVALID_HANDLE_VALUE)
return;
DWORD dwMode = 0;
if (!GetConsoleMode(hOut, &dwMode))
return;
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
if (!SetConsoleMode(hOut, dwMode))
return;
#endif
}
}
int main(int argc, char** argv)
{
setupTerminal();
tools::SourceUpgrade upgrade;
if (!upgrade.parseArguments(argc, argv))
return 1;
upgrade.printPrologue();
if (!upgrade.processInput())
return 1;
return 0;
}