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.
|
* Inline Assembly: Do not warn anymore about variables or functions being shadowed by EVM opcodes.
|
||||||
* NatSpec: Provide source locations for parsing errors.
|
* 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.
|
* 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:
|
Bugfixes:
|
||||||
|
@ -27,6 +27,8 @@
|
|||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
using namespace solidity::langutil;
|
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();)
|
for (auto it = sourceDoc.docTags.begin(); it != sourceDoc.docTags.end();)
|
||||||
{
|
{
|
||||||
string const& tag = it->first;
|
string const& tag = it->first;
|
||||||
// Don't copy tag "inheritdoc" or already existing tags
|
// Don't copy tag "inheritdoc", custom tags or already existing tags
|
||||||
if (tag == "inheritdoc" || _target.docTags.count(tag))
|
if (tag == "inheritdoc" || _target.docTags.count(tag) || boost::starts_with(tag, "custom"))
|
||||||
{
|
{
|
||||||
it++;
|
it++;
|
||||||
continue;
|
continue;
|
||||||
|
@ -28,6 +28,8 @@
|
|||||||
#include <libsolidity/analysis/NameAndTypeResolver.h>
|
#include <libsolidity/analysis/NameAndTypeResolver.h>
|
||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
using namespace solidity::langutil;
|
using namespace solidity::langutil;
|
||||||
@ -153,15 +155,17 @@ void DocStringTagParser::parseDocStrings(
|
|||||||
_annotation.docTags = DocStringParser{*_node.documentation(), m_errorReporter}.parse();
|
_annotation.docTags = DocStringParser{*_node.documentation(), m_errorReporter}.parse();
|
||||||
|
|
||||||
size_t returnTagsVisited = 0;
|
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(
|
m_errorReporter.docstringParsingError(
|
||||||
6546_error,
|
6546_error,
|
||||||
_node.documentation()->location(),
|
_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++;
|
returnTagsVisited++;
|
||||||
if (auto const* varDecl = dynamic_cast<VariableDeclaration const*>(&_node))
|
if (auto const* varDecl = dynamic_cast<VariableDeclaration const*>(&_node))
|
||||||
@ -171,19 +175,19 @@ void DocStringTagParser::parseDocStrings(
|
|||||||
m_errorReporter.docstringParsingError(
|
m_errorReporter.docstringParsingError(
|
||||||
5256_error,
|
5256_error,
|
||||||
_node.documentation()->location(),
|
_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))
|
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"));
|
string firstWord = content.substr(0, content.find_first_of(" \t"));
|
||||||
|
|
||||||
if (returnTagsVisited > function->returnParameters().size())
|
if (returnTagsVisited > function->returnParameters().size())
|
||||||
m_errorReporter.docstringParsingError(
|
m_errorReporter.docstringParsingError(
|
||||||
2604_error,
|
2604_error,
|
||||||
_node.documentation()->location(),
|
_node.documentation()->location(),
|
||||||
"Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" +
|
"Documentation tag \"@" + tagName + " " + tagValue.content + "\"" +
|
||||||
" exceeds the number of return parameters."
|
" exceeds the number of return parameters."
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
@ -193,7 +197,7 @@ void DocStringTagParser::parseDocStrings(
|
|||||||
m_errorReporter.docstringParsingError(
|
m_errorReporter.docstringParsingError(
|
||||||
5856_error,
|
5856_error,
|
||||||
_node.documentation()->location(),
|
_node.documentation()->location(),
|
||||||
"Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" +
|
"Documentation tag \"@" + tagName + " " + tagValue.content + "\"" +
|
||||||
" does not contain the name of its return parameter."
|
" does not contain the name of its return parameter."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,9 @@
|
|||||||
#include <libsolidity/interface/Natspec.h>
|
#include <libsolidity/interface/Natspec.h>
|
||||||
|
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <boost/range/irange.hpp>
|
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
@ -97,7 +99,7 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
|
|||||||
|
|
||||||
Json::Value Natspec::devDocumentation(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);
|
Json::Value methods(Json::objectValue);
|
||||||
|
|
||||||
doc["version"] = Json::Value(c_natspecVersion);
|
doc["version"] = Json::Value(c_natspecVersion);
|
||||||
@ -205,9 +207,21 @@ string Natspec::extractDoc(multimap<string, DocTag> const& _tags, string const&
|
|||||||
return value;
|
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 Natspec::devDocumentation(std::multimap<std::string, DocTag> const& _tags)
|
||||||
{
|
{
|
||||||
Json::Value json(Json::objectValue);
|
Json::Value json = extractCustomDoc(_tags);
|
||||||
auto dev = extractDoc(_tags, "dev");
|
auto dev = extractDoc(_tags, "dev");
|
||||||
if (!dev.empty())
|
if (!dev.empty())
|
||||||
json["details"] = Json::Value(dev);
|
json["details"] = Json::Value(dev);
|
||||||
|
@ -57,6 +57,9 @@ private:
|
|||||||
/// @returns concatenation of all content under the given tag name.
|
/// @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);
|
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.
|
/// Helper-function that will create a json object with dev specific annotations, if present.
|
||||||
/// @param _tags docTags that are used.
|
/// @param _tags docTags that are used.
|
||||||
/// @return A JSON representation
|
/// @return A JSON representation
|
||||||
|
@ -2247,6 +2247,68 @@ BOOST_AUTO_TEST_CASE(dev_return_name_no_description)
|
|||||||
checkNatspec(sourceCode, "B", natspec2, false);
|
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()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
Loading…
Reference in New Issue
Block a user