mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Allow custom natspec tags.
This commit is contained in:
parent
2a25d04896
commit
5690020d88
@ -8,6 +8,7 @@ Compiler Features:
|
||||
* Inline Assembly: Do not warn anymore about variables or functions being shadowed by EVM opcodes.
|
||||
* NatSpec: Provide source locations for parsing errors.
|
||||
* Optimizer: Simple inlining when jumping to small blocks that jump again after a few side-effect free opcodes.
|
||||
* NatSpec: Allow and export all tags that start with ``@custom:``.
|
||||
|
||||
|
||||
Bugfixes:
|
||||
|
@ -27,6 +27,8 @@
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::langutil;
|
||||
@ -54,8 +56,8 @@ void copyMissingTags(set<CallableDeclaration const*> const& _baseFunctions, Stru
|
||||
for (auto it = sourceDoc.docTags.begin(); it != sourceDoc.docTags.end();)
|
||||
{
|
||||
string const& tag = it->first;
|
||||
// Don't copy tag "inheritdoc" or already existing tags
|
||||
if (tag == "inheritdoc" || _target.docTags.count(tag))
|
||||
// Don't copy tag "inheritdoc", custom tags or already existing tags
|
||||
if (tag == "inheritdoc" || _target.docTags.count(tag) || boost::starts_with(tag, "custom"))
|
||||
{
|
||||
it++;
|
||||
continue;
|
||||
|
@ -28,6 +28,8 @@
|
||||
#include <libsolidity/analysis/NameAndTypeResolver.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::langutil;
|
||||
@ -153,15 +155,17 @@ void DocStringTagParser::parseDocStrings(
|
||||
_annotation.docTags = DocStringParser{*_node.documentation(), m_errorReporter}.parse();
|
||||
|
||||
size_t returnTagsVisited = 0;
|
||||
for (auto const& docTag: _annotation.docTags)
|
||||
for (auto const& [tagName, tagValue]: _annotation.docTags)
|
||||
{
|
||||
if (!_validTags.count(docTag.first))
|
||||
if (boost::starts_with(tagName, "custom:") && tagName.size() > string("custom:").size())
|
||||
continue;
|
||||
else if (!_validTags.count(tagName))
|
||||
m_errorReporter.docstringParsingError(
|
||||
6546_error,
|
||||
_node.documentation()->location(),
|
||||
"Documentation tag @" + docTag.first + " not valid for " + _nodeName + "."
|
||||
"Documentation tag @" + tagName + " not valid for " + _nodeName + "."
|
||||
);
|
||||
else if (docTag.first == "return")
|
||||
else if (tagName == "return")
|
||||
{
|
||||
returnTagsVisited++;
|
||||
if (auto const* varDecl = dynamic_cast<VariableDeclaration const*>(&_node))
|
||||
@ -171,19 +175,19 @@ void DocStringTagParser::parseDocStrings(
|
||||
m_errorReporter.docstringParsingError(
|
||||
5256_error,
|
||||
_node.documentation()->location(),
|
||||
"Documentation tag \"@" + docTag.first + "\" is only allowed once on state-variables."
|
||||
"Documentation tag \"@" + tagName + "\" is only allowed once on state-variables."
|
||||
);
|
||||
}
|
||||
else if (auto const* function = dynamic_cast<FunctionDefinition const*>(&_node))
|
||||
{
|
||||
string content = docTag.second.content;
|
||||
string content = tagValue.content;
|
||||
string firstWord = content.substr(0, content.find_first_of(" \t"));
|
||||
|
||||
if (returnTagsVisited > function->returnParameters().size())
|
||||
m_errorReporter.docstringParsingError(
|
||||
2604_error,
|
||||
_node.documentation()->location(),
|
||||
"Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" +
|
||||
"Documentation tag \"@" + tagName + " " + tagValue.content + "\"" +
|
||||
" exceeds the number of return parameters."
|
||||
);
|
||||
else
|
||||
@ -193,7 +197,7 @@ void DocStringTagParser::parseDocStrings(
|
||||
m_errorReporter.docstringParsingError(
|
||||
5856_error,
|
||||
_node.documentation()->location(),
|
||||
"Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" +
|
||||
"Documentation tag \"@" + tagName + " " + tagValue.content + "\"" +
|
||||
" does not contain the name of its return parameter."
|
||||
);
|
||||
}
|
||||
|
@ -27,7 +27,9 @@
|
||||
#include <libsolidity/interface/Natspec.h>
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <boost/range/irange.hpp>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
@ -97,7 +99,7 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
|
||||
|
||||
Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef)
|
||||
{
|
||||
Json::Value doc;
|
||||
Json::Value doc = extractCustomDoc(_contractDef.annotation().docTags);
|
||||
Json::Value methods(Json::objectValue);
|
||||
|
||||
doc["version"] = Json::Value(c_natspecVersion);
|
||||
@ -205,9 +207,21 @@ string Natspec::extractDoc(multimap<string, DocTag> const& _tags, string const&
|
||||
return value;
|
||||
}
|
||||
|
||||
Json::Value Natspec::extractCustomDoc(multimap<string, DocTag> const& _tags)
|
||||
{
|
||||
std::map<string, string> concatenated;
|
||||
for (auto const& [tag, value]: _tags)
|
||||
if (boost::starts_with(tag, "custom"))
|
||||
concatenated[tag] += value.content;
|
||||
Json::Value result;
|
||||
for (auto& [tag, value]: concatenated)
|
||||
result[tag] = move(value);
|
||||
return result;
|
||||
}
|
||||
|
||||
Json::Value Natspec::devDocumentation(std::multimap<std::string, DocTag> const& _tags)
|
||||
{
|
||||
Json::Value json(Json::objectValue);
|
||||
Json::Value json = extractCustomDoc(_tags);
|
||||
auto dev = extractDoc(_tags, "dev");
|
||||
if (!dev.empty())
|
||||
json["details"] = Json::Value(dev);
|
||||
|
@ -57,6 +57,9 @@ 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);
|
||||
|
||||
/// Extract all custom tags from @a _tags.
|
||||
static Json::Value extractCustomDoc(std::multimap<std::string, DocTag> const& _tags);
|
||||
|
||||
/// Helper-function that will create a json object with dev specific annotations, if present.
|
||||
/// @param _tags docTags that are used.
|
||||
/// @return A JSON representation
|
||||
|
@ -2247,6 +2247,68 @@ BOOST_AUTO_TEST_CASE(dev_return_name_no_description)
|
||||
checkNatspec(sourceCode, "B", natspec2, false);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(custom)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
/// @custom:x one two three
|
||||
/// @custom:y line
|
||||
/// break
|
||||
/// @custom:t one
|
||||
/// @custom:t two
|
||||
contract A {
|
||||
/// @custom:note statevar
|
||||
uint x;
|
||||
/// @custom:since 2014
|
||||
function g(int x) public pure virtual returns (int, int) { return (1, 2); }
|
||||
}
|
||||
)";
|
||||
|
||||
char const* natspec = R"ABCDEF({
|
||||
"custom:t": "onetwo",
|
||||
"custom:x": "one two three",
|
||||
"custom:y": "line break",
|
||||
"methods":
|
||||
{
|
||||
"g(int256)":
|
||||
{
|
||||
"custom:since": "2014"
|
||||
}
|
||||
},
|
||||
"stateVariables": { "x": { "custom:note": "statevar" } }
|
||||
})ABCDEF";
|
||||
|
||||
checkNatspec(sourceCode, "A", natspec, false);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(custom_inheritance)
|
||||
{
|
||||
char const *sourceCode = R"(
|
||||
contract A {
|
||||
/// @custom:since 2014
|
||||
function g(uint x) public pure virtual {}
|
||||
}
|
||||
contract B is A {
|
||||
function g(uint x) public pure override {}
|
||||
}
|
||||
)";
|
||||
|
||||
char const* natspecA = R"ABCDEF({
|
||||
"methods":
|
||||
{
|
||||
"g(uint256)":
|
||||
{
|
||||
"custom:since": "2014"
|
||||
}
|
||||
}
|
||||
)ABCDEF";
|
||||
char const* natspecB = R"ABCDEF({
|
||||
"methods": {}
|
||||
})ABCDEF";
|
||||
|
||||
checkNatspec(sourceCode, "A", natspecA, false);
|
||||
checkNatspec(sourceCode, "B", natspecB, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
Loading…
Reference in New Issue
Block a user