From ec53899a10d34a0d1fd160773f24310104bdd7a8 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Wed, 23 Oct 2019 22:13:17 +0200 Subject: [PATCH] Fixes source extraction from docs. --- docs/050-breaking-changes.rst | 314 +++++++++++----------- docs/contracts/visibility-and-getters.rst | 30 +-- docs/layout-of-source-files.rst | 2 +- docs/natspec-format.rst | 30 +-- docs/security-considerations.rst | 94 +++---- scripts/isolate_tests.py | 23 +- 6 files changed, 251 insertions(+), 242 deletions(-) diff --git a/docs/050-breaking-changes.rst b/docs/050-breaking-changes.rst index 46ddc9abe..fd201d77c 100644 --- a/docs/050-breaking-changes.rst +++ b/docs/050-breaking-changes.rst @@ -292,27 +292,27 @@ Consider you have the following pre-0.5.0 contract already deployed: :: - // This will not compile with the current version of the compiler - pragma solidity ^0.4.25; - contract OldContract { - function someOldFunction(uint8 a) { - //... - } - function anotherOldFunction() constant returns (bool) { - //... - } - // ... - } + // This will not compile with the current version of the compiler + pragma solidity ^0.4.25; + contract OldContract { + function someOldFunction(uint8 a) { + //... + } + function anotherOldFunction() constant returns (bool) { + //... + } + // ... + } This will no longer compile with Solidity v0.5.0. However, you can define a compatible interface for it: :: - pragma solidity >=0.5.0 <0.7.0; - interface OldContract { - function someOldFunction(uint8 a) external; - function anotherOldFunction() external returns (bool); - } + pragma solidity >=0.5.0 <0.7.0; + interface OldContract { + function someOldFunction(uint8 a) external; + function anotherOldFunction() external returns (bool); + } Note that we did not declare ``anotherOldFunction`` to be ``view``, despite it being declared ``constant`` in the original contract. This is due to the fact that starting with Solidity v0.5.0 ``staticcall`` is used to call ``view`` functions. @@ -325,19 +325,19 @@ Given the interface defined above, you can now easily use the already deployed p :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.5.0 <0.7.0; - interface OldContract { - function someOldFunction(uint8 a) external; - function anotherOldFunction() external returns (bool); - } + interface OldContract { + function someOldFunction(uint8 a) external; + function anotherOldFunction() external returns (bool); + } - contract NewContract { - function doSomething(OldContract a) public returns (bool) { - a.someOldFunction(0x42); - return a.anotherOldFunction(); - } - } + contract NewContract { + function doSomething(OldContract a) public returns (bool) { + a.someOldFunction(0x42); + return a.anotherOldFunction(); + } + } Similarly, pre-0.5.0 libraries can be used by defining the functions of the library without implementation and supplying the address of the pre-0.5.0 library during linking (see :ref:`commandline-compiler` for how to use the @@ -345,17 +345,17 @@ commandline compiler for linking): :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.5.0 <0.7.0; - library OldLibrary { - function someFunction(uint8 a) public returns(bool); - } + library OldLibrary { + function someFunction(uint8 a) public returns(bool); + } - contract NewContract { - function f(uint8 a) public returns (bool) { - return OldLibrary.someFunction(a); - } - } + contract NewContract { + function f(uint8 a) public returns (bool) { + return OldLibrary.someFunction(a); + } + } Example @@ -368,144 +368,144 @@ Old version: :: - // This will not compile - pragma solidity ^0.4.25; + // This will not compile + pragma solidity ^0.4.25; - contract OtherContract { - uint x; - function f(uint y) external { - x = y; - } - function() payable external {} - } + contract OtherContract { + uint x; + function f(uint y) external { + x = y; + } + function() payable external {} + } - contract Old { - OtherContract other; - uint myNumber; + contract Old { + OtherContract other; + uint myNumber; - // Function mutability not provided, not an error. - function someInteger() internal returns (uint) { return 2; } + // Function mutability not provided, not an error. + function someInteger() internal returns (uint) { return 2; } - // Function visibility not provided, not an error. - // Function mutability not provided, not an error. - function f(uint x) returns (bytes) { - // Var is fine in this version. - var z = someInteger(); - x += z; - // Throw is fine in this version. - if (x > 100) - throw; - bytes b = new bytes(x); - y = -3 >> 1; - // y == -1 (wrong, should be -2) - do { - x += 1; - if (x > 10) continue; - // 'Continue' causes an infinite loop. - } while (x < 11); - // Call returns only a Bool. - bool success = address(other).call("f"); - if (!success) - revert(); - else { - // Local variables could be declared after their use. - int y; - } - return b; - } + // Function visibility not provided, not an error. + // Function mutability not provided, not an error. + function f(uint x) returns (bytes) { + // Var is fine in this version. + var z = someInteger(); + x += z; + // Throw is fine in this version. + if (x > 100) + throw; + bytes b = new bytes(x); + y = -3 >> 1; + // y == -1 (wrong, should be -2) + do { + x += 1; + if (x > 10) continue; + // 'Continue' causes an infinite loop. + } while (x < 11); + // Call returns only a Bool. + bool success = address(other).call("f"); + if (!success) + revert(); + else { + // Local variables could be declared after their use. + int y; + } + return b; + } - // No need for an explicit data location for 'arr' - function g(uint[] arr, bytes8 x, OtherContract otherContract) public { - otherContract.transfer(1 ether); + // No need for an explicit data location for 'arr' + function g(uint[] arr, bytes8 x, OtherContract otherContract) public { + otherContract.transfer(1 ether); - // Since uint32 (4 bytes) is smaller than bytes8 (8 bytes), - // the first 4 bytes of x will be lost. This might lead to - // unexpected behavior since bytesX are right padded. - uint32 y = uint32(x); - myNumber += y + msg.value; - } - } + // Since uint32 (4 bytes) is smaller than bytes8 (8 bytes), + // the first 4 bytes of x will be lost. This might lead to + // unexpected behavior since bytesX are right padded. + uint32 y = uint32(x); + myNumber += y + msg.value; + } + } New version: :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.5.0 <0.7.0; - contract OtherContract { - uint x; - function f(uint y) external { - x = y; - } - function() payable external {} - } + contract OtherContract { + uint x; + function f(uint y) external { + x = y; + } + function() payable external {} + } - contract New { - OtherContract other; - uint myNumber; + contract New { + OtherContract other; + uint myNumber; - // Function mutability must be specified. - function someInteger() internal pure returns (uint) { return 2; } + // Function mutability must be specified. + function someInteger() internal pure returns (uint) { return 2; } - // Function visibility must be specified. - // Function mutability must be specified. - function f(uint x) public returns (bytes memory) { - // The type must now be explicitly given. - uint z = someInteger(); - x += z; - // Throw is now disallowed. - require(x > 100); - int y = -3 >> 1; - // y == -2 (correct) - do { - x += 1; - if (x > 10) continue; - // 'Continue' jumps to the condition below. - } while (x < 11); + // Function visibility must be specified. + // Function mutability must be specified. + function f(uint x) public returns (bytes memory) { + // The type must now be explicitly given. + uint z = someInteger(); + x += z; + // Throw is now disallowed. + require(x > 100); + int y = -3 >> 1; + require(y == -2); + do { + x += 1; + if (x > 10) continue; + // 'Continue' jumps to the condition below. + } while (x < 11); - // Call returns (bool, bytes). - // Data location must be specified. - (bool success, bytes memory data) = address(other).call("f"); - if (!success) - revert(); - return data; - } + // Call returns (bool, bytes). + // Data location must be specified. + (bool success, bytes memory data) = address(other).call("f"); + if (!success) + revert(); + return data; + } - using address_make_payable for address; - // Data location for 'arr' must be specified - function g(uint[] memory arr, bytes8 x, OtherContract otherContract, address unknownContract) public payable { - // 'otherContract.transfer' is not provided. - // Since the code of 'OtherContract' is known and has the fallback - // function, address(otherContract) has type 'address payable'. - address(otherContract).transfer(1 ether); + using address_make_payable for address; + // Data location for 'arr' must be specified + function g(uint[] memory /* arr */, bytes8 x, OtherContract otherContract, address unknownContract) public payable { + // 'otherContract.transfer' is not provided. + // Since the code of 'OtherContract' is known and has the fallback + // function, address(otherContract) has type 'address payable'. + address(otherContract).transfer(1 ether); - // 'unknownContract.transfer' is not provided. - // 'address(unknownContract).transfer' is not provided - // since 'address(unknownContract)' is not 'address payable'. - // If the function takes an 'address' which you want to send - // funds to, you can convert it to 'address payable' via 'uint160'. - // Note: This is not recommended and the explicit type - // 'address payable' should be used whenever possible. - // To increase clarity, we suggest the use of a library for - // the conversion (provided after the contract in this example). - address payable addr = unknownContract.make_payable(); - require(addr.send(1 ether)); + // 'unknownContract.transfer' is not provided. + // 'address(unknownContract).transfer' is not provided + // since 'address(unknownContract)' is not 'address payable'. + // If the function takes an 'address' which you want to send + // funds to, you can convert it to 'address payable' via 'uint160'. + // Note: This is not recommended and the explicit type + // 'address payable' should be used whenever possible. + // To increase clarity, we suggest the use of a library for + // the conversion (provided after the contract in this example). + address payable addr = unknownContract.make_payable(); + require(addr.send(1 ether)); - // Since uint32 (4 bytes) is smaller than bytes8 (8 bytes), - // the conversion is not allowed. - // We need to convert to a common size first: - bytes4 x4 = bytes4(x); // Padding happens on the right - uint32 y = uint32(x4); // Conversion is consistent - // 'msg.value' cannot be used in a 'non-payable' function. - // We need to make the function payable - myNumber += y + msg.value; - } - } + // Since uint32 (4 bytes) is smaller than bytes8 (8 bytes), + // the conversion is not allowed. + // We need to convert to a common size first: + bytes4 x4 = bytes4(x); // Padding happens on the right + uint32 y = uint32(x4); // Conversion is consistent + // 'msg.value' cannot be used in a 'non-payable' function. + // We need to make the function payable + myNumber += y + msg.value; + } + } - // We can define a library for explicitly converting ``address`` - // to ``address payable`` as a workaround. - library address_make_payable { - function make_payable(address x) internal pure returns (address payable) { - return address(uint160(x)); - } - } + // We can define a library for explicitly converting ``address`` + // to ``address payable`` as a workaround. + library address_make_payable { + function make_payable(address x) internal pure returns (address payable) { + return address(uint160(x)); + } + } diff --git a/docs/contracts/visibility-and-getters.rst b/docs/contracts/visibility-and-getters.rst index 7bba31e7c..56d5b0e25 100644 --- a/docs/contracts/visibility-and-getters.rst +++ b/docs/contracts/visibility-and-getters.rst @@ -150,24 +150,24 @@ to write a function, for example: :: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.0 <0.7.0; - contract arrayExample { - // public state variable - uint[] public myArray; + contract arrayExample { + // public state variable + uint[] public myArray; - // Getter function generated by the compiler - /* - function myArray(uint i) returns (uint) { - return myArray[i]; + // Getter function generated by the compiler + /* + function myArray(uint i) public view returns (uint) { + return myArray[i]; + } + */ + + // function that returns entire array + function getArray() public view returns (uint[] memory) { + return myArray; + } } - */ - - // function that returns entire array - function getArray() returns (uint[] memory) { - return myArray; - } - } Now you can use ``getArray()`` to retrieve the entire array, instead of ``myArray(i)``, which returns a single element per call. diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index 3b680113e..7a811fa4b 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -37,7 +37,7 @@ breaking changes. These releases always have versions of the form The version pragma is used as follows:: - pragma solidity ^0.5.2; + pragma solidity ^0.5.2; A source file with the line above does not compile with a compiler earlier than version 0.5.2, and it also does not work on a compiler starting from version 0.6.0 (this diff --git a/docs/natspec-format.rst b/docs/natspec-format.rst index 5c9d98736..cfc94d81b 100644 --- a/docs/natspec-format.rst +++ b/docs/natspec-format.rst @@ -49,22 +49,22 @@ The following example shows a contract and a function using all available tags. .. code:: solidity - pragma solidity ^0.5.6; + pragma solidity ^0.5.6; - /// @title A simulator for trees - /// @author Larry A. Gardner - /// @notice You can use this contract for only the most basic simulation - /// @dev All function calls are currently implemented without side effects - contract Tree { - /// @author Mary A. Botanist - /// @notice Calculate tree age in years, rounded up, for live trees - /// @dev The Alexandr N. Tetearing algorithm could increase precision - /// @param rings The number of rings from dendrochronological sample - /// @return age in years, rounded up for partial years - function age(uint256 rings) external pure returns (uint256) { - return rings + 1; - } - } + /// @title A simulator for trees + /// @author Larry A. Gardner + /// @notice You can use this contract for only the most basic simulation + /// @dev All function calls are currently implemented without side effects + contract Tree { + /// @author Mary A. Botanist + /// @notice Calculate tree age in years, rounded up, for live trees + /// @dev The Alexandr N. Tetearing algorithm could increase precision + /// @param rings The number of rings from dendrochronological sample + /// @return age in years, rounded up for partial years + function age(uint256 rings) external pure returns (uint256) { + return rings + 1; + } + } .. _header-tags: diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index 3f18bf3b5..0b37d2fb5 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -499,26 +499,26 @@ not mean loss of proving power. :: - pragma solidity >=0.5.0; - pragma experimental SMTChecker; + pragma solidity >=0.5.0; + pragma experimental SMTChecker; - contract Recover - { - function f( - bytes32 hash, - uint8 _v1, uint8 _v2, - bytes32 _r1, bytes32 _r2, - bytes32 _s1, bytes32 _s2 - ) public pure returns (address) { - address a1 = ecrecover(hash, _v1, _r1, _s1); - require(_v1 == _v2); - require(_r1 == _r2); - require(_s1 == _s2); - address a2 = ecrecover(hash, _v2, _r2, _s2); - assert(a1 == a2); - return a1; - } - } + contract Recover + { + function f( + bytes32 hash, + uint8 _v1, uint8 _v2, + bytes32 _r1, bytes32 _r2, + bytes32 _s1, bytes32 _s2 + ) public pure returns (address) { + address a1 = ecrecover(hash, _v1, _r1, _s1); + require(_v1 == _v2); + require(_r1 == _r2); + require(_s1 == _s2); + address a2 = ecrecover(hash, _v2, _r2, _s2); + assert(a1 == a2); + return a1; + } + } In the example above, the SMTChecker is not expressive enough to actually compute ``ecrecover``, but by modelling the function calls as uninterpreted @@ -552,34 +552,34 @@ types. :: - pragma solidity >=0.5.0; - pragma experimental SMTChecker; - // This will not compile - contract Aliasing - { - uint[] array; - function f( - uint[] memory a, - uint[] memory b, - uint[][] memory c, - uint[] storage d - ) internal view { - require(array[0] == 42); - require(a[0] == 2); - require(c[0][0] == 2); - require(d[0] == 2); - b[0] = 1; - // Erasing knowledge about memory references should not - // erase knowledge about state variables. - assert(array[0] == 42); - // Fails because `a == b` is possible. - assert(a[0] == 2); - // Fails because `c[i] == b` is possible. - assert(c[0][0] == 2); - assert(d[0] == 2); - assert(b[0] == 1); - } - } + pragma solidity >=0.5.0; + pragma experimental SMTChecker; + // This will report a warning + contract Aliasing + { + uint[] array; + function f( + uint[] memory a, + uint[] memory b, + uint[][] memory c, + uint[] storage d + ) internal view { + require(array[0] == 42); + require(a[0] == 2); + require(c[0][0] == 2); + require(d[0] == 2); + b[0] = 1; + // Erasing knowledge about memory references should not + // erase knowledge about state variables. + assert(array[0] == 42); + // Fails because `a == b` is possible. + assert(a[0] == 2); + // Fails because `c[i] == b` is possible. + assert(c[0][0] == 2); + assert(d[0] == 2); + assert(b[0] == 1); + } + } After the assignment to ``b[0]``, we need to clear knowledge about ``a`` since it has the same type (``uint[]``) and data location (memory). We also need to diff --git a/scripts/isolate_tests.py b/scripts/isolate_tests.py index 06feb0fc6..9492ee137 100755 --- a/scripts/isolate_tests.py +++ b/scripts/isolate_tests.py @@ -39,6 +39,7 @@ def extract_test_cases(path): # and abort a line not indented properly. def extract_docs_cases(path): inside = False + extractedLines = [] tests = [] # Collect all snippets of indented blocks @@ -46,15 +47,23 @@ def extract_docs_cases(path): if l != '': if not inside and l.startswith(' '): # start new test - tests += [''] + extractedLines += [''] inside = l.startswith(' ') if inside: - tests[-1] += l + '\n' - # Filter all tests that do not contain Solidity - return [ - test for test in tests - if re.search(r'^ [ ]*(pragma solidity|contract |library |interface )', test, re.MULTILINE) - ] + extractedLines[-1] += l + '\n' + + codeStart = "(pragma solidity|contract.*{|library.*{|interface.*{)" + + # Filter all tests that do not contain Solidity or are intended incorrectly. + for lines in extractedLines: + if re.search(r'^\s{0,3}' + codeStart, lines, re.MULTILINE): + print("Intendation error in " + path + ":") + print(lines) + exit(1) + if re.search(r'^\s{4}' + codeStart, lines, re.MULTILINE): + tests.append(lines) + + return tests def write_cases(f, tests): cleaned_filename = f.replace(".","_").replace("-","_").replace(" ","_").lower()