aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--Changelog.md7
-rw-r--r--docs/bugs.json11
-rw-r--r--docs/bugs_by_version.json45
-rw-r--r--docs/common-patterns.rst51
-rw-r--r--docs/contracts.rst19
-rw-r--r--docs/frequently-asked-questions.rst3
-rw-r--r--docs/index.rst5
-rw-r--r--docs/layout-of-source-files.rst2
-rw-r--r--docs/security-considerations.rst29
-rw-r--r--docs/solidity-by-example.rst114
-rw-r--r--docs/structure-of-a-contract.rst4
-rw-r--r--docs/types.rst15
-rw-r--r--docs/units-and-global-variables.rst4
-rw-r--r--docs/using-the-compiler.rst13
-rw-r--r--libevmasm/ConstantOptimiser.cpp9
-rw-r--r--libsolidity/analysis/PostTypeChecker.cpp3
-rw-r--r--libsolidity/analysis/StaticAnalyzer.cpp45
-rw-r--r--libsolidity/analysis/StaticAnalyzer.h9
-rw-r--r--libsolidity/analysis/SyntaxChecker.cpp17
-rw-r--r--libsolidity/analysis/SyntaxChecker.h4
-rw-r--r--libsolidity/interface/CompilerStack.cpp64
-rw-r--r--libsolidity/interface/CompilerStack.h19
-rw-r--r--libsolidity/interface/StandardCompiler.cpp30
-rw-r--r--std/StandardToken.sol4
-rw-r--r--test/fuzzer.cpp2
-rw-r--r--test/libsolidity/ASTJSON.cpp26
-rw-r--r--test/libsolidity/SolidityABIJSON.cpp2
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp231
-rw-r--r--test/libsolidity/SolidityNatspecJSON.cpp4
-rw-r--r--test/libsolidity/SolidityOptimizer.cpp50
-rw-r--r--test/libsolidity/StandardCompiler.cpp15
32 files changed, 590 insertions, 268 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 931a8a0f..e53917cb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -8,7 +8,7 @@ include(EthPolicy)
eth_policy()
# project name and version should be set after cmake_policy CMP0048
-set(PROJECT_VERSION "0.4.11")
+set(PROJECT_VERSION "0.4.12")
project(solidity VERSION ${PROJECT_VERSION})
# Let's find our dependencies
diff --git a/Changelog.md b/Changelog.md
index cd54aadb..be5c18c5 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,4 +1,6 @@
-### 0.4.11 (unreleased)
+### 0.4.12 (unreleased)
+
+### 0.4.11 (2017-05-03)
Features:
* Implement the Standard JSON Input / Output API
@@ -10,6 +12,8 @@ Features:
* Inline Assembly: Storage variable access using ``_slot`` and ``_offset`` suffixes.
* Inline Assembly: Disallow blocks with unbalanced stack.
* Static analyzer: Warn about statements without effects.
+ * Static analyzer: Warn about unused local variables, parameters, and return parameters.
+ * Syntax checker: issue deprecation warning for unary '+'
Bugfixes:
* Assembly output: Implement missing AssemblyItem types.
@@ -18,6 +22,7 @@ Bugfixes:
and source mappings.
* Gas Estimator: Reflect the most recent fee schedule.
* Type system: Contract inheriting from base with unimplemented constructor should be abstract.
+ * Optimizer: Number representation bug in the constant optimizer fixed.
### 0.4.10 (2017-03-15)
diff --git a/docs/bugs.json b/docs/bugs.json
index 2a8d167a..1a67d626 100644
--- a/docs/bugs.json
+++ b/docs/bugs.json
@@ -1,5 +1,16 @@
[
{
+ "name": "ConstantOptimizerSubtraction",
+ "summary": "In some situations, the optimizer replaces certain numbers in the code with routines that compute different numbers.",
+ "description": "The optimizer tries to represent any number in the bytecode by routines that compute them with less gas. For some special numbers, an incorrect routine is generated. This could allow an attacker to e.g. trick victims about a specific amount of ether, or function calls to call different functions (or none at all).",
+ "link": "https://blog.ethereum.org/2017/05/03/solidity-optimizer-bug/",
+ "fixed": "0.4.11",
+ "severity": "low",
+ "conditions": {
+ "optimizer": true
+ }
+ },
+ {
"name": "IdentityPrecompileReturnIgnored",
"summary": "Failure of the identity precompile was ignored.",
"description": "Calls to the identity contract, which is used for copying memory, ignored its return value. On the public chain, calls to the identity precompile can be made in a way that they never fail, but this might be different on private chains.",
diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json
index 64015d4a..0f7346b4 100644
--- a/docs/bugs_by_version.json
+++ b/docs/bugs_by_version.json
@@ -1,6 +1,7 @@
{
"0.1.0": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"OptimizerStaleKnowledgeAboutSHA3",
"SendFailsForZeroEther",
@@ -14,6 +15,7 @@
},
"0.1.1": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"OptimizerStaleKnowledgeAboutSHA3",
"SendFailsForZeroEther",
@@ -27,6 +29,7 @@
},
"0.1.2": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"OptimizerStaleKnowledgeAboutSHA3",
"SendFailsForZeroEther",
@@ -40,6 +43,7 @@
},
"0.1.3": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"OptimizerStaleKnowledgeAboutSHA3",
"SendFailsForZeroEther",
@@ -53,6 +57,7 @@
},
"0.1.4": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"OptimizerStaleKnowledgeAboutSHA3",
"SendFailsForZeroEther",
@@ -66,6 +71,7 @@
},
"0.1.5": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"OptimizerStaleKnowledgeAboutSHA3",
"SendFailsForZeroEther",
@@ -79,6 +85,7 @@
},
"0.1.6": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3",
@@ -93,6 +100,7 @@
},
"0.1.7": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3",
@@ -107,6 +115,7 @@
},
"0.2.0": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3",
@@ -121,6 +130,7 @@
},
"0.2.1": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3",
@@ -135,6 +145,7 @@
},
"0.2.2": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3",
@@ -149,6 +160,7 @@
},
"0.3.0": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3",
@@ -162,6 +174,7 @@
},
"0.3.1": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3",
@@ -174,6 +187,7 @@
},
"0.3.2": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3",
@@ -186,6 +200,7 @@
},
"0.3.3": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3",
@@ -197,6 +212,7 @@
},
"0.3.4": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3",
@@ -208,6 +224,7 @@
},
"0.3.5": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3",
@@ -219,6 +236,7 @@
},
"0.3.6": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3",
@@ -228,6 +246,7 @@
},
"0.4.0": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3",
@@ -237,6 +256,7 @@
},
"0.4.1": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3",
@@ -245,11 +265,18 @@
"released": "2016-09-09"
},
"0.4.10": {
- "bugs": [],
+ "bugs": [
+ "ConstantOptimizerSubtraction"
+ ],
"released": "2017-03-15"
},
+ "0.4.11": {
+ "bugs": [],
+ "released": "2017-05-03"
+ },
"0.4.2": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3"
@@ -258,6 +285,7 @@
},
"0.4.3": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage"
],
@@ -265,12 +293,14 @@
},
"0.4.4": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored"
],
"released": "2016-10-31"
},
"0.4.5": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"OptimizerStateKnowledgeNotResetForJumpdest"
],
@@ -278,20 +308,27 @@
},
"0.4.6": {
"bugs": [
+ "ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored"
],
"released": "2016-11-22"
},
"0.4.7": {
- "bugs": [],
+ "bugs": [
+ "ConstantOptimizerSubtraction"
+ ],
"released": "2016-12-15"
},
"0.4.8": {
- "bugs": [],
+ "bugs": [
+ "ConstantOptimizerSubtraction"
+ ],
"released": "2017-01-13"
},
"0.4.9": {
- "bugs": [],
+ "bugs": [
+ "ConstantOptimizerSubtraction"
+ ],
"released": "2017-01-31"
}
} \ No newline at end of file
diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst
index de2da690..acef13b7 100644
--- a/docs/common-patterns.rst
+++ b/docs/common-patterns.rst
@@ -28,7 +28,7 @@ become the new richest.
::
- pragma solidity ^0.4.0;
+ pragma solidity ^0.4.11;
contract WithdrawalContract {
address public richest;
@@ -52,17 +52,12 @@ become the new richest.
}
}
- function withdraw() returns (bool) {
+ function withdraw() {
uint amount = pendingWithdrawals[msg.sender];
// Remember to zero the pending refund before
// sending to prevent re-entrancy attacks
pendingWithdrawals[msg.sender] = 0;
- if (msg.sender.send(amount)) {
- return true;
- } else {
- pendingWithdrawals[msg.sender] = amount;
- return false;
- }
+ msg.sender.transfer(amount);
}
}
@@ -70,7 +65,7 @@ This is as opposed to the more intuitive sending pattern:
::
- pragma solidity ^0.4.0;
+ pragma solidity ^0.4.11;
contract SendContract {
address public richest;
@@ -83,12 +78,8 @@ This is as opposed to the more intuitive sending pattern:
function becomeRichest() payable returns (bool) {
if (msg.value > mostSent) {
- // Check if call succeeds to prevent an attacker
- // from trapping the previous person's funds in
- // this contract through a callstack attack
- if (!richest.send(msg.value)) {
- throw;
- }
+ // This line can cause problems (explained below).
+ richest.transfer(msg.value);
richest = msg.sender;
mostSent = msg.value;
return true;
@@ -100,12 +91,16 @@ This is as opposed to the more intuitive sending pattern:
Notice that, in this example, an attacker could trap the
contract into an unusable state by causing ``richest`` to be
-the address of a contract that has a fallback function
-which consumes more than the 2300 gas stipend. That way,
-whenever ``send`` is called to deliver funds to the
-"poisoned" contract, it will cause execution to always fail
-because there will not be enough gas to finish the execution
-of the fallback function.
+the address of a contract that has a fallback function
+which fails (e.g. by using ``revert()`` or by just
+conssuming more than the 2300 gas stipend). That way,
+whenever ``transfer`` is called to deliver funds to the
+"poisoned" contract, it will fail and thus also ``becomeRichest``
+will fail, with the contract being stuck forever.
+
+In contrast, if you use the "withdraw" pattern from the first example,
+the attacker can only cause his or her own withdraw to fail and not the
+rest of the contract's workings.
.. index:: access;restricting
@@ -135,7 +130,7 @@ restrictions highly readable.
::
- pragma solidity ^0.4.0;
+ pragma solidity ^0.4.11;
contract AccessRestriction {
// These will be assigned at the construction
@@ -152,8 +147,7 @@ restrictions highly readable.
// a certain address.
modifier onlyBy(address _account)
{
- if (msg.sender != _account)
- throw;
+ require(msg.sender == _account);
// Do not forget the "_;"! It will
// be replaced by the actual function
// body when the modifier is used.
@@ -169,7 +163,7 @@ restrictions highly readable.
}
modifier onlyAfter(uint _time) {
- if (now < _time) throw;
+ require(now >= _time);
_;
}
@@ -190,8 +184,7 @@ restrictions highly readable.
// This was dangerous before Solidity version 0.4.0,
// where it was possible to skip the part after `_;`.
modifier costs(uint _amount) {
- if (msg.value < _amount)
- throw;
+ require(msg.value >= _amount);
_;
if (msg.value > _amount)
msg.sender.send(msg.value - _amount);
@@ -276,7 +269,7 @@ function finishes.
::
- pragma solidity ^0.4.0;
+ pragma solidity ^0.4.11;
contract StateMachine {
enum Stages {
@@ -293,7 +286,7 @@ function finishes.
uint public creationTime = now;
modifier atStage(Stages _stage) {
- if (stage != _stage) throw;
+ require(stage == _stage);
_;
}
diff --git a/docs/contracts.rst b/docs/contracts.rst
index 26c0c886..8d7af2c1 100644
--- a/docs/contracts.rst
+++ b/docs/contracts.rst
@@ -327,7 +327,7 @@ inheritable properties of contracts and may be overridden by derived contracts.
::
- pragma solidity ^0.4.0;
+ pragma solidity ^0.4.11;
contract owned {
function owned() { owner = msg.sender; }
@@ -341,8 +341,7 @@ inheritable properties of contracts and may be overridden by derived contracts.
// function is executed and otherwise, an exception is
// thrown.
modifier onlyOwner {
- if (msg.sender != owner)
- throw;
+ require(msg.sender == owner);
_;
}
}
@@ -390,7 +389,7 @@ inheritable properties of contracts and may be overridden by derived contracts.
contract Mutex {
bool locked;
modifier noReentrancy() {
- if (locked) throw;
+ require(!locked);
locked = true;
_;
locked = false;
@@ -401,7 +400,7 @@ inheritable properties of contracts and may be overridden by derived contracts.
/// The `return 7` statement assigns 7 to the return value but still
/// executes the statement `locked = false` in the modifier.
function f() noReentrancy returns (uint) {
- if (!msg.sender.call()) throw;
+ require(msg.sender.call());
return 7;
}
}
@@ -989,7 +988,7 @@ more advanced example to implement a set).
::
- pragma solidity ^0.4.0;
+ pragma solidity ^0.4.11;
library Set {
// We define a new struct datatype that will be used to
@@ -1035,8 +1034,7 @@ more advanced example to implement a set).
// The library functions can be called without a
// specific instance of the library, since the
// "instance" will be the current contract.
- if (!Set.insert(knownValues, value))
- throw;
+ require(Set.insert(knownValues, value));
}
// In this contract, we can also directly access knownValues.flags, if we want.
}
@@ -1166,7 +1164,7 @@ available without having to add further code.
Let us rewrite the set example from the
:ref:`libraries` in this way::
- pragma solidity ^0.4.0;
+ pragma solidity ^0.4.11;
// This is the same code as before, just without comments
library Set {
@@ -1207,8 +1205,7 @@ Let us rewrite the set example from the
// corresponding member functions.
// The following function call is identical to
// Set.insert(knownValues, value)
- if (!knownValues.insert(value))
- throw;
+ require(knownValues.insert(value));
}
}
diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst
index 639eb83e..03ee8388 100644
--- a/docs/frequently-asked-questions.rst
+++ b/docs/frequently-asked-questions.rst
@@ -665,8 +665,7 @@ What does the following strange check do in the Custom Token contract?
::
- if (balanceOf[_to] + _value < balanceOf[_to])
- throw;
+ require((balanceOf[_to] + _value) >= balanceOf[_to]);
Integers in Solidity (and most other machine-related programming languages) are restricted to a certain range.
For ``uint256``, this is ``0`` up to ``2**256 - 1``. If the result of some operation on those numbers
diff --git a/docs/index.rst b/docs/index.rst
index 7ffec806..1312864a 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -53,7 +53,7 @@ Available Solidity Integrations
* `Atom Solidity Linter <https://atom.io/packages/linter-solidity>`_
Plugin for the Atom editor that provides Solidity linting.
-
+
* `Solium <https://github.com/duaraghav8/Solium/>`_
A commandline linter for Solidity which strictly follows the rules prescribed by the `Solidity Style Guide <http://solidity.readthedocs.io/en/latest/style-guide.html>`_.
@@ -90,6 +90,9 @@ Solidity Tools
* `evmdis <https://github.com/Arachnid/evmdis>`_
EVM Disassembler that performs static analysis on the bytecode to provide a higher level of abstraction than raw EVM operations.
+* `Doxity <https://github.com/DigixGlobal/doxity>`_
+ Documentation Generator for Solidity.
+
Third-Party Solidity Parsers and Grammars
-----------------------------------------
diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst
index 05708964..715b29ae 100644
--- a/docs/layout-of-source-files.rst
+++ b/docs/layout-of-source-files.rst
@@ -26,7 +26,7 @@ The version pragma is used as follows::
pragma solidity ^0.4.0;
Such a source file will not compile with a compiler earlier than version 0.4.0
-and it will also not work on a compiler starting form version 0.5.0 (this
+and it will also not work on a compiler starting from version 0.5.0 (this
second condition is added by using ``^``). The idea behind this is that
there will be no breaking changes until version ``0.5.0``, so we can always
be sure that our code will compile the way we intended it to. We do not fix
diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst
index 1e92afa7..33c613d8 100644
--- a/docs/security-considerations.rst
+++ b/docs/security-considerations.rst
@@ -79,7 +79,7 @@ outlined further below:
::
- pragma solidity ^0.4.0;
+ pragma solidity ^0.4.11;
contract Fund {
/// Mapping of ether shares of the contract.
@@ -88,8 +88,7 @@ outlined further below:
function withdraw() {
var share = shares[msg.sender];
shares[msg.sender] = 0;
- if (!msg.sender.send(share))
- throw;
+ msg.sender.transfer(share);
}
}
@@ -124,22 +123,24 @@ Sending and Receiving Ether
(for example in the "details" section in Remix).
- There is a way to forward more gas to the receiving contract using
- ``addr.call.value(x)()``. This is essentially the same as ``addr.send(x)``,
+ ``addr.call.value(x)()``. This is essentially the same as ``addr.transfer(x)``,
only that it forwards all remaining gas and opens up the ability for the
- recipient to perform more expensive actions. This might include calling back
+ recipient to perform more expensive actions (and it only returns a failure code
+ and does not automatically propagate the error). This might include calling back
into the sending contract or other state changes you might not have thought of.
So it allows for great flexibility for honest users but also for malicious actors.
-- If you want to send Ether using ``address.send``, there are certain details to be aware of:
+- If you want to send Ether using ``address.transfer``, there are certain details to be aware of:
1. If the recipient is a contract, it causes its fallback function to be executed which can, in turn, call back the sending contract.
2. Sending Ether can fail due to the call depth going above 1024. Since the caller is in total control of the call
- depth, they can force the transfer to fail; make sure to always check the return value of ``send``. Better yet,
+ depth, they can force the transfer to fail; take this possibility into account or use ``send`` and make sure to always check its return value. Better yet,
write your contract using a pattern where the recipient can withdraw Ether instead.
3. Sending Ether can also fail because the execution of the recipient contract
- requires more than the allotted amount of gas (explicitly by using ``throw`` or
+ requires more than the allotted amount of gas (explicitly by using ``require``,
+ ``assert``, ``revert``, ``throw`` or
because the operation is just too expensive) - it "runs out of gas" (OOG).
- If the return value of ``send`` is checked, this might provide a
+ If you use ``transfer`` or ``send`` with a return value check, this might provide a
means for the recipient to block progress in the sending contract. Again, the best practice here is to use
a :ref:`"withdraw" pattern instead of a "send" pattern <withdrawal_pattern>`.
@@ -162,7 +163,7 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like
::
- pragma solidity ^0.4.0;
+ pragma solidity ^0.4.11;
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
contract TxUserWallet {
@@ -172,9 +173,9 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like
owner = msg.sender;
}
- function transfer(address dest, uint amount) {
- if (tx.origin != owner) { throw; }
- if (!dest.call.value(amount)()) throw;
+ function transferTo(address dest, uint amount) {
+ require(tx.origin == owner);
+ dest.transfer(amount);
}
}
@@ -192,7 +193,7 @@ Now someone tricks you into sending ether to the address of this attack wallet:
}
function() {
- TxUserWallet(msg.sender).transfer(owner, msg.sender.balance);
+ TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance);
}
}
diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst
index 3d6221e4..4ed474df 100644
--- a/docs/solidity-by-example.rst
+++ b/docs/solidity-by-example.rst
@@ -36,7 +36,7 @@ of votes.
::
- pragma solidity ^0.4.0;
+ pragma solidity ^0.4.11;
/// @title Voting with delegation.
contract Ballot {
@@ -87,14 +87,14 @@ of votes.
// Give `voter` the right to vote on this ballot.
// May only be called by `chairperson`.
function giveRightToVote(address voter) {
- if (msg.sender != chairperson || voters[voter].voted) {
- // `throw` terminates and reverts all changes to
- // the state and to Ether balances. It is often
- // a good idea to use this if functions are
- // called incorrectly. But watch out, this
- // will also consume all provided gas.
- throw;
- }
+ // If the argument of `require` evaluates to `false`,
+ // it terminates and reverts all changes to
+ // the state and to Ether balances. It is often
+ // a good idea to use this if functions are
+ // called incorrectly. But watch out, this
+ // will currently also consume all provided gas
+ // (this is planned to change in the future).
+ require((msg.sender == chairperson) && !voters[voter].voted);
voters[voter].weight = 1;
}
@@ -102,12 +102,10 @@ of votes.
function delegate(address to) {
// assigns reference
Voter sender = voters[msg.sender];
- if (sender.voted)
- throw;
+ require(!sender.voted);
// Self-delegation is not allowed.
- if (to == msg.sender)
- throw;
+ require(to != msg.sender);
// Forward the delegation as long as
// `to` also delegated.
@@ -121,8 +119,7 @@ of votes.
to = voters[to].delegate;
// We found a loop in the delegation, not allowed.
- if (to == msg.sender)
- throw;
+ require(to != msg.sender);
}
// Since `sender` is a reference, this
@@ -145,8 +142,7 @@ of votes.
/// to proposal `proposals[proposal].name`.
function vote(uint proposal) {
Voter sender = voters[msg.sender];
- if (sender.voted)
- throw;
+ require(!sender.voted);
sender.voted = true;
sender.vote = proposal;
@@ -218,7 +214,7 @@ activate themselves.
::
- pragma solidity ^0.4.0;
+ pragma solidity ^0.4.11;
contract SimpleAuction {
// Parameters of the auction. Times are either
@@ -269,16 +265,15 @@ activate themselves.
// the transaction. The keyword payable
// is required for the function to
// be able to receive Ether.
- if (now > auctionStart + biddingTime) {
- // Revert the call if the bidding
- // period is over.
- throw;
- }
- if (msg.value <= highestBid) {
- // If the bid is not higher, send the
- // money back.
- throw;
- }
+
+ // Revert the call if the bidding
+ // period is over.
+ require(now <= (auctionStart + biddingTime));
+
+ // If the bid is not higher, send the
+ // money back.
+ require(msg.value > highestBid);
+
if (highestBidder != 0) {
// Sending back the money by simply using
// highestBidder.send(highestBid) is a security risk
@@ -327,18 +322,15 @@ activate themselves.
// external contracts.
// 1. Conditions
- if (now <= auctionStart + biddingTime)
- throw; // auction did not yet end
- if (ended)
- throw; // this function has already been called
+ require(now >= (auctionStart + biddingTime)); // auction did not yet end
+ require(!ended); // this function has already been called
// 2. Effects
ended = true;
AuctionEnded(highestBidder, highestBid);
// 3. Interaction
- if (!beneficiary.send(highestBid))
- throw;
+ beneficiary.transfer(highestBid);
}
}
@@ -381,7 +373,7 @@ high or low invalid bids.
::
- pragma solidity ^0.4.0;
+ pragma solidity ^0.4.11;
contract BlindAuction {
struct Bid {
@@ -409,8 +401,8 @@ high or low invalid bids.
/// functions. `onlyBefore` is applied to `bid` below:
/// The new function body is the modifier's body where
/// `_` is replaced by the old function body.
- modifier onlyBefore(uint _time) { if (now >= _time) throw; _; }
- modifier onlyAfter(uint _time) { if (now <= _time) throw; _; }
+ modifier onlyBefore(uint _time) { require(now < _time); _; }
+ modifier onlyAfter(uint _time) { require(now > _time); _; }
function BlindAuction(
uint _biddingTime,
@@ -454,13 +446,9 @@ high or low invalid bids.
onlyBefore(revealEnd)
{
uint length = bids[msg.sender].length;
- if (
- _values.length != length ||
- _fake.length != length ||
- _secret.length != length
- ) {
- throw;
- }
+ require(_values.length == length);
+ require(_fake.length == length);
+ require(_secret.length == length);
uint refund;
for (uint i = 0; i < length; i++) {
@@ -481,8 +469,7 @@ high or low invalid bids.
// the same deposit.
bid.blindedBid = 0;
}
- if (!msg.sender.send(refund))
- throw;
+ msg.sender.transfer(refund);
}
// This is an "internal" function which means that it
@@ -527,14 +514,12 @@ high or low invalid bids.
function auctionEnd()
onlyAfter(revealEnd)
{
- if (ended)
- throw;
+ require(!ended);
AuctionEnded(highestBidder, highestBid);
ended = true;
// We send all the money we have, because some
// of the refunds might have failed.
- if (!beneficiary.send(this.balance))
- throw;
+ beneficiary.transfer(this.balance);
}
}
@@ -546,7 +531,7 @@ Safe Remote Purchase
::
- pragma solidity ^0.4.0;
+ pragma solidity ^0.4.11;
contract Purchase {
uint public value;
@@ -558,26 +543,26 @@ Safe Remote Purchase
function Purchase() payable {
seller = msg.sender;
value = msg.value / 2;
- if (2 * value != msg.value) throw;
+ require((2 * value) == msg.value);
}
- modifier require(bool _condition) {
- if (!_condition) throw;
+ modifier condition(bool _condition) {
+ require(_condition);
_;
}
modifier onlyBuyer() {
- if (msg.sender != buyer) throw;
+ require(msg.sender == buyer);
_;
}
modifier onlySeller() {
- if (msg.sender != seller) throw;
+ require(msg.sender == seller);
_;
}
modifier inState(State _state) {
- if (state != _state) throw;
+ require(state == _state);
_;
}
@@ -594,8 +579,7 @@ Safe Remote Purchase
{
aborted();
state = State.Inactive;
- if (!seller.send(this.balance))
- throw;
+ seller.transfer(this.balance);
}
/// Confirm the purchase as buyer.
@@ -604,7 +588,7 @@ Safe Remote Purchase
/// is called.
function confirmPurchase()
inState(State.Created)
- require(msg.value == 2 * value)
+ condition(msg.value == (2 * value))
payable
{
purchaseConfirmed();
@@ -623,10 +607,12 @@ Safe Remote Purchase
// otherwise, the contracts called using `send` below
// can call in again here.
state = State.Inactive;
- // This actually allows both the buyer and the seller to
- // block the refund.
- if (!buyer.send(value) || !seller.send(this.balance))
- throw;
+
+ // NOTE: This actually allows both the buyer and the seller to
+ // block the refund - the withdraw pattern should be used.
+
+ buyer.transfer(value);
+ seller.transfer(this.balance));
}
}
diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst
index 24ef69a6..224eb368 100644
--- a/docs/structure-of-a-contract.rst
+++ b/docs/structure-of-a-contract.rst
@@ -62,13 +62,13 @@ Function modifiers can be used to amend the semantics of functions in a declarat
::
- pragma solidity ^0.4.0;
+ pragma solidity ^0.4.11;
contract Purchase {
address public seller;
modifier onlySeller() { // Modifier
- if (msg.sender != seller) throw;
+ require(msg.sender == seller);
_;
}
diff --git a/docs/types.rst b/docs/types.rst
index 60235ad2..c400aecb 100644
--- a/docs/types.rst
+++ b/docs/types.rst
@@ -64,6 +64,12 @@ expression ``x << y`` is equivalent to ``x * 2**y`` and ``x >> y`` is
equivalent to ``x / 2**y``. This means that shifting negative numbers
sign extends. Shifting by a negative amount throws a runtime exception.
+.. warning::
+ The results produced by shift right of negative values of signed integer types is different from those produced
+ by other programming languages. In Solidity, shift right maps to division so the shifted negative values
+ are going to be rounded towards zero (truncated). In other programming languages the shift right of negative values
+ works like division with rounding down (towards negative infinity).
+
.. index:: address, balance, send, call, callcode, delegatecall, transfer
.. _address:
@@ -414,7 +420,7 @@ Example that shows how to use internal function types::
Another example that uses external function types::
- pragma solidity ^0.4.5;
+ pragma solidity ^0.4.11;
contract Oracle {
struct Request {
@@ -439,7 +445,7 @@ Another example that uses external function types::
oracle.query("USD", this.oracleResponse);
}
function oracleResponse(bytes response) {
- if (msg.sender != address(oracle)) throw;
+ require(msg.sender == address(oracle));
// Use the data
}
}
@@ -716,7 +722,7 @@ shown in the following example:
::
- pragma solidity ^0.4.0;
+ pragma solidity ^0.4.11;
contract CrowdFunding {
// Defines a new type with two fields.
@@ -757,8 +763,7 @@ shown in the following example:
return false;
uint amount = c.amount;
c.amount = 0;
- if (!c.beneficiary.send(amount))
- throw;
+ c.beneficiary.transfer(amount);
return true;
}
}
diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst
index ab957c7d..246cc564 100644
--- a/docs/units-and-global-variables.rst
+++ b/docs/units-and-global-variables.rst
@@ -22,8 +22,8 @@ unit and units are considered naively in the following way:
* ``1 minutes == 60 seconds``
* ``1 hours == 60 minutes``
* ``1 days == 24 hours``
- * ``1 weeks = 7 days``
- * ``1 years = 365 days``
+ * ``1 weeks == 7 days``
+ * ``1 years == 365 days``
Take care if you perform calendar calculations using these units, because
not every year equals 365 days and not even every day has 24 hours
diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst
index b74da213..7aa997f8 100644
--- a/docs/using-the-compiler.rst
+++ b/docs/using-the-compiler.rst
@@ -121,7 +121,6 @@ Input Description
// abi - ABI
// ast - AST of all source files (not supported atm)
// legacyAST - legacy AST of all source files
- // why3 - Why3 translated output
// devdoc - Developer documentation (natspec)
// userdoc - User documentation (natspec)
// metadata - Metadata
@@ -154,9 +153,9 @@ Input Description
"*": {
"*": [ "evm.sourceMap" ]
},
- // Enable the legacy AST and Why3 output of every single file.
+ // Enable the legacy AST output of every single file.
"*": {
- "": [ "legacyAST", "why3" ]
+ "": [ "legacyAST" ]
}
}
}
@@ -180,7 +179,7 @@ Output Description
],
// Mandatory: Error type, such as "TypeError", "InternalCompilerError", "Exception", etc
type: "TypeError",
- // Mandatory: Component where the error originated, such as "general", "why3", "ewasm", etc.
+ // Mandatory: Component where the error originated, such as "general", "ewasm", etc.
component: "general",
// Mandatory ("error" or "warning")
severity: "error",
@@ -222,7 +221,7 @@ Output Description
// Assembly (string)
assembly: "",
// Old-style assembly (object)
- legacyAssembly: [],
+ legacyAssembly: {},
// Bytecode and related details.
bytecode: {
// The bytecode as a hex string.
@@ -272,7 +271,5 @@ Output Description
}
}
}
- },
- // Why3 output (string)
- why3: ""
+ }
}
diff --git a/libevmasm/ConstantOptimiser.cpp b/libevmasm/ConstantOptimiser.cpp
index d2ed4faf..0c093ebf 100644
--- a/libevmasm/ConstantOptimiser.cpp
+++ b/libevmasm/ConstantOptimiser.cpp
@@ -203,8 +203,13 @@ AssemblyItems ComputeMethod::findRepresentation(u256 const& _value)
u256 powerOfTwo = u256(1) << bits;
u256 upperPart = _value >> bits;
bigint lowerPart = _value & (powerOfTwo - 1);
- if (abs(powerOfTwo - lowerPart) < lowerPart)
+ if ((powerOfTwo - lowerPart) < lowerPart)
+ {
lowerPart = lowerPart - powerOfTwo; // make it negative
+ upperPart++;
+ }
+ if (upperPart == 0)
+ continue;
if (abs(lowerPart) >= (powerOfTwo >> 8))
continue;
@@ -212,7 +217,7 @@ AssemblyItems ComputeMethod::findRepresentation(u256 const& _value)
if (lowerPart != 0)
newRoutine += findRepresentation(u256(abs(lowerPart)));
newRoutine += AssemblyItems{u256(bits), u256(2), Instruction::EXP};
- if (upperPart != 1 && upperPart != 0)
+ if (upperPart != 1)
newRoutine += findRepresentation(upperPart) + AssemblyItems{Instruction::MUL};
if (lowerPart > 0)
newRoutine += AssemblyItems{Instruction::ADD};
diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp
index cae77c74..e8da3ca4 100644
--- a/libsolidity/analysis/PostTypeChecker.cpp
+++ b/libsolidity/analysis/PostTypeChecker.cpp
@@ -58,6 +58,9 @@ void PostTypeChecker::endVisit(ContractDefinition const&)
for (auto declaration: m_constVariables)
if (auto identifier = findCycle(declaration))
typeError(declaration->location(), "The value of the constant " + declaration->name() + " has a cyclic dependency via " + identifier->name() + ".");
+
+ m_constVariables.clear();
+ m_constVariableDependencies.clear();
}
bool PostTypeChecker::visit(VariableDeclaration const& _variable)
diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp
index 190d2420..369376fa 100644
--- a/libsolidity/analysis/StaticAnalyzer.cpp
+++ b/libsolidity/analysis/StaticAnalyzer.cpp
@@ -48,13 +48,58 @@ void StaticAnalyzer::endVisit(ContractDefinition const&)
bool StaticAnalyzer::visit(FunctionDefinition const& _function)
{
+ if (_function.isImplemented())
+ m_currentFunction = &_function;
+ else
+ solAssert(!m_currentFunction, "");
+ solAssert(m_localVarUseCount.empty(), "");
m_nonPayablePublic = _function.isPublic() && !_function.isPayable();
return true;
}
void StaticAnalyzer::endVisit(FunctionDefinition const&)
{
+ m_currentFunction = nullptr;
m_nonPayablePublic = false;
+ for (auto const& var: m_localVarUseCount)
+ if (var.second == 0)
+ warning(var.first->location(), "Unused local variable");
+ m_localVarUseCount.clear();
+}
+
+bool StaticAnalyzer::visit(Identifier const& _identifier)
+{
+ if (m_currentFunction)
+ if (auto var = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
+ {
+ solAssert(!var->name().empty(), "");
+ if (var->isLocalVariable())
+ m_localVarUseCount[var] += 1;
+ }
+ return true;
+}
+
+bool StaticAnalyzer::visit(VariableDeclaration const& _variable)
+{
+ if (m_currentFunction)
+ {
+ solAssert(_variable.isLocalVariable(), "");
+ if (_variable.name() != "")
+ // This is not a no-op, the entry might pre-exist.
+ m_localVarUseCount[&_variable] += 0;
+ }
+ return true;
+}
+
+bool StaticAnalyzer::visit(Return const& _return)
+{
+ // If the return has an expression, it counts as
+ // a "use" of the return parameters.
+ if (m_currentFunction && _return.expression())
+ for (auto const& var: m_currentFunction->returnParameters())
+ if (!var->name().empty())
+ m_localVarUseCount[var.get()] += 1;
+ return true;
}
bool StaticAnalyzer::visit(ExpressionStatement const& _statement)
diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h
index 84342322..ab72e7d9 100644
--- a/libsolidity/analysis/StaticAnalyzer.h
+++ b/libsolidity/analysis/StaticAnalyzer.h
@@ -61,7 +61,9 @@ private:
virtual void endVisit(FunctionDefinition const& _function) override;
virtual bool visit(ExpressionStatement const& _statement) override;
-
+ virtual bool visit(VariableDeclaration const& _variable) override;
+ virtual bool visit(Identifier const& _identifier) override;
+ virtual bool visit(Return const& _return) override;
virtual bool visit(MemberAccess const& _memberAccess) override;
ErrorList& m_errors;
@@ -71,6 +73,11 @@ private:
/// Flag that indicates whether a public function does not contain the "payable" modifier.
bool m_nonPayablePublic = false;
+
+ /// Number of uses of each (named) local variable in a function, counter is initialized with zero.
+ std::map<VariableDeclaration const*, int> m_localVarUseCount;
+
+ FunctionDefinition const* m_currentFunction = nullptr;
};
}
diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp
index 89014133..94e82a87 100644
--- a/libsolidity/analysis/SyntaxChecker.cpp
+++ b/libsolidity/analysis/SyntaxChecker.cpp
@@ -32,6 +32,16 @@ bool SyntaxChecker::checkSyntax(ASTNode const& _astRoot)
return Error::containsOnlyWarnings(m_errors);
}
+void SyntaxChecker::warning(SourceLocation const& _location, string const& _description)
+{
+ auto err = make_shared<Error>(Error::Type::Warning);
+ *err <<
+ errinfo_sourceLocation(_location) <<
+ errinfo_comment(_description);
+
+ m_errors.push_back(err);
+}
+
void SyntaxChecker::syntaxError(SourceLocation const& _location, std::string const& _description)
{
auto err = make_shared<Error>(Error::Type::SyntaxError);
@@ -148,6 +158,13 @@ bool SyntaxChecker::visit(Break const& _breakStatement)
return true;
}
+bool SyntaxChecker::visit(UnaryOperation const& _operation)
+{
+ if (_operation.getOperator() == Token::Add)
+ warning(_operation.location(), "Use of unary + is deprecated.");
+ return true;
+}
+
bool SyntaxChecker::visit(PlaceholderStatement const&)
{
m_placeholderFound = true;
diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h
index 308e128b..8d7dcdd3 100644
--- a/libsolidity/analysis/SyntaxChecker.h
+++ b/libsolidity/analysis/SyntaxChecker.h
@@ -32,6 +32,7 @@ namespace solidity
* The module that performs syntax analysis on the AST:
* - whether continue/break is in a for/while loop.
* - whether a modifier contains at least one '_'
+ * - issues deprecation warnings for unary '+'
*/
class SyntaxChecker: private ASTConstVisitor
{
@@ -43,6 +44,7 @@ public:
private:
/// Adds a new error to the list of errors.
+ void warning(SourceLocation const& _location, std::string const& _description);
void syntaxError(SourceLocation const& _location, std::string const& _description);
virtual bool visit(SourceUnit const& _sourceUnit) override;
@@ -60,6 +62,8 @@ private:
virtual bool visit(Continue const& _continueStatement) override;
virtual bool visit(Break const& _breakStatement) override;
+ virtual bool visit(UnaryOperation const& _operation) override;
+
virtual bool visit(PlaceholderStatement const& _placeholderStatement) override;
ErrorList& m_errors;
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index b09435a8..9c9c9614 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -57,7 +57,7 @@ using namespace dev;
using namespace dev::solidity;
CompilerStack::CompilerStack(ReadFile::Callback const& _readFile):
- m_readFile(_readFile), m_parseSuccessful(false) {}
+ m_readFile(_readFile) {}
void CompilerStack::setRemappings(vector<string> const& _remappings)
{
@@ -79,10 +79,12 @@ void CompilerStack::setRemappings(vector<string> const& _remappings)
void CompilerStack::reset(bool _keepSources)
{
- m_parseSuccessful = false;
if (_keepSources)
+ {
+ m_stackState = SourcesSet;
for (auto sourcePair: m_sources)
sourcePair.second.reset();
+ }
else
{
m_sources.clear();
@@ -94,6 +96,7 @@ void CompilerStack::reset(bool _keepSources)
m_sourceOrder.clear();
m_contracts.clear();
m_errors.clear();
+ m_stackState = Empty;
}
bool CompilerStack::addSource(string const& _name, string const& _content, bool _isLibrary)
@@ -102,6 +105,7 @@ bool CompilerStack::addSource(string const& _name, string const& _content, bool
reset(true);
m_sources[_name].scanner = make_shared<Scanner>(CharStream(_content), _name);
m_sources[_name].isLibrary = _isLibrary;
+ m_stackState = SourcesSet;
return existed;
}
@@ -114,9 +118,10 @@ void CompilerStack::setSource(string const& _sourceCode)
bool CompilerStack::parse()
{
//reset
+ if(m_stackState != SourcesSet)
+ return false;
m_errors.clear();
ASTNode::resetID();
- m_parseSuccessful = false;
if (SemVerVersion{string(VersionString)}.isPrerelease())
{
@@ -128,14 +133,12 @@ bool CompilerStack::parse()
vector<string> sourcesToParse;
for (auto const& s: m_sources)
sourcesToParse.push_back(s.first);
- map<string, SourceUnit const*> sourceUnitsByName;
for (size_t i = 0; i < sourcesToParse.size(); ++i)
{
string const& path = sourcesToParse[i];
Source& source = m_sources[path];
source.scanner->reset();
source.ast = Parser(m_errors).parse(source.scanner);
- sourceUnitsByName[path] = source.ast.get();
if (!source.ast)
solAssert(!Error::containsOnlyWarnings(m_errors), "Parser returned null but did not report error.");
else
@@ -150,10 +153,19 @@ bool CompilerStack::parse()
}
}
}
- if (!Error::containsOnlyWarnings(m_errors))
- // errors while parsing. should stop before type checking
+ if (Error::containsOnlyWarnings(m_errors))
+ {
+ m_stackState = ParsingSuccessful;
+ return true;
+ }
+ else
return false;
+}
+bool CompilerStack::analyze()
+{
+ if (m_stackState != ParsingSuccessful)
+ return false;
resolveImports();
bool noErrors = true;
@@ -173,6 +185,9 @@ bool CompilerStack::parse()
if (!resolver.registerDeclarations(*source->ast))
return false;
+ map<string, SourceUnit const*> sourceUnitsByName;
+ for (auto& source: m_sources)
+ sourceUnitsByName[source.first] = source.second.ast.get();
for (Source const* source: m_sourceOrder)
if (!resolver.performImports(*source->ast, sourceUnitsByName))
return false;
@@ -235,8 +250,13 @@ bool CompilerStack::parse()
noErrors = false;
}
- m_parseSuccessful = noErrors;
- return m_parseSuccessful;
+ if (noErrors)
+ {
+ m_stackState = AnalysisSuccessful;
+ return true;
+ }
+ else
+ return false;
}
bool CompilerStack::parse(string const& _sourceCode)
@@ -245,9 +265,20 @@ bool CompilerStack::parse(string const& _sourceCode)
return parse();
}
+bool CompilerStack::parseAndAnalyze()
+{
+ return parse() && analyze();
+}
+
+bool CompilerStack::parseAndAnalyze(std::string const& _sourceCode)
+{
+ setSource(_sourceCode);
+ return parseAndAnalyze();
+}
+
vector<string> CompilerStack::contractNames() const
{
- if (!m_parseSuccessful)
+ if (m_stackState < AnalysisSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
vector<string> contractNames;
for (auto const& contract: m_contracts)
@@ -258,8 +289,8 @@ vector<string> CompilerStack::contractNames() const
bool CompilerStack::compile(bool _optimize, unsigned _runs, map<string, h160> const& _libraries)
{
- if (!m_parseSuccessful)
- if (!parse())
+ if (m_stackState < AnalysisSuccessful)
+ if (!parseAndAnalyze())
return false;
m_optimize = _optimize;
@@ -272,12 +303,13 @@ bool CompilerStack::compile(bool _optimize, unsigned _runs, map<string, h160> co
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
compileContract(*contract, compiledContracts);
this->link();
+ m_stackState = CompilationSuccessful;
return true;
}
bool CompilerStack::compile(string const& _sourceCode, bool _optimize, unsigned _runs)
{
- return parse(_sourceCode) && compile(_optimize, _runs);
+ return parseAndAnalyze(_sourceCode) && compile(_optimize, _runs);
}
void CompilerStack::link()
@@ -419,7 +451,7 @@ Json::Value const& CompilerStack::interface(string const& _contractName) const
Json::Value const& CompilerStack::metadata(string const& _contractName, DocumentationType _type) const
{
- if (!m_parseSuccessful)
+ if (m_stackState < AnalysisSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
return metadata(contract(_contractName), _type);
@@ -427,7 +459,7 @@ Json::Value const& CompilerStack::metadata(string const& _contractName, Document
Json::Value const& CompilerStack::metadata(Contract const& _contract, DocumentationType _type) const
{
- if (!m_parseSuccessful)
+ if (m_stackState < AnalysisSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
solAssert(_contract.contract, "");
@@ -458,7 +490,7 @@ Json::Value const& CompilerStack::metadata(Contract const& _contract, Documentat
string const& CompilerStack::onChainMetadata(string const& _contractName) const
{
- if (!m_parseSuccessful)
+ if (m_stackState != CompilationSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
return contract(_contractName).onChainMetadata;
diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h
index d46c99be..c1d344ca 100644
--- a/libsolidity/interface/CompilerStack.h
+++ b/libsolidity/interface/CompilerStack.h
@@ -103,6 +103,16 @@ public:
/// Sets the given source code as the only source unit apart from standard sources and parses it.
/// @returns false on error.
bool parse(std::string const& _sourceCode);
+ /// performs the analyisis steps (imports, scopesetting, syntaxCheck, referenceResolving,
+ /// typechecking, staticAnalysis) on previously set sources
+ /// @returns false on error.
+ bool analyze();
+ /// Parses and analyzes all source units that were added
+ /// @returns false on error.
+ bool parseAndAnalyze();
+ /// Sets the given source code as the only source unit apart from standard sources and parses and analyzes it.
+ /// @returns false on error.
+ bool parseAndAnalyze(std::string const& _sourceCode);
/// @returns a list of the contract names in the sources.
std::vector<std::string> contractNames() const;
std::string defaultContractName() const;
@@ -226,6 +236,13 @@ private:
mutable std::unique_ptr<std::string const> sourceMapping;
mutable std::unique_ptr<std::string const> runtimeSourceMapping;
};
+ enum State {
+ Empty,
+ SourcesSet,
+ ParsingSuccessful,
+ AnalysisSuccessful,
+ CompilationSuccessful
+ };
/// Loads the missing sources from @a _ast (named @a _path) using the callback
/// @a m_readFile and stores the absolute paths of all imports in the AST annotations.
@@ -266,7 +283,6 @@ private:
/// list of path prefix remappings, e.g. mylibrary: github.com/ethereum = /usr/local/ethereum
/// "context:prefix=target"
std::vector<Remapping> m_remappings;
- bool m_parseSuccessful;
std::map<std::string const, Source> m_sources;
std::shared_ptr<GlobalContext> m_globalContext;
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>> m_scopes;
@@ -275,6 +291,7 @@ private:
std::string m_formalTranslation;
ErrorList m_errors;
bool m_metadataLiteralSources = false;
+ State m_stackState = Empty;
};
}
diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp
index 2b5e861b..223cc15d 100644
--- a/libsolidity/interface/StandardCompiler.cpp
+++ b/libsolidity/interface/StandardCompiler.cpp
@@ -391,10 +391,10 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
// ABI, documentation and metadata
Json::Value contractData(Json::objectValue);
- contractData["abi"] = dev::jsonCompactPrint(m_compilerStack.metadata(contractName, DocumentationType::ABIInterface));
+ contractData["abi"] = m_compilerStack.metadata(contractName, DocumentationType::ABIInterface);
contractData["metadata"] = m_compilerStack.onChainMetadata(contractName);
- contractData["userdoc"] = dev::jsonCompactPrint(m_compilerStack.metadata(contractName, DocumentationType::NatspecUser));
- contractData["devdoc"] = dev::jsonCompactPrint(m_compilerStack.metadata(contractName, DocumentationType::NatspecDev));
+ contractData["userdoc"] = m_compilerStack.metadata(contractName, DocumentationType::NatspecUser);
+ contractData["devdoc"] = m_compilerStack.metadata(contractName, DocumentationType::NatspecDev);
// EVM
Json::Value evmData(Json::objectValue);
@@ -425,30 +425,6 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
}
output["contracts"] = contractsOutput;
- {
- ErrorList formalErrors;
- if (m_compilerStack.prepareFormalAnalysis(&formalErrors))
- output["why3"] = m_compilerStack.formalTranslation();
-
- for (auto const& error: formalErrors)
- {
- auto err = dynamic_pointer_cast<Error const>(error);
-
- errors.append(formatErrorWithException(
- *error,
- err->type() == Error::Type::Warning,
- err->typeName(),
- "general",
- "",
- scannerFromSourceName
- ));
- }
-
- // FIXME!!
- if (!formalErrors.empty())
- output["errors"] = errors;
- }
-
return output;
}
diff --git a/std/StandardToken.sol b/std/StandardToken.sol
index 4dad8541..51f925e0 100644
--- a/std/StandardToken.sol
+++ b/std/StandardToken.sol
@@ -25,7 +25,7 @@ contract StandardToken is Token {
return doTransfer(msg.sender, _to, _value);
}
- function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
+ function transferFrom(address _from, address _to, uint256 _value) returns (bool) {
if (m_allowance[_from][msg.sender] >= _value) {
if (doTransfer(_from, _to, _value)) {
m_allowance[_from][msg.sender] -= _value;
@@ -53,7 +53,7 @@ contract StandardToken is Token {
return true;
}
- function allowance(address _owner, address _spender) constant returns (uint256 remaining) {
+ function allowance(address _owner, address _spender) constant returns (uint256) {
return m_allowance[_owner][_spender];
}
}
diff --git a/test/fuzzer.cpp b/test/fuzzer.cpp
index afa50671..053d880f 100644
--- a/test/fuzzer.cpp
+++ b/test/fuzzer.cpp
@@ -205,7 +205,7 @@ Allowed options)",
catch (po::error const& _exception)
{
cerr << _exception.what() << endl;
- return false;
+ return 1;
}
if (arguments.count("quiet"))
diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp
index 0972ce82..d23815b4 100644
--- a/test/libsolidity/ASTJSON.cpp
+++ b/test/libsolidity/ASTJSON.cpp
@@ -41,7 +41,7 @@ BOOST_AUTO_TEST_CASE(smoke_test)
{
CompilerStack c;
c.addSource("a", "contract C {}");
- c.parse();
+ c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json();
@@ -52,7 +52,7 @@ BOOST_AUTO_TEST_CASE(source_location)
{
CompilerStack c;
c.addSource("a", "contract C { function f() { var x = 2; x++; } }");
- c.parse();
+ c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json();
@@ -66,7 +66,7 @@ BOOST_AUTO_TEST_CASE(inheritance_specifier)
{
CompilerStack c;
c.addSource("a", "contract C1 {} contract C2 is C1 {}");
- c.parse();
+ c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json();
@@ -81,7 +81,7 @@ BOOST_AUTO_TEST_CASE(using_for_directive)
{
CompilerStack c;
c.addSource("a", "library L {} contract C { using L for uint; }");
- c.parse();
+ c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json();
@@ -91,14 +91,14 @@ BOOST_AUTO_TEST_CASE(using_for_directive)
BOOST_CHECK_EQUAL(usingFor["children"][0]["name"], "UserDefinedTypeName");
BOOST_CHECK_EQUAL(usingFor["children"][0]["attributes"]["name"], "L");
BOOST_CHECK_EQUAL(usingFor["children"][1]["name"], "ElementaryTypeName");
- BOOST_CHECK_EQUAL(usingFor["children"][1]["attributes"]["name"], "uint");
+ BOOST_CHECK_EQUAL(usingFor["children"][1]["attributes"]["name"], "uint");
}
BOOST_AUTO_TEST_CASE(enum_value)
{
CompilerStack c;
c.addSource("a", "contract C { enum E { A, B } }");
- c.parse();
+ c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json();
@@ -115,7 +115,7 @@ BOOST_AUTO_TEST_CASE(modifier_definition)
{
CompilerStack c;
c.addSource("a", "contract C { modifier M(uint i) { _; } function F() M(1) {} }");
- c.parse();
+ c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json();
@@ -129,7 +129,7 @@ BOOST_AUTO_TEST_CASE(modifier_invocation)
{
CompilerStack c;
c.addSource("a", "contract C { modifier M(uint i) { _; } function F() M(1) {} }");
- c.parse();
+ c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json();
@@ -145,7 +145,7 @@ BOOST_AUTO_TEST_CASE(event_definition)
{
CompilerStack c;
c.addSource("a", "contract C { event E(); }");
- c.parse();
+ c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json();
@@ -159,7 +159,7 @@ BOOST_AUTO_TEST_CASE(array_type_name)
{
CompilerStack c;
c.addSource("a", "contract C { uint[] i; }");
- c.parse();
+ c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json();
@@ -172,7 +172,7 @@ BOOST_AUTO_TEST_CASE(placeholder_statement)
{
CompilerStack c;
c.addSource("a", "contract C { modifier M { _; } }");
- c.parse();
+ c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json();
@@ -185,7 +185,7 @@ BOOST_AUTO_TEST_CASE(non_utf8)
{
CompilerStack c;
c.addSource("a", "contract C { function f() { var x = hex\"ff\"; } }");
- c.parse();
+ c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json();
@@ -204,7 +204,7 @@ BOOST_AUTO_TEST_CASE(function_type)
"contract C { function f(function() external payable returns (uint) x) "
"returns (function() external constant returns (uint)) {} }"
);
- c.parse();
+ c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json();
diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp
index 043d74ed..bdcc5b10 100644
--- a/test/libsolidity/SolidityABIJSON.cpp
+++ b/test/libsolidity/SolidityABIJSON.cpp
@@ -42,7 +42,7 @@ public:
void checkInterface(std::string const& _code, std::string const& _expectedInterfaceString)
{
- ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parse("pragma solidity >=0.0;\n" + _code), "Parsing contract failed");
+ ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parseAndAnalyze("pragma solidity >=0.0;\n" + _code), "Parsing contract failed");
Json::Value generatedInterface = m_compilerStack.metadata("", DocumentationType::ABIInterface);
Json::Value expectedInterface;
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index f88f600a..3a9f7295 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -216,7 +216,7 @@ BOOST_AUTO_TEST_CASE(smoke_test)
char const* text = R"(
contract test {
uint256 stateVariable1;
- function fun(uint256 arg1) { uint256 y; }
+ function fun(uint256 arg1) { uint256 y; y = arg1; }
}
)";
CHECK_SUCCESS(text);
@@ -237,8 +237,8 @@ BOOST_AUTO_TEST_CASE(double_function_declaration)
{
char const* text = R"(
contract test {
- function fun() { uint x; }
- function fun() { uint x; }
+ function fun() { }
+ function fun() { }
}
)";
CHECK_ERROR(text, DeclarationError, "");
@@ -262,7 +262,7 @@ BOOST_AUTO_TEST_CASE(name_shadowing)
char const* text = R"(
contract test {
uint256 variable;
- function f() { uint32 variable; }
+ function f() { uint32 variable; variable = 2; }
}
)";
CHECK_SUCCESS(text);
@@ -273,7 +273,7 @@ BOOST_AUTO_TEST_CASE(name_references)
char const* text = R"(
contract test {
uint256 variable;
- function f(uint256 arg) returns (uint out) { f(variable); test; out; }
+ function f(uint256) returns (uint out) { f(variable); test; out; }
}
)";
CHECK_SUCCESS(text);
@@ -404,8 +404,8 @@ BOOST_AUTO_TEST_CASE(type_checking_function_call)
{
char const* text = R"(
contract test {
- function f() returns (bool r) { return g(12, true) == 3; }
- function g(uint256 a, bool b) returns (uint256 r) { }
+ function f() returns (bool) { return g(12, true) == 3; }
+ function g(uint256, bool) returns (uint256) { }
}
)";
CHECK_SUCCESS(text);
@@ -523,12 +523,12 @@ BOOST_AUTO_TEST_CASE(forward_function_reference)
{
char const* text = R"(
contract First {
- function fun() returns (bool ret) {
+ function fun() returns (bool) {
return Second(1).fun(1, true, 3) > 0;
}
}
contract Second {
- function fun(uint a, bool b, uint c) returns (uint ret) {
+ function fun(uint, bool, uint) returns (uint) {
if (First(2).fun() == true) return 1;
}
}
@@ -636,10 +636,10 @@ BOOST_AUTO_TEST_CASE(abstract_contract_constructor_args_not_provided)
{
ASTPointer<SourceUnit> sourceUnit;
char const* text = R"(
- contract BaseBase { function BaseBase(uint j); }
+ contract BaseBase { function BaseBase(uint); }
contract base is BaseBase { function foo(); }
contract derived is base {
- function derived(uint i) {}
+ function derived(uint) {}
function foo() {}
}
)";
@@ -701,7 +701,7 @@ BOOST_AUTO_TEST_CASE(function_canonical_signature_type_aliases)
ASTPointer<SourceUnit> sourceUnit;
char const* text = R"(
contract Test {
- function boo(uint arg1, bytes32 arg2, address arg3) returns (uint ret) {
+ function boo(uint, bytes32, address) returns (uint ret) {
ret = 5;
}
}
@@ -725,7 +725,7 @@ BOOST_AUTO_TEST_CASE(function_external_types)
uint a;
}
contract Test {
- function boo(uint arg2, bool arg3, bytes8 arg4, bool[2] pairs, uint[] dynamic, C carg, address[] addresses) external returns (uint ret) {
+ function boo(uint, bool, bytes8, bool[2], uint[], C, address[]) external returns (uint ret) {
ret = 5;
}
}
@@ -914,7 +914,7 @@ BOOST_AUTO_TEST_CASE(complex_inheritance)
{
char const* text = R"(
contract A { function f() { uint8 x = C(0).g(); } }
- contract B { function f() {} function g() returns (uint8 r) {} }
+ contract B { function f() {} function g() returns (uint8) {} }
contract C is A, B { }
)";
CHECK_SUCCESS(text);
@@ -1672,7 +1672,7 @@ BOOST_AUTO_TEST_CASE(exp_warn_literal_base)
{
char const* sourceCode = R"(
contract test {
- function f() returns(uint d) {
+ function f() returns(uint) {
uint8 x = 100;
return 10**x;
}
@@ -1681,7 +1681,7 @@ BOOST_AUTO_TEST_CASE(exp_warn_literal_base)
CHECK_WARNING(sourceCode, "might overflow");
sourceCode = R"(
contract test {
- function f() returns(uint d) {
+ function f() returns(uint) {
uint8 x = 100;
return uint8(10)**x;
}
@@ -1690,7 +1690,7 @@ BOOST_AUTO_TEST_CASE(exp_warn_literal_base)
CHECK_SUCCESS(sourceCode);
sourceCode = R"(
contract test {
- function f() returns(uint d) {
+ function f() returns(uint) {
return 2**80;
}
}
@@ -1941,10 +1941,10 @@ BOOST_AUTO_TEST_CASE(test_for_bug_override_function_with_bytearray_type)
{
char const* sourceCode = R"(
contract Vehicle {
- function f(bytes _a) external returns (uint256 r) {r = 1;}
+ function f(bytes) external returns (uint256 r) {r = 1;}
}
contract Bike is Vehicle {
- function f(bytes _a) external returns (uint256 r) {r = 42;}
+ function f(bytes) external returns (uint256 r) {r = 42;}
}
)";
ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(sourceCode), "Parsing and Name Resolving failed");
@@ -2570,6 +2570,7 @@ BOOST_AUTO_TEST_CASE(storage_location_local_variables)
uint[] storage x;
uint[] memory y;
uint[] memory z;
+ x;y;z;
}
}
)";
@@ -2625,6 +2626,7 @@ BOOST_AUTO_TEST_CASE(uninitialized_mapping_variable)
contract C {
function f() {
mapping(uint => uint) x;
+ x;
}
}
)";
@@ -2637,6 +2639,7 @@ BOOST_AUTO_TEST_CASE(uninitialized_mapping_array_variable)
contract C {
function f() {
mapping(uint => uint)[] x;
+ x;
}
}
)";
@@ -2789,6 +2792,7 @@ BOOST_AUTO_TEST_CASE(literal_strings)
function f() {
string memory long = "01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
string memory short = "123";
+ long; short;
}
}
)";
@@ -2865,7 +2869,7 @@ BOOST_AUTO_TEST_CASE(call_to_library_function)
{
char const* text = R"(
library Lib {
- function min(uint x, uint y) returns (uint);
+ function min(uint, uint) returns (uint);
}
contract Test {
function f() {
@@ -2963,9 +2967,9 @@ BOOST_AUTO_TEST_CASE(cyclic_binary_dependency_via_inheritance)
BOOST_AUTO_TEST_CASE(multi_variable_declaration_fail)
{
char const* text = R"(
- contract C { function f() { var (x,y); } }
+ contract C { function f() { var (x,y); x = 1; y = 1;} }
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Assignment necessary for type detection.");
}
BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fine)
@@ -2982,6 +2986,7 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fine)
var (,e,g) = two();
var (,,) = three();
var () = none();
+ a;b;c;d;e;g;
}
}
)";
@@ -3040,6 +3045,7 @@ BOOST_AUTO_TEST_CASE(tuples)
var (b,) = (1,);
var (c,d) = (1, 2 + a);
var (e,) = (1, 2, b);
+ a;b;c;d;e;
}
}
)";
@@ -3197,7 +3203,7 @@ BOOST_AUTO_TEST_CASE(using_for_overload)
library D {
struct s { uint a; }
function mul(s storage self, uint x) returns (uint) { return self.a *= x; }
- function mul(s storage self, bytes32 x) returns (bytes32) { }
+ function mul(s storage, bytes32) returns (bytes32) { }
}
contract C {
using D for D.s;
@@ -3309,6 +3315,7 @@ BOOST_AUTO_TEST_CASE(create_memory_arrays)
L.S[][] memory x = new L.S[][](10);
var y = new uint[](20);
var z = new bytes(size);
+ x;y;z;
}
}
)";
@@ -3355,8 +3362,8 @@ BOOST_AUTO_TEST_CASE(function_overload_array_type)
{
char const* text = R"(
contract M {
- function f(uint[] values);
- function f(int[] values);
+ function f(uint[]);
+ function f(int[]);
}
)";
CHECK_SUCCESS(text);
@@ -3654,16 +3661,20 @@ BOOST_AUTO_TEST_CASE(conditional_with_all_types)
uint x;
uint y;
uint g = true ? x : y;
+ g += 1; // Avoid unused var warning
// integer constants
uint h = true ? 1 : 3;
+ h += 1; // Avoid unused var warning
// string literal
var i = true ? "hello" : "world";
+ i = "used"; //Avoid unused var warning
}
function f2() {
// bool
bool j = true ? true : false;
+ j = j && true; // Avoid unused var warning
// real is not there yet.
@@ -3671,15 +3682,19 @@ BOOST_AUTO_TEST_CASE(conditional_with_all_types)
byte[2] memory a;
byte[2] memory b;
var k = true ? a : b;
+ k[0] = 0; //Avoid unused var warning
bytes memory e;
bytes memory f;
var l = true ? e : f;
+ l[0] = 0; // Avoid unused var warning
// fixed bytes
bytes2 c;
bytes2 d;
var m = true ? c : d;
+ m &= m;
+
}
function f3() {
// contract doesn't fit in here
@@ -3689,7 +3704,7 @@ BOOST_AUTO_TEST_CASE(conditional_with_all_types)
// function
var r = true ? fun_x : fun_y;
-
+ r(); // Avoid unused var warning
// enum
small enum_x;
small enum_y;
@@ -3697,13 +3712,13 @@ BOOST_AUTO_TEST_CASE(conditional_with_all_types)
// tuple
var (n, o) = true ? (1, 2) : (3, 4);
-
+ (n, o) = (o, n); // Avoid unused var warning
// mapping
var p = true ? table1 : table2;
-
+ p[0] = 0; // Avoid unused var warning
// typetype
var q = true ? uint32(1) : uint32(2);
-
+ q += 1; // Avoid unused var warning
// modifier doesn't fit in here
// magic doesn't fit in here
@@ -3753,6 +3768,7 @@ BOOST_AUTO_TEST_CASE(uint7_and_uintM_as_identifier)
uint7 = 5;
string memory intM;
uint bytesM = 21;
+ intM; bytesM;
}
}
)";
@@ -3804,6 +3820,7 @@ BOOST_AUTO_TEST_CASE(int10abc_is_identifier)
function f() {
uint uint10abc = 3;
int int10abc = 4;
+ uint10abc; int10abc;
}
}
)";
@@ -3888,6 +3905,7 @@ BOOST_AUTO_TEST_CASE(fixed_type_int_conversion)
int128 b = 4;
fixed c = b;
ufixed d = a;
+ c; d;
}
}
)";
@@ -3901,6 +3919,7 @@ BOOST_AUTO_TEST_CASE(fixed_type_rational_int_conversion)
function f() {
fixed c = 3;
ufixed d = 4;
+ c; d;
}
}
)";
@@ -3914,6 +3933,7 @@ BOOST_AUTO_TEST_CASE(fixed_type_rational_fraction_conversion)
function f() {
fixed a = 4.5;
ufixed d = 2.5;
+ a; d;
}
}
)";
@@ -3927,6 +3947,7 @@ BOOST_AUTO_TEST_CASE(invalid_int_implicit_conversion_from_fixed)
function f() {
fixed a = 4.5;
int b = a;
+ a; b;
}
}
)";
@@ -3938,12 +3959,33 @@ BOOST_AUTO_TEST_CASE(rational_unary_operation)
char const* text = R"(
contract test {
function f() {
+ ufixed8x16 a = 3.25;
+ fixed8x16 b = -3.25;
+ a;
+ b;
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+ text = R"(
+ contract test {
+ function f() {
ufixed8x16 a = +3.25;
fixed8x16 b = -3.25;
+ a; b;
}
}
)";
- CHECK_SUCCESS(text);
+ CHECK_WARNING(text,"Use of unary + is deprecated");
+ text = R"(
+ contract test {
+ function f(uint x) {
+ uint y = +x;
+ y;
+ }
+ }
+ )";
+ CHECK_WARNING(text,"Use of unary + is deprecated");
}
BOOST_AUTO_TEST_CASE(leading_zero_rationals_convert)
@@ -3955,6 +3997,7 @@ BOOST_AUTO_TEST_CASE(leading_zero_rationals_convert)
ufixed0x56 b = 0.0000000000000006661338147750939242541790008544921875;
fixed0x8 c = -0.5;
fixed0x56 d = -0.0000000000000006661338147750939242541790008544921875;
+ a; b; c; d;
}
}
)";
@@ -3972,6 +4015,7 @@ BOOST_AUTO_TEST_CASE(size_capabilities_of_fixed_point_types)
fixed248x8 d = -123456781234567979695948382928485849359686494864095409282048094275023098123.5;
fixed0x256 e = -0.93322335481643744342575580035176794825198893968114429702091846411734101080123092162893656820177312738451291806995868682861328125;
fixed0x256 g = -0.00011788606643744342575580035176794825198893968114429702091846411734101080123092162893656820177312738451291806995868682861328125;
+ a; b; c; d; e; g;
}
}
)";
@@ -4011,6 +4055,7 @@ BOOST_AUTO_TEST_CASE(fixed_type_valid_explicit_conversions)
ufixed0x256 a = ufixed0x256(1/3);
ufixed0x248 b = ufixed0x248(1/3);
ufixed0x8 c = ufixed0x8(1/3);
+ a; b; c;
}
}
)";
@@ -4142,6 +4187,7 @@ BOOST_AUTO_TEST_CASE(rational_to_fixed_literal_expression)
ufixed8x248 e = ufixed8x248(35.245 % 12.9);
ufixed8x248 f = ufixed8x248(1.2 % 2);
fixed g = 2 ** -2;
+ a; b; c; d; e; f; g;
}
}
)";
@@ -4252,6 +4298,7 @@ BOOST_AUTO_TEST_CASE(var_capable_of_holding_constant_rationals)
var a = 0.12345678;
var b = 12345678.352;
var c = 0.00000009;
+ a; b; c;
}
}
)";
@@ -4264,6 +4311,7 @@ BOOST_AUTO_TEST_CASE(var_and_rational_with_tuple)
contract test {
function f() {
var (a, b) = (.5, 1/3);
+ a; b;
}
}
)";
@@ -4337,6 +4385,7 @@ BOOST_AUTO_TEST_CASE(zero_handling)
function f() {
fixed8x8 a = 0;
ufixed8x8 b = 0;
+ a; b;
}
}
)";
@@ -4842,6 +4891,7 @@ BOOST_AUTO_TEST_CASE(function_type_arrays)
function(uint) returns (uint)[10] storage b = y;
function(uint) external returns (uint)[] memory c;
c = new function(uint) external returns (uint)[](200);
+ a; b;
}
}
)";
@@ -4900,8 +4950,8 @@ BOOST_AUTO_TEST_CASE(external_function_to_function_type_calldata_parameter)
// when converting to a function type.
char const* text = R"(
contract C {
- function f(function(bytes memory x) external g) { }
- function callback(bytes x) external {}
+ function f(function(bytes memory) external g) { }
+ function callback(bytes) external {}
function g() {
f(this.callback);
}
@@ -5270,6 +5320,7 @@ BOOST_AUTO_TEST_CASE(invalid_address_checksum)
contract C {
function f() {
var x = 0xFA0bFc97E48458494Ccd857e1A85DC91F7F0046E;
+ x;
}
}
)";
@@ -5282,6 +5333,7 @@ BOOST_AUTO_TEST_CASE(invalid_address_no_checksum)
contract C {
function f() {
var x = 0xfa0bfc97e48458494ccd857e1a85dc91f7f0046e;
+ x;
}
}
)";
@@ -5294,6 +5346,7 @@ BOOST_AUTO_TEST_CASE(invalid_address_length)
contract C {
function f() {
var x = 0xA0bFc97E48458494Ccd857e1A85DC91F7F0046E;
+ x;
}
}
)";
@@ -5328,6 +5381,7 @@ BOOST_AUTO_TEST_CASE(address_methods)
bool delegatecallRet = addr.delegatecall();
bool sendRet = addr.send(1);
addr.transfer(1);
+ callRet; callcodeRet; delegatecallRet; sendRet;
}
}
)";
@@ -5554,6 +5608,119 @@ BOOST_AUTO_TEST_CASE(pure_statement_check_for_regular_for_loop)
success(text);
}
+BOOST_AUTO_TEST_CASE(warn_unused_local)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ uint a;
+ }
+ }
+ )";
+ CHECK_WARNING(text, "Unused");
+}
+
+BOOST_AUTO_TEST_CASE(warn_unused_local_assigned)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ var a = 1;
+ }
+ }
+ )";
+ CHECK_WARNING(text, "Unused");
+}
+
+BOOST_AUTO_TEST_CASE(warn_unused_param)
+{
+ char const* text = R"(
+ contract C {
+ function f(uint a) {
+ }
+ }
+ )";
+ CHECK_WARNING(text, "Unused");
+ text = R"(
+ contract C {
+ function f(uint a) {
+ }
+ }
+ )";
+ success(text);
+}
+
+BOOST_AUTO_TEST_CASE(warn_unused_return_param)
+{
+ char const* text = R"(
+ contract C {
+ function f() returns (uint a) {
+ }
+ }
+ )";
+ CHECK_WARNING(text, "Unused");
+ text = R"(
+ contract C {
+ function f() returns (uint a) {
+ return;
+ }
+ }
+ )";
+ CHECK_WARNING(text, "Unused");
+ text = R"(
+ contract C {
+ function f() returns (uint) {
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+ text = R"(
+ contract C {
+ function f() returns (uint a) {
+ a = 1;
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+ text = R"(
+ contract C {
+ function f() returns (uint a) {
+ return 1;
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+}
+
+BOOST_AUTO_TEST_CASE(no_unused_warnings)
+{
+ char const* text = R"(
+ contract C {
+ function f(uint a) returns (uint b) {
+ uint c = 1;
+ b = a + c;
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+}
+
+BOOST_AUTO_TEST_CASE(no_unused_dec_after_use)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ a = 7;
+ uint a;
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+}
+
+
+
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp
index ac55382b..78c1a0ee 100644
--- a/test/libsolidity/SolidityNatspecJSON.cpp
+++ b/test/libsolidity/SolidityNatspecJSON.cpp
@@ -45,7 +45,7 @@ public:
bool _userDocumentation
)
{
- ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parse("pragma solidity >=0.0;\n" + _code), "Parsing failed");
+ ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parseAndAnalyze("pragma solidity >=0.0;\n" + _code), "Parsing failed");
Json::Value generatedDocumentation;
if (_userDocumentation)
@@ -63,7 +63,7 @@ public:
void expectNatspecError(std::string const& _code)
{
- BOOST_CHECK(!m_compilerStack.parse(_code));
+ BOOST_CHECK(!m_compilerStack.parseAndAnalyze(_code));
BOOST_REQUIRE(Error::containsErrorOfType(m_compilerStack.errors(), Error::Type::DocstringParsingError));
}
diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp
index bcf6cd49..d705e3c8 100644
--- a/test/libsolidity/SolidityOptimizer.cpp
+++ b/test/libsolidity/SolidityOptimizer.cpp
@@ -74,16 +74,17 @@ public:
void compileBothVersions(
std::string const& _sourceCode,
u256 const& _value = 0,
- std::string const& _contractName = ""
+ std::string const& _contractName = "",
+ unsigned const _optimizeRuns = 200
)
{
- bytes nonOptimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, false);
+ bytes nonOptimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, false, _optimizeRuns);
m_nonOptimizedContract = m_contractAddress;
- bytes optimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, true);
+ bytes optimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, true, _optimizeRuns);
size_t nonOptimizedSize = numInstructions(nonOptimizedBytecode);
size_t optimizedSize = numInstructions(optimizedBytecode);
BOOST_CHECK_MESSAGE(
- optimizedSize < nonOptimizedSize,
+ _optimizeRuns < 50 || optimizedSize < nonOptimizedSize,
string("Optimizer did not reduce bytecode size. Non-optimized size: ") +
std::to_string(nonOptimizedSize) + " - optimized size: " +
std::to_string(optimizedSize)
@@ -1191,31 +1192,42 @@ BOOST_AUTO_TEST_CASE(clear_unreachable_code)
BOOST_AUTO_TEST_CASE(computing_constants)
{
char const* sourceCode = R"(
- contract c {
- uint a;
- uint b;
- uint c;
- function set() returns (uint a, uint b, uint c) {
- a = 0x77abc0000000000000000000000000000000000000000000000000000000001;
- b = 0x817416927846239487123469187231298734162934871263941234127518276;
+ contract C {
+ uint m_a;
+ uint m_b;
+ uint m_c;
+ uint m_d;
+ function C() {
+ set();
+ }
+ function set() returns (uint) {
+ m_a = 0x77abc0000000000000000000000000000000000000000000000000000000001;
+ m_b = 0x817416927846239487123469187231298734162934871263941234127518276;
g();
+ return 1;
}
function g() {
- b = 0x817416927846239487123469187231298734162934871263941234127518276;
- c = 0x817416927846239487123469187231298734162934871263941234127518276;
+ m_b = 0x817416927846239487123469187231298734162934871263941234127518276;
+ m_c = 0x817416927846239487123469187231298734162934871263941234127518276;
+ h();
+ }
+ function h() {
+ m_d = 0xff05694900000000000000000000000000000000000000000000000000000000;
}
- function get() returns (uint ra, uint rb, uint rc) {
- ra = a;
- rb = b;
- rc = c ;
+ function get() returns (uint ra, uint rb, uint rc, uint rd) {
+ ra = m_a;
+ rb = m_b;
+ rc = m_c;
+ rd = m_d;
}
}
)";
- compileBothVersions(sourceCode);
+ compileBothVersions(sourceCode, 0, "C", 1);
+ compareVersions("get()");
compareVersions("set()");
compareVersions("get()");
- bytes optimizedBytecode = compileAndRunWithOptimizer(sourceCode, 0, "c", true, 1);
+ bytes optimizedBytecode = compileAndRunWithOptimizer(sourceCode, 0, "C", true, 1);
bytes complicatedConstant = toBigEndian(u256("0x817416927846239487123469187231298734162934871263941234127518276"));
unsigned occurrences = 0;
for (auto iter = optimizedBytecode.cbegin(); iter < optimizedBytecode.cend(); ++occurrences)
diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp
index 9b53e841..ffb0e2c6 100644
--- a/test/libsolidity/StandardCompiler.cpp
+++ b/test/libsolidity/StandardCompiler.cpp
@@ -68,7 +68,10 @@ bool containsAtMostWarnings(Json::Value const& _compilerResult)
BOOST_REQUIRE(error.isObject());
BOOST_REQUIRE(error["severity"].isString());
if (error["severity"].asString() != "warning")
+ {
+ cout << error << std::endl;
return false;
+ }
}
return true;
@@ -232,12 +235,12 @@ BOOST_AUTO_TEST_CASE(basic_compilation)
BOOST_CHECK(containsAtMostWarnings(result));
Json::Value contract = getContractResult(result, "fileA", "A");
BOOST_CHECK(contract.isObject());
- BOOST_CHECK(contract["abi"].isString());
- BOOST_CHECK(contract["abi"].asString() == "[]");
- BOOST_CHECK(contract["devdoc"].isString());
- BOOST_CHECK(contract["devdoc"].asString() == "{\"methods\":{}}");
- BOOST_CHECK(contract["userdoc"].isString());
- BOOST_CHECK(contract["userdoc"].asString() == "{\"methods\":{}}");
+ BOOST_CHECK(contract["abi"].isArray());
+ BOOST_CHECK(dev::jsonCompactPrint(contract["abi"]) == "[]");
+ BOOST_CHECK(contract["devdoc"].isObject());
+ BOOST_CHECK(dev::jsonCompactPrint(contract["devdoc"]) == "{\"methods\":{}}");
+ BOOST_CHECK(contract["userdoc"].isObject());
+ BOOST_CHECK(dev::jsonCompactPrint(contract["userdoc"]) == "{\"methods\":{}}");
BOOST_CHECK(contract["evm"].isObject());
/// @TODO check evm.methodIdentifiers, legacyAssembly, bytecode, deployedBytecode
BOOST_CHECK(contract["evm"]["bytecode"].isObject());