Merge pull request #1451 from LefterisJP/fix_build

Accept any kind of whitespace after natspec tags
This commit is contained in:
chriseth 2017-01-27 14:09:39 +01:00 committed by GitHub
commit 636e480156
3 changed files with 124 additions and 20 deletions

View File

@ -14,6 +14,7 @@ Bugfixes:
* Code generator: Allow recursive structs. * Code generator: Allow recursive structs.
* Inline assembly: Disallow variables named like opcodes. * Inline assembly: Disallow variables named like opcodes.
* Type checker: Allow multiple events of the same name (but with different arities or argument types) * Type checker: Allow multiple events of the same name (but with different arities or argument types)
* Natspec parser: Fix error with ``@param`` parsing and whitespace.
### 0.4.8 (2017-01-13) ### 0.4.8 (2017-01-13)

View File

@ -1,14 +1,19 @@
#include <libsolidity/parsing/DocStringParser.h> #include <libsolidity/parsing/DocStringParser.h>
#include <boost/range/irange.hpp>
#include <libsolidity/interface/Utils.h> #include <libsolidity/interface/Utils.h>
#include <boost/range/irange.hpp>
#include <boost/range/algorithm.hpp>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::solidity; using namespace dev::solidity;
static inline string::const_iterator skipLineOrEOS( namespace
{
string::const_iterator skipLineOrEOS(
string::const_iterator _nlPos, string::const_iterator _nlPos,
string::const_iterator _end string::const_iterator _end
) )
@ -16,14 +21,34 @@ static inline string::const_iterator skipLineOrEOS(
return (_nlPos == _end) ? _end : ++_nlPos; return (_nlPos == _end) ? _end : ++_nlPos;
} }
static inline string::const_iterator firstSpaceOrNl( string::const_iterator firstSpaceOrTab(
string::const_iterator _pos, string::const_iterator _pos,
string::const_iterator _end string::const_iterator _end
) )
{ {
auto spacePos = find(_pos, _end, ' '); return boost::range::find_first_of(make_pair(_pos, _end), " \t");
auto nlPos = find(_pos, _end, '\n'); }
return (spacePos < nlPos) ? spacePos : nlPos;
string::const_iterator firstWhitespaceOrNewline(
string::const_iterator _pos,
string::const_iterator _end
)
{
return boost::range::find_first_of(make_pair(_pos, _end), " \t\n");
}
string::const_iterator skipWhitespace(
string::const_iterator _pos,
string::const_iterator _end
)
{
auto currPos = _pos;
while (currPos != _end && (*currPos == ' ' || *currPos == '\t'))
currPos += 1;
return currPos;
}
} }
bool DocStringParser::parse(string const& _docString, ErrorList& _errors) bool DocStringParser::parse(string const& _docString, ErrorList& _errors)
@ -43,7 +68,7 @@ bool DocStringParser::parse(string const& _docString, ErrorList& _errors)
if (tagPos != end && tagPos < nlPos) if (tagPos != end && tagPos < nlPos)
{ {
// we found a tag // we found a tag
auto tagNameEndPos = firstSpaceOrNl(tagPos, end); auto tagNameEndPos = firstWhitespaceOrNewline(tagPos, end);
if (tagNameEndPos == end) if (tagNameEndPos == end)
{ {
appendError("End of tag " + string(tagPos, tagNameEndPos) + "not found"); appendError("End of tag " + string(tagPos, tagNameEndPos) + "not found");
@ -75,27 +100,40 @@ DocStringParser::iter DocStringParser::parseDocTagLine(iter _pos, iter _end, boo
{ {
solAssert(!!m_lastTag, ""); solAssert(!!m_lastTag, "");
auto nlPos = find(_pos, _end, '\n'); auto nlPos = find(_pos, _end, '\n');
if (_appending && _pos < _end && *_pos != ' ') if (_appending && _pos < _end && *_pos != ' ' && *_pos != '\t')
m_lastTag->content += " "; m_lastTag->content += " ";
else if (!_appending)
_pos = skipWhitespace(_pos, _end);
copy(_pos, nlPos, back_inserter(m_lastTag->content)); copy(_pos, nlPos, back_inserter(m_lastTag->content));
return skipLineOrEOS(nlPos, _end); return skipLineOrEOS(nlPos, _end);
} }
DocStringParser::iter DocStringParser::parseDocTagParam(iter _pos, iter _end) DocStringParser::iter DocStringParser::parseDocTagParam(iter _pos, iter _end)
{ {
// find param name // find param name start
auto currPos = find(_pos, _end, ' '); auto nameStartPos = skipWhitespace(_pos, _end);
if (currPos == _end) if (nameStartPos == _end)
{ {
appendError("End of param name not found" + string(_pos, _end)); appendError("No param name given");
return _end;
}
auto nameEndPos = firstSpaceOrTab(nameStartPos, _end);
if (nameEndPos == _end)
{
appendError("End of param name not found: " + string(nameStartPos, _end));
return _end;
}
auto paramName = string(nameStartPos, nameEndPos);
auto descStartPos = skipWhitespace(nameEndPos, _end);
if (descStartPos == _end)
{
appendError("No description given for param " + paramName);
return _end; return _end;
} }
auto paramName = string(_pos, currPos); auto nlPos = find(descStartPos, _end, '\n');
auto paramDesc = string(descStartPos, nlPos);
currPos += 1;
auto nlPos = find(currPos, _end, '\n');
auto paramDesc = string(currPos, nlPos);
newTag("param"); newTag("param");
m_lastTag->paramName = paramName; m_lastTag->paramName = paramName;
m_lastTag->content = paramDesc; m_lastTag->content = paramDesc;

View File

@ -56,7 +56,7 @@ public:
m_reader.parse(_expectedDocumentationString, expectedDocumentation); m_reader.parse(_expectedDocumentationString, expectedDocumentation);
BOOST_CHECK_MESSAGE( BOOST_CHECK_MESSAGE(
expectedDocumentation == generatedDocumentation, expectedDocumentation == generatedDocumentation,
"Expected " << expectedDocumentation.toStyledString() << "Expected:\n" << expectedDocumentation.toStyledString() <<
"\n but got:\n" << generatedDocumentation.toStyledString() "\n but got:\n" << generatedDocumentation.toStyledString()
); );
} }
@ -215,7 +215,7 @@ BOOST_AUTO_TEST_CASE(dev_desc_after_nl)
char const* natspec = "{" char const* natspec = "{"
"\"methods\":{" "\"methods\":{"
" \"mul(uint256,uint256)\":{ \n" " \"mul(uint256,uint256)\":{ \n"
" \"details\": \" Multiplies a number by 7 and adds second parameter\",\n" " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\n" " \"params\": {\n"
" \"a\": \"Documentation for the first parameter\",\n" " \"a\": \"Documentation for the first parameter\",\n"
" \"second\": \"Documentation for the second parameter\"\n" " \"second\": \"Documentation for the second parameter\"\n"
@ -251,6 +251,29 @@ BOOST_AUTO_TEST_CASE(dev_multiple_params)
checkNatspec(sourceCode, natspec, false); checkNatspec(sourceCode, natspec, false);
} }
BOOST_AUTO_TEST_CASE(dev_multiple_params_mixed_whitespace)
{
char const* sourceCode = "contract test {\n"
" /// @dev Multiplies a number by 7 and adds second parameter\n"
" /// @param a Documentation for the first parameter\n"
" /// @param second Documentation for the second parameter\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
char const* natspec = "{"
"\"methods\":{"
" \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\n"
" \"a\": \"Documentation for the first parameter\",\n"
" \"second\": \"Documentation for the second parameter\"\n"
" }\n"
" }\n"
"}}";
checkNatspec(sourceCode, natspec, false);
}
BOOST_AUTO_TEST_CASE(dev_mutiline_param_description) BOOST_AUTO_TEST_CASE(dev_mutiline_param_description)
{ {
char const* sourceCode = R"( char const* sourceCode = R"(
@ -379,7 +402,7 @@ BOOST_AUTO_TEST_CASE(dev_return_desc_after_nl)
" \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n" " \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n"
" \"second\": \"Documentation for the second parameter\"\n" " \"second\": \"Documentation for the second parameter\"\n"
" },\n" " },\n"
" \"return\": \" The result of the multiplication\"\n" " \"return\": \"The result of the multiplication\"\n"
" }\n" " }\n"
"}}"; "}}";
@ -612,6 +635,48 @@ BOOST_AUTO_TEST_CASE(dev_documenting_nonexistent_param)
expectNatspecError(sourceCode); expectNatspecError(sourceCode);
} }
BOOST_AUTO_TEST_CASE(dev_documenting_no_paramname)
{
char const* sourceCode = R"(
contract test {
/// @dev Multiplies a number by 7 and adds second parameter
/// @param a Documentation for the first parameter
/// @param
function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }
}
)";
expectNatspecError(sourceCode);
}
BOOST_AUTO_TEST_CASE(dev_documenting_no_paramname_end)
{
char const* sourceCode = R"(
contract test {
/// @dev Multiplies a number by 7 and adds second parameter
/// @param a Documentation for the first parameter
/// @param se
function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }
}
)";
expectNatspecError(sourceCode);
}
BOOST_AUTO_TEST_CASE(dev_documenting_no_param_description)
{
char const* sourceCode = R"(
contract test {
/// @dev Multiplies a number by 7 and adds second parameter
/// @param a Documentation for the first parameter
/// @param second
function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }
}
)";
expectNatspecError(sourceCode);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }