Natspec title and author tag.

- Adding the title and author natspec documentation tags for contracts
- Also using the author tag for functions now
- Tests
This commit is contained in:
Lefteris Karapetsas 2014-12-10 13:13:12 +01:00
parent 57e6827cb5
commit fbc35003ce
4 changed files with 114 additions and 30 deletions

9
AST.h
View File

@ -146,13 +146,15 @@ class ContractDefinition: public Declaration
public:
ContractDefinition(Location const& _location,
ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation,
std::vector<ASTPointer<StructDefinition>> const& _definedStructs,
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions):
Declaration(_location, _name),
m_definedStructs(_definedStructs),
m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions)
m_definedFunctions(_definedFunctions),
m_documentation(_documentation)
{}
virtual void accept(ASTVisitor& _visitor) override;
@ -161,6 +163,10 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; }
std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; }
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
/// Returns the functions that make up the calling interface in the intended order.
std::vector<FunctionDefinition const*> getInterfaceFunctions() const;
@ -168,6 +174,7 @@ private:
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
ASTPointer<ASTString> m_documentation;
};
class StructDefinition: public Declaration

View File

@ -75,7 +75,7 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi
if (strPtr)
{
resetUser();
parseDocString(*strPtr);
parseDocString(*strPtr, CommentOwner::FUNCTION);
if (!m_notice.empty())
{// since @notice is the only user tag if missing function should not appear
user["notice"] = Json::Value(m_notice);
@ -95,6 +95,20 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
Json::Value doc;
Json::Value methods(Json::objectValue);
auto contractDoc = _contractDef.getDocumentation();
if (contractDoc)
{
m_contractAuthor.clear();
m_title.clear();
parseDocString(*contractDoc, CommentOwner::CONTRACT);
if (!m_contractAuthor.empty())
doc["author"] = m_contractAuthor;
if (!m_title.empty())
doc["title"] = m_title;
}
for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions())
{
Json::Value method;
@ -102,16 +116,21 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
if (strPtr)
{
resetDev();
parseDocString(*strPtr);
parseDocString(*strPtr, CommentOwner::FUNCTION);
if (!m_dev.empty())
method["details"] = Json::Value(m_dev);
if (!m_author.empty())
method["author"] = m_author;
Json::Value params(Json::objectValue);
for (auto const& pair: m_params)
params[pair.first] = pair.second;
if (!m_params.empty())
method["params"] = params;
if (!m_return.empty())
method["return"] = m_return;
@ -133,6 +152,7 @@ void InterfaceHandler::resetUser()
void InterfaceHandler::resetDev()
{
m_dev.clear();
m_author.clear();
m_return.clear();
m_params.clear();
}
@ -193,7 +213,8 @@ std::string::const_iterator InterfaceHandler::appendDocTagParam(std::string::con
std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end,
std::string const& _tag)
std::string const& _tag,
CommentOwner _owner)
{
// LTODO: need to check for @(start of a tag) between here and the end of line
// for all cases
@ -205,37 +226,77 @@ std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_ite
return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE);
else if (_tag == "return")
return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN);
else if (_tag == "author")
{
if (_owner == CommentOwner::CONTRACT)
return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR);
else if(_owner == CommentOwner::FUNCTION)
return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR);
else
// LTODO: for now this else makes no sense but later comments will go to more language constructs
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag is legal only for contracts"));
}
else if (_tag == "title")
{
if (_owner == CommentOwner::CONTRACT)
return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE);
else
// LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag is legal only for contracts"));
}
else if (_tag == "param")
return parseDocTagParam(_pos, _end);
else
{
// LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("Unknown tag " + _tag + " encountered"));
}
}
else
return appendDocTag(_pos, _end);
return appendDocTag(_pos, _end, _owner);
}
std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end)
std::string::const_iterator _end,
CommentOwner _owner)
{
switch (m_lastTag)
{
case DocTagType::DEV:
m_dev += " ";
return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV);
case DocTagType::NOTICE:
m_notice += " ";
return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE);
case DocTagType::RETURN:
m_return += " ";
return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN);
case DocTagType::PARAM:
return appendDocTagParam(_pos, _end);
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Illegal documentation tag type"));
break;
case DocTagType::DEV:
m_dev += " ";
return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV);
case DocTagType::NOTICE:
m_notice += " ";
return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE);
case DocTagType::RETURN:
m_return += " ";
return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN);
case DocTagType::AUTHOR:
if (_owner == CommentOwner::CONTRACT)
{
m_contractAuthor += " ";
return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR);
}
else if(_owner == CommentOwner::FUNCTION)
{
m_author += " ";
return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR);
}
else
// LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag in illegal comment"));
case DocTagType::TITLE:
if (_owner == CommentOwner::CONTRACT)
{
m_title += " ";
return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE);
}
else
// LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag in illegal comment"));
case DocTagType::PARAM:
return appendDocTagParam(_pos, _end);
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Illegal documentation tag type"));
break;
}
}
@ -247,7 +308,7 @@ static inline std::string::const_iterator getFirstSpaceOrNl(std::string::const_i
return (spacePos < nlPos) ? spacePos : nlPos;
}
void InterfaceHandler::parseDocString(std::string const& _string)
void InterfaceHandler::parseDocString(std::string const& _string, CommentOwner _owner)
{
auto currPos = _string.begin();
auto end = _string.end();
@ -265,10 +326,10 @@ void InterfaceHandler::parseDocString(std::string const& _string)
BOOST_THROW_EXCEPTION(DocstringParsingError() <<
errinfo_comment("End of tag " + std::string(tagPos, tagNameEndPos) + "not found"));
currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos));
currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos), _owner);
}
else if (m_lastTag != DocTagType::NONE) // continuation of the previous tag
currPos = appendDocTag(currPos + 1, end);
currPos = appendDocTag(currPos + 1, end, _owner);
else if (currPos != end) // skip the line if a newline was found
currPos = nlPos + 1;
}

View File

@ -45,7 +45,15 @@ enum class DocTagType: uint8_t
DEV,
NOTICE,
PARAM,
RETURN
RETURN,
AUTHOR,
TITLE
};
enum class CommentOwner
{
CONTRACT,
FUNCTION
};
class InterfaceHandler
@ -89,12 +97,14 @@ private:
std::string::const_iterator _end);
std::string::const_iterator appendDocTagParam(std::string::const_iterator _pos,
std::string::const_iterator _end);
void parseDocString(std::string const& _string);
void parseDocString(std::string const& _string, CommentOwner _owner);
std::string::const_iterator appendDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end);
std::string::const_iterator _end,
CommentOwner _owner);
std::string::const_iterator parseDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end,
std::string const& _tag);
std::string const& _tag,
CommentOwner _owner);
Json::StyledWriter m_writer;
@ -103,6 +113,9 @@ private:
std::string m_notice;
std::string m_dev;
std::string m_return;
std::string m_contractAuthor;
std::string m_author;
std::string m_title;
std::vector<std::pair<std::string, std::string>> m_params;
};

View File

@ -112,6 +112,9 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
ASTPointer<ContractDefinition> Parser::parseContractDefinition()
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring;
if (m_scanner->getCurrentCommentLiteral() != "")
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::CONTRACT);
ASTPointer<ASTString> name = expectIdentifierToken();
expectToken(Token::LBRACE);
@ -146,7 +149,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
}
nodeFactory.markEndPosition();
expectToken(Token::RBRACE);
return nodeFactory.createNode<ContractDefinition>(name, structs, stateVariables, functions);
return nodeFactory.createNode<ContractDefinition>(name, docstring, structs, stateVariables, functions);
}
ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)