Skip to content

Commit

Permalink
Fix: natspec annotations on constructors
Browse files Browse the repository at this point in the history
- natspec annotations on constructore where ignored.
  • Loading branch information
aarlt committed Jul 21, 2018
1 parent 1b56b42 commit cba2ce3
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 20 deletions.
38 changes: 18 additions & 20 deletions libsolidity/interface/Natspec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
Json::Value doc;
Json::Value methods(Json::objectValue);

auto constructorDefinition(_contractDef.constructor());
if (constructorDefinition) {
string value = extractDoc(constructorDefinition->annotation().docTags, "notice");
if (!value.empty())
// add the constructor, only if we have any documentation to add
methods["constructor"] = Json::Value(value);
}

string notice = extractDoc(_contractDef.annotation().docTags, "notice");
if (!notice.empty())
doc["notice"] = Json::Value(notice);
Expand Down Expand Up @@ -73,33 +81,23 @@ Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef)
if (!dev.empty())
doc["details"] = Json::Value(dev);

auto constructorDefinition(_contractDef.constructor());
if (constructorDefinition) {
Json::Value method;
extractDevAnnotations(method, constructorDefinition);
if (!method.empty())
// add the constructor, only if we have any documentation to add
methods["constructor"] = method;
}

for (auto const& it: _contractDef.interfaceFunctions())
{
if (!it.second->hasDeclaration())
continue;
Json::Value method;
if (auto fun = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
{
auto dev = extractDoc(fun->annotation().docTags, "dev");
if (!dev.empty())
method["details"] = Json::Value(dev);

auto author = extractDoc(fun->annotation().docTags, "author");
if (!author.empty())
method["author"] = author;

auto ret = extractDoc(fun->annotation().docTags, "return");
if (!ret.empty())
method["return"] = ret;

Json::Value params(Json::objectValue);
auto paramRange = fun->annotation().docTags.equal_range("param");
for (auto i = paramRange.first; i != paramRange.second; ++i)
params[i->second.paramName] = Json::Value(i->second.content);

if (!params.empty())
method["params"] = params;

extractDevAnnotations(method, fun);
if (!method.empty())
// add the function, only if we have any documentation to add
methods[it.second->externalSignature()] = method;
Expand Down
32 changes: 32 additions & 0 deletions libsolidity/interface/Natspec.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <string>
#include <memory>
#include <json/json.h>
#include <boost/algorithm/string.hpp>

namespace dev
{
Expand All @@ -54,6 +55,37 @@ class Natspec
private:
/// @returns concatenation of all content under the given tag name.
static std::string extractDoc(std::multimap<std::string, DocTag> const& _tags, std::string const& _name);

/// Helper-function that will retrieve docTags from fun->annotation().docTags,
/// so T need to satisfy the concept for "annotation().docTags".
/// @param _json JSON object where parsed annotations will be stored
/// @param _fun T const* that is able satisfy annotation().docTags.
template<typename T>
static void extractDevAnnotations(Json::Value &_json, T const *_fun)
{
if (_fun)
{
auto dev = extractDoc(_fun->annotation().docTags, "dev");
if (!dev.empty())
_json["details"] = Json::Value(dev);

auto author = extractDoc(_fun->annotation().docTags, "author");
if (!author.empty())
_json["author"] = author;

auto ret = extractDoc(_fun->annotation().docTags, "return");
if (!ret.empty())
_json["return"] = ret;

Json::Value params(Json::objectValue);
auto paramRange = _fun->annotation().docTags.equal_range("param");
for (auto i = paramRange.first; i != paramRange.second; ++i)
params[i->second.paramName] = Json::Value(i->second.content);

if (!params.empty())
_json["params"] = params;
}
}
};

} //solidity NS
Expand Down
113 changes: 113 additions & 0 deletions test/libsolidity/SolidityNatspecJSON.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ class DocumentationChecker
m_compilerStack.addSource("", "pragma solidity >=0.0;\n" + _code);
m_compilerStack.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_REQUIRE_MESSAGE(m_compilerStack.parseAndAnalyze(), "Parsing contract failed");
for (auto& error : m_compilerStack.errors()) {
std::cout << error->what() << std::endl;
}

Json::Value generatedDocumentation;
if (_userDocumentation)
Expand Down Expand Up @@ -682,6 +685,116 @@ BOOST_AUTO_TEST_CASE(dev_documenting_no_param_description)
expectNatspecError(sourceCode);
}

BOOST_AUTO_TEST_CASE(user_constructor)
{
char const *sourceCode = R"(
contract test {
/// @notice this is a really nice constructor
constructor(uint a, uint second) public { }
}
)";

char const *natspec = R"ABCDEF({
"methods" : {
"constructor" : "this is a really nice constructor"
}
})ABCDEF";

checkNatspec(sourceCode, natspec, true);
}

BOOST_AUTO_TEST_CASE(user_constructor_and_function)
{
char const *sourceCode = R"(
contract test {
/// @notice this is a really nice constructor
constructor(uint a, uint second) public { }
/// another multiplier
function mul(uint a, uint second) public returns(uint d) { return a * 7 + second; }
}
)";

char const *natspec = R"ABCDEF({
"methods" : {
"mul(uint256,uint256)" : {
"notice" : "another multiplier"
},
"constructor" : "this is a really nice constructor"
}
})ABCDEF";

checkNatspec(sourceCode, natspec, true);
}

BOOST_AUTO_TEST_CASE(dev_constructor)
{
char const *sourceCode = R"(
contract test {
/// @author Alex
/// @param a the parameter a is really nice and very useful
/// @param second the second parameter is not very useful, it just provides additional confusion
constructor(uint a, uint second) public { }
}
)";

char const *natspec = R"ABCDEF({
"methods" : {
"constructor" : {
"author" : "Alex",
"params" : {
"a" : "the parameter a is really nice and very useful",
"second" : "the second parameter is not very useful, it just provides additional confusion"
}
}
}
})ABCDEF";

checkNatspec(sourceCode, natspec, false);
}

BOOST_AUTO_TEST_CASE(dev_constructor_and_function)
{
char const *sourceCode = R"(
contract test {
/// @author Alex
/// @param a the parameter a is really nice and very useful
/// @param second the second parameter is not very useful, it just provides additional confusion
constructor(uint a, uint second) public { }
/// @dev Multiplies a number by 7 and adds second parameter
/// @param a Documentation for the first parameter starts here.
/// Since it's a really complicated parameter we need 2 lines
/// @param second Documentation for the second parameter
/// @return The result of the multiplication
/// and cookies with nutella
function mul(uint a, uint second) public returns(uint d) {
return a * 7 + second;
}
}
)";

char const *natspec = R"ABCDEF({
"methods" : {
"mul(uint256,uint256)" : {
"details" : "Multiplies a number by 7 and adds second parameter",
"params" : {
"a" : "Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines",
"second" : "Documentation for the second parameter"
},
"return" : "The result of the multiplication and cookies with nutella"
},
"constructor" : {
"author" : "Alex",
"params" : {
"a" : "the parameter a is really nice and very useful",
"second" : "the second parameter is not very useful, it just provides additional confusion"
}
}
}
})ABCDEF";

checkNatspec(sourceCode, natspec, false);
}

BOOST_AUTO_TEST_SUITE_END()

}
Expand Down

0 comments on commit cba2ce3

Please sign in to comment.