mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Adds documentation for Solidity source upgrader.
This commit is contained in:
parent
5de3379d82
commit
f2701db0aa
@ -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:
|
||||||
|
@ -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)
|
||||||
|
@ -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 {}
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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().
|
||||||
|
@ -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
14
tools/CMakeLists.txt
Normal 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}")
|
159
tools/solidityUpgrade/SourceTransform.h
Normal file
159
tools/solidityUpgrade/SourceTransform.h
Normal 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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
525
tools/solidityUpgrade/SourceUpgrade.cpp
Normal file
525
tools/solidityUpgrade/SourceUpgrade.cpp
Normal 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);
|
||||||
|
}
|
158
tools/solidityUpgrade/SourceUpgrade.h
Normal file
158
tools/solidityUpgrade/SourceUpgrade.h
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
58
tools/solidityUpgrade/Upgrade050.cpp
Normal file
58
tools/solidityUpgrade/Upgrade050.cpp
Normal 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")
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
58
tools/solidityUpgrade/Upgrade050.h
Normal file
58
tools/solidityUpgrade/Upgrade050.h
Normal 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);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
212
tools/solidityUpgrade/Upgrade060.cpp
Normal file
212
tools/solidityUpgrade/Upgrade060.cpp
Normal 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)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
69
tools/solidityUpgrade/Upgrade060.h
Normal file
69
tools/solidityUpgrade/Upgrade060.h
Normal 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);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
73
tools/solidityUpgrade/UpgradeChange.cpp
Normal file
73
tools/solidityUpgrade/UpgradeChange.cpp
Normal 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;
|
||||||
|
}
|
82
tools/solidityUpgrade/UpgradeChange.h
Normal file
82
tools/solidityUpgrade/UpgradeChange.h
Normal 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);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
90
tools/solidityUpgrade/UpgradeSuite.h
Normal file
90
tools/solidityUpgrade/UpgradeSuite.h
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
23
tools/solidityUpgrade/contracts/DocsExamplePass.sol
Normal file
23
tools/solidityUpgrade/contracts/DocsExamplePass.sol
Normal 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() {}
|
||||||
|
}
|
18
tools/solidityUpgrade/contracts/Test.sol
Normal file
18
tools/solidityUpgrade/contracts/Test.sol
Normal 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() {}
|
||||||
|
}
|
21
tools/solidityUpgrade/contracts/TestMultiline.sol
Normal file
21
tools/solidityUpgrade/contracts/TestMultiline.sol
Normal 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
|
||||||
|
{}
|
||||||
|
}
|
15
tools/solidityUpgrade/contracts/TestNonFixable.sol
Normal file
15
tools/solidityUpgrade/contracts/TestNonFixable.sol
Normal 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 {}
|
||||||
|
}
|
81
tools/solidityUpgrade/main.cpp
Normal file
81
tools/solidityUpgrade/main.cpp
Normal 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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user