diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp index 108a18564..3bcc87660 100644 --- a/libsolidity/analysis/DocStringAnalyser.cpp +++ b/libsolidity/analysis/DocStringAnalyser.cpp @@ -129,9 +129,36 @@ void DocStringAnalyser::parseDocStrings( m_errorOccured = true; _annotation.docTags = parser.tags(); } + + size_t returnTagsVisited = 0; for (auto const& docTag: _annotation.docTags) + { if (!_validTags.count(docTag.first)) - appendError("Doc tag @" + docTag.first + " not valid for " + _nodeName + "."); + appendError("Documentation tag @" + docTag.first + " not valid for " + _nodeName + "."); + else + if (docTag.first == "return") + { + returnTagsVisited++; + if (auto* function = dynamic_cast(&_node)) + { + string content = docTag.second.content; + string firstWord = content.substr(0, content.find_first_of(" \t")); + + if (returnTagsVisited > function->returnParameters().size()) + appendError("Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" + + " exceedes the number of return parameters." + ); + else + { + auto parameter = function->returnParameters().at(returnTagsVisited - 1); + if (!parameter->name().empty() && parameter->name() != firstWord) + appendError("Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" + + " does not contain the name of its return parameter." + ); + } + } + } + } } void DocStringAnalyser::appendError(string const& _description) diff --git a/test/compilationTests/gnosis/Markets/Campaign.sol b/test/compilationTests/gnosis/Markets/Campaign.sol index a678e7a43..451eef9ed 100644 --- a/test/compilationTests/gnosis/Markets/Campaign.sol +++ b/test/compilationTests/gnosis/Markets/Campaign.sol @@ -146,7 +146,6 @@ contract Campaign { } /// @dev Allows to withdraw fees from market contract to campaign contract - /// @return Fee amount function closeMarket() public atStage(Stages.MarketCreated) diff --git a/test/compilationTests/milestonetracker/RLP.sol b/test/compilationTests/milestonetracker/RLP.sol index 799233a1a..b58bbdf90 100644 --- a/test/compilationTests/milestonetracker/RLP.sol +++ b/test/compilationTests/milestonetracker/RLP.sol @@ -90,14 +90,14 @@ library RLP { /// @dev Check if the RLP item is null. /// @param self The RLP item. - /// @return 'true' if the item is null. + /// @return ret 'true' if the item is null. function isNull(RLPItem memory self) internal view returns (bool ret) { return self._unsafe_length == 0; } /// @dev Check if the RLP item is a list. /// @param self The RLP item. - /// @return 'true' if the item is a list. + /// @return ret 'true' if the item is a list. function isList(RLPItem memory self) internal view returns (bool ret) { if (self._unsafe_length == 0) return false; @@ -109,7 +109,7 @@ library RLP { /// @dev Check if the RLP item is data. /// @param self The RLP item. - /// @return 'true' if the item is data. + /// @return ret 'true' if the item is data. function isData(RLPItem memory self) internal view returns (bool ret) { if (self._unsafe_length == 0) return false; @@ -121,7 +121,7 @@ library RLP { /// @dev Check if the RLP item is empty (string or list). /// @param self The RLP item. - /// @return 'true' if the item is null. + /// @return ret 'true' if the item is null. function isEmpty(RLPItem memory self) internal view returns (bool ret) { if(isNull(self)) return false; @@ -156,7 +156,7 @@ library RLP { /// @dev Create an iterator. /// @param self The RLP item. - /// @return An 'Iterator' over the item. + /// @return it An 'Iterator' over the item. function iterator(RLPItem memory self) internal view returns (Iterator memory it) { if (!isList(self)) revert(); @@ -167,7 +167,7 @@ library RLP { /// @dev Return the RLP encoded bytes. /// @param self The RLPItem. - /// @return The bytes. + /// @return bts The bytes. function toBytes(RLPItem memory self) internal returns (bytes memory bts) { uint len = self._unsafe_length; if (len != 0) @@ -180,7 +180,7 @@ library RLP { /// @dev Decode an RLPItem into bytes. This will not work if the /// RLPItem is a list. /// @param self The RLPItem. - /// @return The decoded string. + /// @return bts The decoded string. function toData(RLPItem memory self) internal returns (bytes memory bts) { if(!isData(self)) revert(); @@ -192,7 +192,7 @@ library RLP { /// @dev Get the list of sub-items from an RLP encoded list. /// Warning: This is inefficient, as it requires that the list is read twice. /// @param self The RLP item. - /// @return Array of RLPItems. + /// @return list Array of RLPItems. function toList(RLPItem memory self) internal view returns (RLPItem[] memory list) { if(!isList(self)) revert(); @@ -209,7 +209,7 @@ library RLP { /// @dev Decode an RLPItem into an ascii string. This will not work if the /// RLPItem is a list. /// @param self The RLPItem. - /// @return The decoded string. + /// @return str The decoded string. function toAscii(RLPItem memory self) internal returns (string memory str) { if(!isData(self)) revert(); @@ -222,7 +222,7 @@ library RLP { /// @dev Decode an RLPItem into a uint. This will not work if the /// RLPItem is a list. /// @param self The RLPItem. - /// @return The decoded string. + /// @return data The decoded string. function toUint(RLPItem memory self) internal view returns (uint data) { if(!isData(self)) revert(); @@ -237,7 +237,7 @@ library RLP { /// @dev Decode an RLPItem into a boolean. This will not work if the /// RLPItem is a list. /// @param self The RLPItem. - /// @return The decoded string. + /// @return data The decoded string. function toBool(RLPItem memory self) internal view returns (bool data) { if(!isData(self)) revert(); @@ -256,7 +256,7 @@ library RLP { /// @dev Decode an RLPItem into a byte. This will not work if the /// RLPItem is a list. /// @param self The RLPItem. - /// @return The decoded string. + /// @return data The decoded string. function toByte(RLPItem memory self) internal view returns (byte data) { if(!isData(self)) revert(); @@ -273,7 +273,7 @@ library RLP { /// @dev Decode an RLPItem into an int. This will not work if the /// RLPItem is a list. /// @param self The RLPItem. - /// @return The decoded string. + /// @return data The decoded string. function toInt(RLPItem memory self) internal view returns (int data) { return int(toUint(self)); } @@ -281,7 +281,7 @@ library RLP { /// @dev Decode an RLPItem into a bytes32. This will not work if the /// RLPItem is a list. /// @param self The RLPItem. - /// @return The decoded string. + /// @return data The decoded string. function toBytes32(RLPItem memory self) internal view returns (bytes32 data) { return bytes32(toUint(self)); } @@ -289,7 +289,7 @@ library RLP { /// @dev Decode an RLPItem into an address. This will not work if the /// RLPItem is a list. /// @param self The RLPItem. - /// @return The decoded string. + /// @return data The decoded string. function toAddress(RLPItem memory self) internal view returns (address data) { if(!isData(self)) revert(); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 5b4919198..d6f959957 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -12831,7 +12831,7 @@ BOOST_AUTO_TEST_CASE(snark) return G1Point(p.X, q - (p.Y % q)); } - /// @return the sum of two points of G1 + /// @return r the sum of two points of G1 function add(G1Point memory p1, G1Point memory p2) internal returns (G1Point memory r) { uint[4] memory input; input[0] = p1.X; @@ -12847,7 +12847,7 @@ BOOST_AUTO_TEST_CASE(snark) require(success); } - /// @return the product of a point on G1 and a scalar, i.e. + /// @return r the product of a point on G1 and a scalar, i.e. /// p == p.mul(1) and p.add(p) == p.mul(2) for all points p. function mul(G1Point memory p, uint s) internal returns (G1Point memory r) { uint[3] memory input; diff --git a/test/libsolidity/syntaxTests/natspec/dosctring_named_return_parameter.sol b/test/libsolidity/syntaxTests/natspec/dosctring_named_return_parameter.sol new file mode 100644 index 000000000..8fdcc3156 --- /dev/null +++ b/test/libsolidity/syntaxTests/natspec/dosctring_named_return_parameter.sol @@ -0,0 +1,5 @@ +abstract contract C { + /// @return value The value returned by this function. + function vote() public virtual returns (uint value); +} +// ---- \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/natspec/invalid/dosctring_named_return_param_mismatch.sol b/test/libsolidity/syntaxTests/natspec/invalid/dosctring_named_return_param_mismatch.sol new file mode 100644 index 000000000..23f4828e4 --- /dev/null +++ b/test/libsolidity/syntaxTests/natspec/invalid/dosctring_named_return_param_mismatch.sol @@ -0,0 +1,7 @@ +abstract contract C { + /// @param id Some identifier + /// @return No value returned + function vote(uint id) public virtual returns (uint value); +} +// ---- +// DocstringParsingError: Documentation tag "@return No value returned" does not contain the name of its return parameter. diff --git a/test/libsolidity/syntaxTests/natspec/invalid/dosctring_return_size_mismatch.sol b/test/libsolidity/syntaxTests/natspec/invalid/dosctring_return_size_mismatch.sol new file mode 100644 index 000000000..23f4828e4 --- /dev/null +++ b/test/libsolidity/syntaxTests/natspec/invalid/dosctring_return_size_mismatch.sol @@ -0,0 +1,7 @@ +abstract contract C { + /// @param id Some identifier + /// @return No value returned + function vote(uint id) public virtual returns (uint value); +} +// ---- +// DocstringParsingError: Documentation tag "@return No value returned" does not contain the name of its return parameter.